Первая версия нового маппинга юнита и использование StoryLine. Также актуализировал код под кор библиотеку.

This commit is contained in:
Struchkov Mark 2022-06-26 22:49:15 +03:00
parent 0c3b098e55
commit 7e4264a9e6
33 changed files with 936 additions and 78 deletions

View File

@ -6,7 +6,7 @@
<parent>
<groupId>dev.struchkov.godfather</groupId>
<artifactId>godfather-bot</artifactId>
<version>0.0.6</version>
<version>0.0.7</version>
</parent>
<artifactId>bot-context</artifactId>

View File

@ -0,0 +1,77 @@
package dev.struchkov.godfather.context.domain;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
public class UnitDefinition {
private final Set<String> nextUnitNames = new HashSet<>();
private final Set<String> dependentUnits = new HashSet<>();
private String name;
private Object objectConfig;
private Method method;
private boolean lazy;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getObjectConfig() {
return objectConfig;
}
public void setObjectConfig(Object objectConfig) {
this.objectConfig = objectConfig;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Set<String> getNextUnitNames() {
return new HashSet<>(nextUnitNames);
}
public void setNextUnitNames(Set<String> nextUnitNames) {
this.nextUnitNames.addAll(nextUnitNames);
}
public void setNextUnitName(String nextUnitName) {
this.nextUnitNames.add(nextUnitName);
}
public void removeNextUnit(String nextUnitName) {
this.nextUnitNames.remove(nextUnitName);
}
public void removeDependentUnit(String dependentUnitName) {
this.dependentUnits.remove(dependentUnitName);
}
public Set<String> getDependentUnits() {
return new HashSet<>(dependentUnits);
}
public void setDependentUnits(Set<String> dependentUnitNames) {
this.dependentUnits.addAll(dependentUnitNames);
}
public boolean isLazy() {
return lazy;
}
public void setLazy(boolean lazy) {
this.lazy = lazy;
}
}

View File

@ -0,0 +1,36 @@
package dev.struchkov.godfather.context.domain;
import java.util.Objects;
public class UnitPointer {
private Long personId;
private String unitName;
public UnitPointer(Long personId, String unitName) {
this.personId = personId;
this.unitName = unitName;
}
public Long getPersonId() {
return personId;
}
public String getUnitName() {
return unitName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UnitPointer that = (UnitPointer) o;
return Objects.equals(personId, that.personId) && Objects.equals(unitName, that.unitName);
}
@Override
public int hashCode() {
return Objects.hash(personId, unitName);
}
}

View File

@ -0,0 +1,20 @@
package dev.struchkov.godfather.context.domain.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target({METHOD, PARAMETER})
public @interface Unit {
String value() default "";
boolean lazy() default false;
boolean mainUnit() default false;
}

View File

@ -33,7 +33,7 @@ public class SimpleButton implements KeyBoardButton {
}
public static SimpleButton simpleButton(@NotNull String label) {
return new SimpleButton(label, label);
return new SimpleButton(label, null);
}
public String getLabel() {

View File

@ -33,7 +33,7 @@ public class SimpleKeyBoard implements KeyBoard {
return new SimpleKeyBoard(List.of(line));
}
public static Builder build() {
public static Builder builder() {
return new Builder();
}

View File

@ -16,8 +16,12 @@ public class UnitConfigException extends AppBotException{
super(message);
}
public UnitConfigException(String message, Object... objects) {
super(MessageFormat.format(message, objects));
}
public static Supplier<NotFoundException> unitConfigException(String message, Object... objects) {
return () -> new NotFoundException(MessageFormat.format(message, objects));
return () -> new NotFoundException(message, objects);
}
}

View File

@ -0,0 +1,16 @@
package dev.struchkov.godfather.context.repository;
import java.util.Optional;
import java.util.Set;
public interface PersonSettingRepository {
Set<Long> findAllByAllowedProcessing(Set<Long> personIds);
void disableMessageProcessing(Long personId);
void enableMessageProcessing(Long personId);
Optional<Boolean> findStateByPersonId(Long personId);
}

View File

@ -0,0 +1,19 @@
package dev.struchkov.godfather.context.repository;
import dev.struchkov.godfather.context.domain.UnitPointer;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
/**
* Контракт для сохранения позиции пользователя в сценарии.
*/
public interface UnitPointerRepository {
UnitPointer save(@NotNull UnitPointer unitPointer);
Optional<String> findUnitNameByPersonId(@NotNull Long personId);
void removeByPersonId(@NotNull Long personId);
}

View File

@ -0,0 +1,37 @@
package dev.struchkov.godfather.context.repository.impl.local;
import dev.struchkov.godfather.context.repository.PersonSettingRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public class PersonSettingLocalRepository implements PersonSettingRepository {
private final Map<Long, Boolean> map = new HashMap<>();
@Override
public Set<Long> findAllByAllowedProcessing(Set<Long> personIds) {
return personIds.stream()
.filter(map::get)
.collect(Collectors.toSet());
}
@Override
public void disableMessageProcessing(Long personId) {
map.put(personId, false);
}
@Override
public void enableMessageProcessing(Long personId) {
map.put(personId, true);
}
@Override
public Optional<Boolean> findStateByPersonId(Long personId) {
return Optional.ofNullable(map.get(personId));
}
}

View File

@ -0,0 +1,31 @@
package dev.struchkov.godfather.context.repository.impl.local;
import dev.struchkov.godfather.context.domain.UnitPointer;
import dev.struchkov.godfather.context.repository.UnitPointerRepository;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class UnitPointLocalRepository implements UnitPointerRepository {
public static final Map<Long, String> map = new HashMap<>();
@Override
public UnitPointer save(@NotNull UnitPointer unitPointer) {
map.put(unitPointer.getPersonId(), unitPointer.getUnitName());
return unitPointer;
}
@Override
public Optional<String> findUnitNameByPersonId(@NotNull Long personId) {
return Optional.ofNullable(map.get(personId));
}
@Override
public void removeByPersonId(@NotNull Long personId) {
map.remove(personId);
}
}

View File

@ -0,0 +1,9 @@
package dev.struchkov.godfather.context.service;
import dev.struchkov.godfather.context.domain.content.Message;
public interface EventProvider<M extends Message> {
void sendEvent(M message);
}

View File

@ -0,0 +1,18 @@
package dev.struchkov.godfather.context.service;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
import java.util.Set;
public interface PersonSettingService {
Set<Long> getAllPersonIdDisableMessages(@NotNull Set<Long> personIds);
Optional<Boolean> getStateProcessingByPersonId(@NotNull Long personId);
void disableMessageProcessing(@NotNull Long personId);
void enableMessageProcessing(@NotNull Long personId);
}

View File

@ -0,0 +1,21 @@
package dev.struchkov.godfather.context.service;
import dev.struchkov.godfather.context.domain.UnitPointer;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
/**
* Сервис для взаимодействия с сущностью {@link UnitPointer}.
*
* @author upagge [07/07/2019]
*/
public interface UnitPointerService {
UnitPointer save(@NotNull UnitPointer unitPointer);
Optional<String> getUnitNameByPersonId(@NotNull Long personId);
void removeByPersonId(@NotNull Long personId);
}

View File

@ -18,8 +18,6 @@ public interface Sending {
*/
void send(@NotNull Long personId, @NotNull BoxAnswer boxAnswer);
void send(@NotNull Long contentId, @NotNull Long personId, @NotNull BoxAnswer boxAnswer);
/**
* Возвращает тип объекта отправляющего ответ пользователя. В зависимости от типа ответ будет отправлен с помощью
* разных методов.

View File

@ -12,12 +12,12 @@ import java.util.List;
*
* @author upagge [08/07/2019]
*/
public class KeyBoards {
public class SimpleKeyBoards {
public static final SimpleButton YES_BUTTON = SimpleButton.simpleButton("Да", "{\"button\": \"yes\"}");
public static final SimpleButton NO_BUTTON = SimpleButton.simpleButton("Нет", "{\"button\": \"no\"}");
private KeyBoards() {
private SimpleKeyBoards() {
throw new IllegalStateException();
}
@ -27,7 +27,7 @@ public class KeyBoards {
* @return {@link SimpleKeyBoard}
*/
public static SimpleKeyBoard keyBoardYesNo() {
return SimpleKeyBoard.build().line(
return SimpleKeyBoard.builder().line(
SimpleKeyBoardLine.builder().button(YES_BUTTON).button(NO_BUTTON).build()
).build();
}
@ -39,7 +39,7 @@ public class KeyBoards {
* @return {@link SimpleKeyBoard}
*/
public static SimpleKeyBoard verticalMenuString(List<String> labelButtons) {
final SimpleKeyBoard.Builder keyBoard = SimpleKeyBoard.build();
final SimpleKeyBoard.Builder keyBoard = SimpleKeyBoard.builder();
for (String labelButton : labelButtons) {
final SimpleButton simpleButton = SimpleButton.simpleButton(labelButton, "{\"button\": \"" + labelButton + "\"}");
keyBoard.line(SimpleKeyBoardLine.builder().button(simpleButton).build());
@ -74,7 +74,7 @@ public class KeyBoards {
* @return {@link SimpleKeyBoard}
*/
public static SimpleKeyBoard verticalDuoMenuString(List<String> labelButton) {
final SimpleKeyBoard.Builder keyBoard = SimpleKeyBoard.build();
final SimpleKeyBoard.Builder keyBoard = SimpleKeyBoard.builder();
boolean flag = true;
SimpleKeyBoardLine.Builder keyBoardLine = SimpleKeyBoardLine.builder();
for (int i = 0; i <= labelButton.size() - 1; i++) {
@ -102,7 +102,7 @@ public class KeyBoards {
* @return {@link SimpleKeyBoard}
*/
public static SimpleKeyBoard verticalMenuButton(List<SimpleButton> simpleButtons) {
final SimpleKeyBoard.Builder keyBoard = SimpleKeyBoard.build();
final SimpleKeyBoard.Builder keyBoard = SimpleKeyBoard.builder();
for (SimpleButton simpleButton : simpleButtons) {
keyBoard.line(SimpleKeyBoardLine.builder().button(simpleButton).build());
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>dev.struchkov.godfather</groupId>
<artifactId>godfather-bot</artifactId>
<version>0.0.6</version>
<version>0.0.7</version>
</parent>
<artifactId>bot-core</artifactId>

View File

@ -1,16 +1,17 @@
package dev.struchkov.godfather.core;
import dev.struchkov.autoresponder.AutoResponder;
import dev.struchkov.autoresponder.entity.UnitPointer;
import dev.struchkov.autoresponder.repository.UnitPointerRepository;
import dev.struchkov.autoresponder.service.UnitPointerServiceImpl;
import dev.struchkov.autoresponder.Responder;
import dev.struchkov.autoresponder.entity.Unit;
import dev.struchkov.godfather.context.domain.UnitPointer;
import dev.struchkov.godfather.context.domain.content.Message;
import dev.struchkov.godfather.context.exception.ConfigAppException;
import dev.struchkov.godfather.context.service.MessageService;
import dev.struchkov.godfather.context.service.Modifiable;
import dev.struchkov.godfather.context.service.PersonSettingService;
import dev.struchkov.godfather.context.service.UnitPointerService;
import dev.struchkov.godfather.context.service.sender.Sending;
import dev.struchkov.godfather.core.domain.unit.MainUnit;
import dev.struchkov.godfather.core.domain.unit.UnitActiveType;
import dev.struchkov.godfather.core.service.Accessibility;
import dev.struchkov.godfather.core.service.action.ActionUnit;
import dev.struchkov.godfather.core.service.action.AnswerCheckAction;
import dev.struchkov.godfather.core.service.action.AnswerProcessingAction;
@ -27,26 +28,26 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class GeneralAutoResponder<T extends Message> extends TimerTask {
public class GeneralAutoResponder<T extends Message> {
private final PersonSettingService personSettingService;
private final UnitPointerService unitPointerService;
private final StoryLine storyLine;
protected final AutoResponder<MainUnit> autoResponder;
private final MessageService<T> messageService;
protected Map<String, ActionUnit<? extends MainUnit, ? extends Message>> actionUnitMap = new HashMap<>();
protected List<Modifiable<T>> modifiables;
private ExecutorService executorService = Executors.newFixedThreadPool(10);
protected List<Modifiable<T>> modifiable;
protected GeneralAutoResponder(Set<MainUnit> menuUnit,
Sending sending,
MessageService<T> messageService,
UnitPointerRepository<MainUnit> unitPointerRepository
protected GeneralAutoResponder(
Sending sending,
PersonSettingService personSettingService,
UnitPointerService unitPointerService,
List<Object> unitConfigurations
) {
this.messageService = messageService;
autoResponder = new AutoResponder<>(new UnitPointerServiceImpl<>(unitPointerRepository), menuUnit);
this.personSettingService = personSettingService;
this.unitPointerService = unitPointerService;
this.storyLine = new StorylineMaker(unitConfigurations).createStoryLine();
init(sending);
}
@ -57,8 +58,8 @@ public class GeneralAutoResponder<T extends Message> extends TimerTask {
actionUnitMap.put(TypeUnit.VALIDITY, new AnswerValidityAction());
}
public void initModifiables(List<Modifiable<T>> modifiables) {
this.modifiables = modifiables;
public void initModifiable(List<Modifiable<T>> modifiable) {
this.modifiable = modifiable;
}
public void initActionUnit(String typeUnit, ActionUnit<? super MainUnit, T> actionUnit) {
@ -69,10 +70,6 @@ public class GeneralAutoResponder<T extends Message> extends TimerTask {
}
}
public <U extends MainUnit> void initDefaultUnit(U defaultUnit) {
autoResponder.setDefaultUnit(defaultUnit);
}
public void initSaveAction(AnswerSaveAction<?> answerSaveAction) {
actionUnitMap.put(TypeUnit.SAVE, answerSaveAction);
}
@ -81,33 +78,56 @@ public class GeneralAutoResponder<T extends Message> extends TimerTask {
actionUnitMap.put(TypeUnit.TIMER, new AnswerTimerAction(timerService, this));
}
public void setDefaultUnit(MainUnit mainUnit) {
autoResponder.setDefaultUnit(mainUnit);
}
public void checkNewMessage() {
List<T> eventByTime = messageService.getNewMessage();
if (eventByTime != null && !eventByTime.isEmpty()) {
executorService.execute(
() -> eventByTime.parallelStream().forEach(processing())
);
public void processingNewMessage(T newMessage) {
if (newMessage != null) {
final boolean state = personSettingService.getStateProcessingByPersonId(newMessage.getPersonId()).orElse(true);
if (state) {
processing(newMessage);
}
}
}
private Consumer<T> processing() {
return event -> {
if (modifiables != null) {
modifiables.forEach(modifiable -> modifiable.change(event));
}
autoResponder.answer(event.getPersonId(), event.getText()).ifPresent(unitAnswer -> answer(event, unitAnswer));
};
public void processingNewMessages(List<T> newMessages) {
if (newMessages != null && !newMessages.isEmpty()) {
final Set<Long> personIds = newMessages.stream()
.map(Message::getPersonId)
.collect(Collectors.toSet());
final Set<Long> disableIds = personSettingService.getAllPersonIdDisableMessages(personIds);
final List<T> allowedMessages = newMessages.stream()
.filter(message -> !disableIds.contains(message.getPersonId()))
.toList();
allowedMessages.parallelStream().forEach(this::processing);
}
}
public void answer(T event, MainUnit unitAnswer) {
unitAnswer = getAction(event, unitAnswer);
unitAnswer = activeUnitAfter(unitAnswer, event);
if (!(autoResponder.getDefaultUnit() != null && autoResponder.getDefaultUnit().equals(unitAnswer))) {
autoResponder.getUnitPointerService().save(new UnitPointer<>(event.getPersonId(), unitAnswer));
private void processing(T message) {
if (modifiable != null) {
modifiable.forEach(m -> m.change(message));
}
final Set<MainUnit> units = unitPointerService.getUnitNameByPersonId(message.getPersonId())
.flatMap(storyLine::getUnit)
.map(Unit::getNextUnits)
.filter(mainUnits -> !mainUnits.isEmpty())
.orElse(storyLine.getStartingUnits());
final Optional<MainUnit> optAnswer = Responder.nextUnit(message.getText(), units);
if (optAnswer.isPresent()) {
final MainUnit answer = optAnswer.get();
if (checkPermission(answer.getAccessibility(), message)) {
answer(message, answer);
}
}
}
private boolean checkPermission(Optional<Accessibility> accessibility, T message) {
return accessibility.isEmpty() || accessibility.get().check(message);
}
public void answer(T message, MainUnit unitAnswer) {
unitAnswer = getAction(message, unitAnswer);
unitAnswer = activeUnitAfter(unitAnswer, message);
final Optional<MainUnit> optDefaultUnit = storyLine.getDefaultUnit();
if (optDefaultUnit.isEmpty() || !optDefaultUnit.get().equals(unitAnswer)) {
unitPointerService.save(new UnitPointer(message.getPersonId(), unitAnswer.getName()));
}
}
@ -134,9 +154,9 @@ public class GeneralAutoResponder<T extends Message> extends TimerTask {
}
}
@Override
public void run() {
checkNewMessage();
//TODO [22.06.2022]: Временное решение для ленивой инициализации
public void link(String firstName, String secondName) {
storyLine.link(firstName, secondName);
}
}

View File

@ -0,0 +1,55 @@
package dev.struchkov.godfather.core;
import dev.struchkov.godfather.core.domain.unit.MainUnit;
import dev.struchkov.haiti.utils.Inspector;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class StoryLine {
private MainUnit defaultUnit;
private final Set<MainUnit> startingUnits = new HashSet<>();
private final Map<String, MainUnit> units = new HashMap<>();
public StoryLine(Set<MainUnit> startingUnits, Map<String, MainUnit> units) {
this.startingUnits.addAll(startingUnits);
this.units.putAll(units);
}
public void setDefaultUnit(MainUnit defaultUnit) {
this.defaultUnit = defaultUnit;
}
public Optional<MainUnit> getDefaultUnit() {
return Optional.ofNullable(defaultUnit);
}
/**
* Получить юнит по названию.
*
* @param unitName Название юнита.
*/
public Optional<MainUnit> getUnit(String unitName) {
Inspector.isNotNull(unitName);
return Optional.ofNullable(units.get(unitName));
}
public Set<MainUnit> getStartingUnits() {
return startingUnits;
}
//TODO [22.06.2022]: Временное решение ленивой связки юнитов, пока не будет реализован нормальный механизм.
public void link(@NotNull String firstName, @NotNull String secondName) {
Inspector.isNotNull(firstName, secondName);
final MainUnit firstUnit = units.get(firstName);
final MainUnit secondUnit = units.get(secondName);
Inspector.isNotNull(firstUnit, secondUnit);
firstUnit.getNextUnits().add(secondUnit);
}
}

View File

@ -0,0 +1,185 @@
package dev.struchkov.godfather.core;
import dev.struchkov.godfather.context.domain.UnitDefinition;
import dev.struchkov.godfather.context.domain.annotation.Unit;
import dev.struchkov.godfather.context.exception.UnitConfigException;
import dev.struchkov.godfather.core.domain.unit.LazyUnit;
import dev.struchkov.godfather.core.domain.unit.MainUnit;
import dev.struchkov.haiti.utils.Inspector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static dev.struchkov.godfather.context.exception.UnitConfigException.unitConfigException;
public class StorylineMaker {
private static final Logger log = LoggerFactory.getLogger(StorylineMaker.class);
private final List<Object> configurations = new ArrayList<>();
private final Map<String, UnitDefinition> unitDefinitions = new HashMap<>();
private final Map<String, MainUnit> unitMap = new HashMap<>();
private final Set<String> lazyUnits = new HashSet<>();
private final Set<String> mainUnits = new HashSet<>();
public StorylineMaker(List<Object> unitConfigurations) {
this.configurations.addAll(unitConfigurations);
}
public Map<String, UnitDefinition> getUnitDefinitions() {
return unitDefinitions;
}
public Map<String, MainUnit> getUnitMap() {
return unitMap;
}
public StoryLine createStoryLine() {
generateUnitDefinitions();
try {
createUnitMap();
createLazy();
final Set<MainUnit> mainUnit = getMainUnit();
return new StoryLine(mainUnit, unitMap);
} catch (IllegalAccessException | InvocationTargetException e) {
log.error(e.getMessage(), e);
}
throw new UnitConfigException("Ошибка построения StoryLine");
}
private Set<MainUnit> getMainUnit() {
Inspector.isNotEmpty(mainUnits, unitConfigException("Не задан ни один mainUnit. Установите хотя бы для одного Unit флаг mainUnit"));
return mainUnits.stream()
.map(unitMap::get)
.collect(Collectors.toSet());
}
private void createLazy() throws IllegalAccessException, InvocationTargetException {
final List<UnitDefinition> lazyDefinitions = unitDefinitions.values().stream()
.filter(UnitDefinition::isLazy)
.toList();
for (UnitDefinition lazyDefinition : lazyDefinitions) {
final MainUnit lazyUnit = createUnit(lazyDefinition);
unitMap.put(lazyDefinition.getName(), lazyUnit);
for (String dependentUnit : lazyDefinition.getDependentUnits()) {
final MainUnit mainUnit = unitMap.get(dependentUnit);
final Set<MainUnit> nextUnits = mainUnit.getNextUnits();
if (nextUnits != null) {
nextUnits.add(lazyUnit);
}
}
}
}
private void createUnitMap() throws IllegalAccessException, InvocationTargetException {
for (UnitDefinition unitDefinition : unitDefinitions.values()) {
if (!unitMap.containsKey(unitDefinition.getName())) {
final Set<String> nextUnitNames = unitDefinition.getNextUnitNames();
if (nextUnitNames.isEmpty() || unitMap.keySet().containsAll(nextUnitNames)) {
createUnit(unitDefinition);
} else if (unitDefinition.isLazy()) {
createLazyUnit(unitDefinition);
}
}
}
}
private void createLazyUnit(UnitDefinition unitDefinition) {
final String unitName = unitDefinition.getName();
unitMap.put(unitName, LazyUnit.create(unitName, unitDefinition));
}
private MainUnit createUnit(UnitDefinition unitDefinition) throws IllegalAccessException, InvocationTargetException {
final Object objectConfig = unitDefinition.getObjectConfig();
final String currentUnitName = unitDefinition.getName();
final Method method = unitDefinition.getMethod();
final Object[] nextUnits = Arrays.stream(method.getParameters())
.filter(parameter -> parameter.isAnnotationPresent(Unit.class))
.map(parameter -> parameter.getAnnotation(Unit.class))
.map(Unit::value)
.map(unitMap::get)
.toArray();
MainUnit newUnit = (MainUnit) method.invoke(objectConfig, nextUnits);
newUnit.setName(currentUnitName);
unitMap.put(currentUnitName, newUnit);
final Set<String> dependentUnitsName = unitDefinition.getDependentUnits();
dependentUnitsName.removeAll(lazyUnits);
for (String dependentUnitName : dependentUnitsName) {
final Set<String> dependentNextUnitNames = unitDefinitions.get(dependentUnitName).getNextUnitNames();
if (unitMap.keySet().containsAll(dependentNextUnitNames)) {
createUnit(unitDefinitions.get(dependentUnitName));
}
}
return newUnit;
}
private void generateUnitDefinitions() {
final Map<String, Set<String>> dependentUnits = new HashMap<>();
for (Object config : configurations) {
final Class<?> classUnitConfig = config.getClass();
for (Method method : classUnitConfig.getDeclaredMethods()) {
if (method.isAnnotationPresent(Unit.class)) {
final Unit unitConfig = method.getAnnotation(Unit.class);
final String unitName = unitConfig.value();
final UnitDefinition unitDefinition = new UnitDefinition();
unitDefinition.setName(unitName);
unitDefinition.setMethod(method);
unitDefinition.setObjectConfig(config);
unitDefinition.setLazy(unitConfig.lazy());
if (unitConfig.lazy()) {
lazyUnits.add(unitName);
}
if (unitConfig.mainUnit()) {
mainUnits.add(unitName);
}
final Parameter[] nextUnits = method.getParameters();
if (nextUnits.length > 0) {
for (Parameter nextUnit : nextUnits) {
if (nextUnit.isAnnotationPresent(Unit.class)) {
final Unit nextUnitConfig = nextUnit.getAnnotation(Unit.class);
final String nextUnitName = nextUnitConfig.value();
unitDefinition.setNextUnitName(nextUnitName);
dependentUnits.computeIfAbsent(nextUnitName, k -> new HashSet<>());
dependentUnits.get(nextUnitName).add(unitName);
}
}
}
unitDefinitions.put(unitDefinition.getName(), unitDefinition);
}
}
}
for (Map.Entry<String, Set<String>> entry : dependentUnits.entrySet()) {
final UnitDefinition unitDefinition = unitDefinitions.get(entry.getKey());
if (unitDefinition != null) {
unitDefinition.setDependentUnits(entry.getValue());
} else {
throw new UnitConfigException("Ошибка связи юнитов. Проблема с описанием {0} юнита. Возможно вы не указали класс конфигурации для этого юнита.", entry.getKey());
}
}
}
}

View File

@ -2,6 +2,7 @@ package dev.struchkov.godfather.core.domain.unit;
import dev.struchkov.godfather.context.domain.content.Message;
import dev.struchkov.godfather.context.service.usercode.CheckData;
import dev.struchkov.godfather.core.service.Accessibility;
import dev.struchkov.godfather.core.utils.TypeUnit;
import java.util.HashSet;
@ -35,7 +36,18 @@ public class AnswerCheck extends MainUnit {
private final CheckData<Message> check;
private AnswerCheck(Builder builder) {
super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, null, builder.activeType, TypeUnit.CHECK);
super(
builder.name,
builder.keyWords,
builder.phrase,
builder.pattern,
builder.matchThreshold,
builder.priority,
new HashSet<>(),
builder.activeType,
builder.accessibility,
TypeUnit.CHECK
);
unitTrue = builder.unitTrue;
unitFalse = builder.unitFalse;
check = builder.check;
@ -58,6 +70,7 @@ public class AnswerCheck extends MainUnit {
}
public static final class Builder {
private String name;
private Set<String> keyWords = new HashSet<>();
private String phrase;
private Pattern pattern;
@ -67,10 +80,16 @@ public class AnswerCheck extends MainUnit {
private MainUnit unitFalse;
private CheckData<Message> check;
private UnitActiveType activeType;
private Accessibility accessibility;
private Builder() {
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder keyWords(Set<String> val) {
keyWords = val;
return this;
@ -116,6 +135,11 @@ public class AnswerCheck extends MainUnit {
return this;
}
public Builder accessibility(Accessibility val) {
accessibility = val;
return this;
}
public Builder activeType(UnitActiveType val) {
activeType = val;
return this;

View File

@ -3,6 +3,7 @@ package dev.struchkov.godfather.core.domain.unit;
import dev.struchkov.godfather.context.domain.content.Message;
import dev.struchkov.godfather.context.service.sender.Sending;
import dev.struchkov.godfather.context.service.usercode.ProcessingData;
import dev.struchkov.godfather.core.service.Accessibility;
import dev.struchkov.godfather.core.utils.TypeUnit;
import java.util.HashSet;
@ -27,7 +28,18 @@ public class AnswerProcessing<M extends Message> extends MainUnit {
private final Sending sending;
private AnswerProcessing(Builder<M> builder) {
super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, builder.nextUnits, builder.activeType, TypeUnit.PROCESSING);
super(
builder.name,
builder.keyWords,
builder.phrase,
builder.pattern,
builder.matchThreshold,
builder.priority,
builder.nextUnits,
builder.activeType,
builder.accessibility,
TypeUnit.PROCESSING
);
processingData = builder.processingData;
sending = builder.sending;
}
@ -45,6 +57,7 @@ public class AnswerProcessing<M extends Message> extends MainUnit {
}
public static final class Builder<M extends Message> {
private String name;
private Set<String> keyWords = new HashSet<>();
private String phrase;
private Pattern pattern;
@ -54,10 +67,16 @@ public class AnswerProcessing<M extends Message> extends MainUnit {
private ProcessingData<M> processingData;
private Sending sending;
private UnitActiveType activeType;
private Accessibility accessibility;
private Builder() {
}
public Builder<M> name(String name) {
this.name = name;
return this;
}
public Builder<M> processingData(ProcessingData<M> val) {
processingData = val;
return this;
@ -108,6 +127,11 @@ public class AnswerProcessing<M extends Message> extends MainUnit {
return this;
}
public Builder accessibility(Accessibility val) {
accessibility = val;
return this;
}
public Builder<M> activeType(UnitActiveType val) {
activeType = val;
return this;

View File

@ -1,6 +1,7 @@
package dev.struchkov.godfather.core.domain.unit;
import dev.struchkov.godfather.context.domain.content.Message;
import dev.struchkov.godfather.core.service.Accessibility;
import dev.struchkov.godfather.core.service.save.CheckSave;
import dev.struchkov.godfather.core.service.save.Preservable;
import dev.struchkov.godfather.core.service.save.data.PreservableData;
@ -49,7 +50,18 @@ public class AnswerSave<D> extends MainUnit {
private final CheckSave<? super Message> checkSave;
private AnswerSave(Builder<D> builder) {
super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, builder.nextUnits, (builder.hidden) ? UnitActiveType.AFTER : UnitActiveType.DEFAULT, TypeUnit.SAVE);
super(
builder.name,
builder.keyWords,
builder.phrase,
builder.pattern,
builder.matchThreshold,
builder.priority,
builder.nextUnits,
(builder.hidden) ? UnitActiveType.AFTER : UnitActiveType.DEFAULT,
builder.accessibility,
TypeUnit.SAVE
);
maintenanceNextUnit(nextUnits);
preservable = builder.preservable;
key = builder.key;
@ -94,6 +106,7 @@ public class AnswerSave<D> extends MainUnit {
}
public static final class Builder<D> {
private String name;
private Set<String> keyWords = new HashSet<>();
private String phrase;
private Pattern pattern;
@ -106,10 +119,16 @@ public class AnswerSave<D> extends MainUnit {
private PreservableData<D, ? super Message> preservableData;
private boolean hidden;
private CheckSave<? super Message> checkSave;
private Accessibility accessibility;
private Builder() {
}
public Builder<D> name(String name) {
this.name = name;
return this;
}
public Builder<D> keyWords(Set<String> val) {
keyWords = val;
return this;
@ -180,6 +199,11 @@ public class AnswerSave<D> extends MainUnit {
return this;
}
public Builder accessibility(Accessibility val) {
accessibility = val;
return this;
}
public AnswerSave<D> build() {
isNotNull(preservable, "Не указан репозиторий для сохранения формы пользователя");
isNotNull(preservableData, "Не указаны данные для сохранения");

View File

@ -7,6 +7,7 @@ import dev.struchkov.godfather.context.service.sender.Sending;
import dev.struchkov.godfather.context.service.usercode.Insert;
import dev.struchkov.godfather.context.service.usercode.MessageFunction;
import dev.struchkov.godfather.context.service.usercode.ProcessingData;
import dev.struchkov.godfather.core.service.Accessibility;
import dev.struchkov.godfather.core.utils.TypeUnit;
import java.util.HashSet;
@ -39,7 +40,18 @@ public class AnswerText<M extends Message> extends MainUnit {
private final Sending sending;
private AnswerText(Builder<M> builder) {
super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, builder.nextUnits, builder.activeType, TypeUnit.TEXT);
super(
builder.name,
builder.keyWords,
builder.phrase,
builder.pattern,
builder.matchThreshold,
builder.priority,
builder.nextUnits,
builder.activeType,
builder.accessibility,
TypeUnit.TEXT
);
boxAnswer = builder.boxAnswer;
insert = builder.insert;
sending = builder.sending;
@ -66,6 +78,7 @@ public class AnswerText<M extends Message> extends MainUnit {
}
public static final class Builder<M extends Message> {
private String name;
private ProcessingData<M> boxAnswer;
private Insert insert;
private Sending sending;
@ -76,10 +89,16 @@ public class AnswerText<M extends Message> extends MainUnit {
private Integer priority;
private Set<MainUnit> nextUnits = new HashSet<>();
private UnitActiveType activeType;
private Accessibility accessibility;
private Builder() {
}
public Builder<M> name(String name) {
this.name = name;
return this;
}
public Builder<M> message(ProcessingData<M> message) {
this.boxAnswer = message;
return this;
@ -151,6 +170,11 @@ public class AnswerText<M extends Message> extends MainUnit {
return this;
}
public Builder accessibility(Accessibility val) {
accessibility = val;
return this;
}
public Builder<M> activeType(UnitActiveType val) {
activeType = val;
return this;

View File

@ -2,6 +2,7 @@ package dev.struchkov.godfather.core.domain.unit;
import dev.struchkov.godfather.context.domain.content.Message;
import dev.struchkov.godfather.context.service.usercode.CheckData;
import dev.struchkov.godfather.core.service.Accessibility;
import dev.struchkov.godfather.core.utils.TypeUnit;
import java.util.Set;
@ -38,7 +39,18 @@ public class AnswerTimer<M extends Message> extends MainUnit {
private final CheckData<M> checkLoop;
private AnswerTimer(Builder<M> builder) {
super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, null, builder.activeType, TypeUnit.TIMER);
super(
builder.name,
builder.keyWords,
builder.phrase,
builder.pattern,
builder.matchThreshold,
builder.priority,
null,
builder.activeType,
builder.accessibility,
TypeUnit.TIMER
);
unitAnswer = builder.unitAnswer;
timeDelaySec = builder.timeDelaySec;
timeDeathSec = builder.timeDeathSec;
@ -66,6 +78,7 @@ public class AnswerTimer<M extends Message> extends MainUnit {
}
public static final class Builder<M extends Message> {
private String name;
private MainUnit unitAnswer;
private Integer timeDelaySec;
private Integer timeDeathSec;
@ -76,9 +89,14 @@ public class AnswerTimer<M extends Message> extends MainUnit {
private Integer matchThreshold;
private Integer priority;
private UnitActiveType activeType = UnitActiveType.AFTER;
private Accessibility accessibility;
private Builder() {
}
public Builder<M> name(String name) {
this.name = name;
return this;
}
public Builder<M> unitAnswer(MainUnit val) {
@ -126,6 +144,11 @@ public class AnswerTimer<M extends Message> extends MainUnit {
return this;
}
public Builder accessibility(Accessibility val) {
accessibility = val;
return this;
}
public Builder<M> activeType(UnitActiveType val) {
activeType = val;
return this;

View File

@ -1,5 +1,6 @@
package dev.struchkov.godfather.core.domain.unit;
import dev.struchkov.godfather.core.service.Accessibility;
import dev.struchkov.godfather.core.service.ClarificationQuestion;
import dev.struchkov.godfather.core.service.save.LocalPreservable;
import dev.struchkov.godfather.core.service.save.Preservable;
@ -36,7 +37,18 @@ public class AnswerValidity extends MainUnit {
private final ClarificationQuestion clarificationQuestion;
private AnswerValidity(Builder builder) {
super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, builder.nextUnits, UnitActiveType.DEFAULT, TypeUnit.VALIDITY);
super(
builder.name,
builder.keyWords,
builder.phrase,
builder.pattern,
builder.matchThreshold,
builder.priority,
builder.nextUnits,
UnitActiveType.DEFAULT,
builder.accessibility,
TypeUnit.VALIDITY
);
unitYes = builder.unitYes;
unitNo = builder.unitNo;
unitNull = builder.unitNull;
@ -68,6 +80,7 @@ public class AnswerValidity extends MainUnit {
}
public static final class Builder {
private String name;
private MainUnit unitYes;
private MainUnit unitNo;
private MainUnit unitNull;
@ -78,10 +91,16 @@ public class AnswerValidity extends MainUnit {
private Integer matchThreshold;
private Integer priority;
private Set<MainUnit> nextUnits = new HashSet<>();
private Accessibility accessibility;
private Builder() {
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder unitYes(MainUnit val) {
unitYes = val;
return this;
@ -147,6 +166,11 @@ public class AnswerValidity extends MainUnit {
return this;
}
public Builder accessibility(Accessibility val) {
accessibility = val;
return this;
}
public AnswerValidity build() {
return new AnswerValidity(this);
}

View File

@ -0,0 +1,34 @@
package dev.struchkov.godfather.core.domain.unit;
import dev.struchkov.godfather.context.domain.UnitDefinition;
import dev.struchkov.godfather.core.service.Accessibility;
import java.util.Set;
import java.util.regex.Pattern;
public class LazyUnit extends MainUnit {
private final UnitDefinition unitDefinition;
private LazyUnit(
String name,
Set<String> keyWords,
String phrase,
Pattern pattern,
Integer matchThreshold,
Integer priority,
Set<MainUnit> nextUnits,
UnitActiveType activeType,
String type,
UnitDefinition unitDefinition,
Accessibility accessibility
) {
super(name, keyWords, phrase, pattern, matchThreshold, priority, nextUnits, activeType, accessibility, type);
this.unitDefinition = unitDefinition;
}
public static LazyUnit create(String name, UnitDefinition unitDefinition) {
return new LazyUnit(name, null, null, null, null, null, null, null, null, unitDefinition, null);
}
}

View File

@ -1,6 +1,7 @@
package dev.struchkov.godfather.core.domain.unit;
import dev.struchkov.autoresponder.entity.Unit;
import dev.struchkov.godfather.core.service.Accessibility;
import java.util.Objects;
import java.util.Optional;
@ -20,14 +21,28 @@ public abstract class MainUnit extends Unit<MainUnit> {
*/
protected final String type;
/**
* Уникальный идентификатор юнита
*/
private final String uuid = UUID.randomUUID().toString();
/**
* Режим срабатывания Unit-а.
*/
protected UnitActiveType activeType;
private final String uuid = UUID.randomUUID().toString();
/**
* Уникальное имя юнита
*/
private String name;
/**
* Проверка доступа пользователя к юниту.
*/
private Accessibility accessibility;
protected MainUnit(
String name,
Set<String> keyWords,
String phrase,
Pattern pattern,
@ -35,10 +50,13 @@ public abstract class MainUnit extends Unit<MainUnit> {
Integer priority,
Set<MainUnit> nextUnits,
UnitActiveType activeType,
Accessibility accessibility,
String type
) {
super(keyWords, phrase, pattern, matchThreshold, priority, nextUnits);
this.name = name;
this.activeType = Optional.ofNullable(activeType).orElse(UnitActiveType.DEFAULT);
this.accessibility = accessibility;
this.type = type;
}
@ -46,18 +64,31 @@ public abstract class MainUnit extends Unit<MainUnit> {
return type;
}
public void setActiveType(UnitActiveType activeType) {
this.activeType = activeType;
}
public UnitActiveType getActiveType() {
return activeType;
}
public void setActiveType(UnitActiveType activeType) {
this.activeType = activeType;
}
public String getUuid() {
return uuid;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Optional<Accessibility> getAccessibility() {
return Optional.ofNullable(accessibility);
}
//TODO [27.05.2022]: Возможно стоит добавить имя юнита и убрать остальное
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -0,0 +1,9 @@
package dev.struchkov.godfather.core.service;
import dev.struchkov.godfather.context.domain.content.Message;
public interface Accessibility {
boolean check(Message message);
}

View File

@ -0,0 +1,42 @@
package dev.struchkov.godfather.core.service;
import dev.struchkov.godfather.context.repository.PersonSettingRepository;
import dev.struchkov.godfather.context.service.PersonSettingService;
import dev.struchkov.haiti.utils.Inspector;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
import java.util.Set;
public class PersonSettingServiceImpl implements PersonSettingService {
private final PersonSettingRepository personSettingRepository;
public PersonSettingServiceImpl(PersonSettingRepository personSettingRepository) {
this.personSettingRepository = personSettingRepository;
}
@Override
public Set<Long> getAllPersonIdDisableMessages(@NotNull Set<Long> personIds) {
Inspector.isNotNull(personIds);
return personSettingRepository.findAllByAllowedProcessing(personIds);
}
@Override
public Optional<Boolean> getStateProcessingByPersonId(@NotNull Long personId) {
return personSettingRepository.findStateByPersonId(personId);
}
@Override
public void disableMessageProcessing(@NotNull Long personId) {
Inspector.isNotNull(personId);
personSettingRepository.disableMessageProcessing(personId);
}
@Override
public void enableMessageProcessing(@NotNull Long personId) {
Inspector.isNotNull(personId);
personSettingRepository.enableMessageProcessing(personId);
}
}

View File

@ -0,0 +1,33 @@
package dev.struchkov.godfather.core.service;
import dev.struchkov.godfather.context.domain.UnitPointer;
import dev.struchkov.godfather.context.repository.UnitPointerRepository;
import dev.struchkov.godfather.context.service.UnitPointerService;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public class UnitPointerServiceImpl implements UnitPointerService {
private final UnitPointerRepository unitPointerRepository;
public UnitPointerServiceImpl(UnitPointerRepository unitPointerRepository) {
this.unitPointerRepository = unitPointerRepository;
}
@Override
public UnitPointer save(@NotNull UnitPointer unitPointer) {
return unitPointerRepository.save(unitPointer);
}
@Override
public Optional<String> getUnitNameByPersonId(@NotNull Long personId) {
return unitPointerRepository.findUnitNameByPersonId(personId);
}
@Override
public void removeByPersonId(@NotNull Long personId) {
unitPointerRepository.removeByPersonId(personId);
}
}

View File

@ -0,0 +1,20 @@
package dev.struchkov.godfather.core.service.provider;
import dev.struchkov.godfather.context.domain.content.Mail;
import dev.struchkov.godfather.context.service.EventProvider;
import dev.struchkov.godfather.core.GeneralAutoResponder;
public class EventStoryLineProvider implements EventProvider<Mail> {
private final GeneralAutoResponder<Mail> generalAutoResponder;
public EventStoryLineProvider(GeneralAutoResponder<Mail> generalAutoResponder) {
this.generalAutoResponder = generalAutoResponder;
}
@Override
public void sendEvent(Mail message) {
generalAutoResponder.processingNewMessage(message);
}
}

View File

@ -6,7 +6,7 @@
<groupId>dev.struchkov.godfather</groupId>
<artifactId>godfather-bot</artifactId>
<version>0.0.6</version>
<version>0.0.7</version>
<packaging>pom</packaging>
<modules>
@ -32,12 +32,12 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<godfather.ver>0.0.6</godfather.ver>
<godfather.ver>0.0.7</godfather.ver>
<godfather.context.ver>${godfather.ver}</godfather.context.ver>
<godfather.core.ver>${godfather.ver}</godfather.core.ver>
<autoresponder.ver>2.0.1</autoresponder.ver>
<autoresponder.ver>3.0.0</autoresponder.ver>
<haiti.utils>1.0.2</haiti.utils>
<javax.persistence.api.ver>2.2</javax.persistence.api.ver>