Просмотр исходного кода

эксперименты с StateMachine, похоже успешные. Нужно сделать сервис сохранения состояний и восстановления по id юзера и/или карточки

kpmy 8 лет назад
Родитель
Сommit
7254a09f6d

+ 6 - 0
pom.xml

@@ -53,6 +53,12 @@
             <version>3.0.3</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.statemachine</groupId>
+            <artifactId>spring-statemachine-core</artifactId>
+            <version>1.2.2.RELEASE</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 49 - 0
src/main/java/in/ocsf/these/days/app/SM.java

@@ -0,0 +1,49 @@
+package in.ocsf.these.days.app;/* kpmy 20.02.2017 */
+
+import in.ocsf.these.days.app.object.ChatEvent;
+import in.ocsf.these.days.app.object.ChatState;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.statemachine.StateMachineContext;
+import org.springframework.statemachine.StateMachinePersist;
+import org.springframework.statemachine.config.EnableStateMachineFactory;
+import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
+import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
+import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
+
+import java.util.EnumSet;
+import java.util.HashMap;
+
+@Configuration
+@EnableStateMachineFactory
+public class SM extends EnumStateMachineConfigurerAdapter<ChatState, ChatEvent> {
+
+    @Override
+    public void configure(StateMachineStateConfigurer<ChatState, ChatEvent> states) throws Exception {
+        states.withStates()
+                .initial(ChatState.unknown)
+                .states(EnumSet.allOf(ChatState.class));
+    }
+
+    @Override
+    public void configure(StateMachineTransitionConfigurer<ChatState, ChatEvent> transitions) throws Exception {
+        transitions.withExternal().source(ChatState.unknown).target(ChatState.newbie).event(ChatEvent.start)
+                .and()
+                .withExternal().source(ChatState.newbie).target(ChatState.client).event(ChatEvent.init);
+    }
+
+    public static class InMemoryStateMachinePersist implements StateMachinePersist<ChatState, ChatEvent, Long> {
+
+        private final HashMap<Long, StateMachineContext<ChatState, ChatEvent>> contexts = new HashMap<>();
+
+        @Override
+        public void write(StateMachineContext<ChatState, ChatEvent> context, Long contextObj) throws Exception {
+            contexts.put(contextObj, context);
+        }
+
+        @Override
+        public StateMachineContext<ChatState, ChatEvent> read(Long contextObj) throws Exception {
+            return contexts.get(contextObj);
+        }
+    }
+
+}

+ 41 - 0
src/main/java/in/ocsf/these/days/app/messaging/ChatHlelper.java

@@ -0,0 +1,41 @@
+package in.ocsf.these.days.app.messaging;/* kpmy 20.02.2017 */
+
+import com.pengrad.telegrambot.Callback;
+import com.pengrad.telegrambot.TelegramBot;
+import com.pengrad.telegrambot.TelegramBotAdapter;
+import com.pengrad.telegrambot.request.SendMessage;
+import com.pengrad.telegrambot.response.SendResponse;
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class ChatHlelper {
+
+    private final Logger log = Logger.getLogger(getClass());
+
+    @Value("${these-days.bot.token}")
+    private String token;
+
+    public Optional<SendResponse> sendMessage(SendMessage request) {
+        TelegramBot bot = TelegramBotAdapter.build(token);
+        List<SendResponse> responseList = new ArrayList<>();
+        bot.execute(request, new Callback<SendMessage, SendResponse>() {
+            @Override
+            public void onResponse(SendMessage request, SendResponse response) {
+                responseList.add(response);
+            }
+
+            @Override
+            public void onFailure(SendMessage request, IOException e) {
+                e.printStackTrace();
+            }
+        });
+        return !responseList.isEmpty() ? Optional.of(responseList.get(0)) : Optional.empty();
+    }
+}

+ 5 - 4
src/main/java/in/ocsf/these/days/app/listener/ChatListener.java → src/main/java/in/ocsf/these/days/app/messaging/ChatListener.java

@@ -1,4 +1,4 @@
-package in.ocsf.these.days.app.listener;/* kpmy 19.02.2017 */
+package in.ocsf.these.days.app.messaging;/* kpmy 19.02.2017 */
 
 import com.pengrad.telegrambot.Callback;
 import com.pengrad.telegrambot.TelegramBot;
@@ -47,7 +47,7 @@ public class ChatListener {
 
     @Async
     protected void update(Update update) {
-        updateService.update(update);
+        updateService.handleUpdate(update);
     }
 
     @PostConstruct
@@ -81,9 +81,10 @@ public class ChatListener {
                 if (response.isOk()) {
                     if (response.updates() != null)
                         response.updates().stream().forEach(u -> {
-                            update(u);
-                            if (u.updateId() > state.getUpdateId())
+                            if (u.updateId() > state.getUpdateId()) {
                                 state.setUpdateId(Long.valueOf(u.updateId()));
+                                update(u);
+                            }
                         });
                     stateRepo.save(state);
                 } else {

+ 56 - 0
src/main/java/in/ocsf/these/days/app/messaging/UpdateHelper.java

@@ -0,0 +1,56 @@
+package in.ocsf.these.days.app.messaging;/* kpmy 20.02.2017 */
+
+import com.pengrad.telegrambot.model.Update;
+import com.pengrad.telegrambot.model.User;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class UpdateHelper {
+
+    private Update inner;
+
+    private UpdateHelper() {
+
+    }
+
+    public static UpdateHelper from(Update update) {
+        UpdateHelper uh = new UpdateHelper();
+        uh.inner = update;
+        return uh;
+    }
+
+    public User getUser() {
+        return !isEdit() ? inner.message().from() : inner.editedMessage().from();
+    }
+
+    public boolean hasUser() {
+        return !isEdit() ? inner.message().from() != null : inner.editedMessage().from() != null;
+    }
+
+    public List<String> getSplitCommand() {
+        if (!isCommand()) return new ArrayList<>();
+        return Arrays.asList(getText().split(" ", -1));
+    }
+
+    public String getText() {
+        return hasText() ? !isEdit() ? inner.message().text() : inner.editedMessage().text() : null;
+    }
+
+    public boolean isCommand() {
+        return hasText() && getText().startsWith("/");
+    }
+
+    public boolean isEdit() {
+        return inner.editedMessage() != null;
+    }
+
+    public boolean hasText() {
+        return !isEdit() ? inner.message().text() != null : inner.editedMessage().text() != null;
+    }
+
+    public Update getUpdate() {
+        return inner;
+    }
+}

+ 21 - 0
src/main/java/in/ocsf/these/days/app/object/Card.java

@@ -0,0 +1,21 @@
+package in.ocsf.these.days.app.object;/* kpmy 20.02.2017 */
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Document(collection = "card")
+public class Card {
+
+    @Id
+    private Long id;
+
+    private User user;
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+}

+ 5 - 0
src/main/java/in/ocsf/these/days/app/object/ChatEvent.java

@@ -0,0 +1,5 @@
+package in.ocsf.these.days.app.object;/* kpmy 20.02.2017 */
+
+public enum ChatEvent {
+    start, init
+}

+ 5 - 0
src/main/java/in/ocsf/these/days/app/object/ChatState.java

@@ -0,0 +1,5 @@
+package in.ocsf.these.days.app.object;/* kpmy 20.02.2017 */
+
+public enum ChatState {
+    unknown, newbie, client
+}

+ 2 - 2
src/main/java/in/ocsf/these/days/app/object/UpdateState.java

@@ -3,10 +3,10 @@ package in.ocsf.these.days.app.object;/* kpmy 19.02.2017 */
 import org.springframework.data.annotation.Id;
 import org.springframework.data.mongodb.core.mapping.Document;
 
-@Document(collection = "update-state")
+@Document(collection = "handleUpdate-state")
 public class UpdateState {
 
-    public static final String defaultId = "last-update";
+    public static final String defaultId = "last-handleUpdate";
 
     @Id
     private String id = defaultId;

+ 12 - 0
src/main/java/in/ocsf/these/days/app/repo/CardRepositrory.java

@@ -0,0 +1,12 @@
+package in.ocsf.these.days.app.repo;/* kpmy 20.02.2017 */
+
+import in.ocsf.these.days.app.object.Card;
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.data.rest.core.annotation.RepositoryRestResource;
+
+import java.util.Optional;
+
+@RepositoryRestResource(exported = false)
+public interface CardRepositrory extends MongoRepository<Card, Long> {
+    Optional<Card> findFirstByUserId(Long id);
+}

+ 47 - 10
src/main/java/in/ocsf/these/days/app/service/UpdateService.java

@@ -1,32 +1,69 @@
 package in.ocsf.these.days.app.service;/* kpmy 19.02.2017 */
 
-import com.google.common.base.Optional;
 import com.pengrad.telegrambot.model.Update;
-import in.ocsf.these.days.app.object.Message;
+import in.ocsf.these.days.app.SM;
+import in.ocsf.these.days.app.messaging.UpdateHelper;
+import in.ocsf.these.days.app.object.ChatEvent;
+import in.ocsf.these.days.app.object.ChatState;
 import in.ocsf.these.days.app.object.User;
+import in.ocsf.these.days.app.repo.CardRepositrory;
 import in.ocsf.these.days.app.repo.MessageRepository;
 import in.ocsf.these.days.app.repo.UserRepository;
+import org.apache.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.config.StateMachineFactory;
+import org.springframework.statemachine.persist.DefaultStateMachinePersister;
+import org.springframework.statemachine.persist.StateMachinePersister;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 @Service
 public class UpdateService {
 
+    private final Logger log = Logger.getLogger(getClass());
+
     @Autowired
     private UserRepository userRepo;
 
     @Autowired
     private MessageRepository msgRepo;
 
-    public void update(Update upd) {
-        com.pengrad.telegrambot.model.Message src = Optional.fromNullable(upd.message()).or(Optional.fromNullable(upd.editedMessage())).orNull();
-        if (src == null) throw new RuntimeException();
+    @Autowired
+    private CardRepositrory cardRepo;
+
+    @Autowired
+    private StateMachineFactory<ChatState, ChatEvent> stateMachineFactory;
+
+    private void handleUpdate(UpdateHelper upd) throws Exception {
+        if (!upd.isEdit()) {
+            if (upd.hasText()) {
+                if (upd.isCommand()) {
+                    List<String> cmd = upd.getSplitCommand();
+                    switch (cmd.get(0)) {
+                        case "/start":
+                            User user = User.fromUser(upd.getUser());
+                            StateMachine<ChatState, ChatEvent> state = stateMachineFactory.getStateMachine(Long.toHexString(user.getId()));
+                            state.start();
+                            StateMachinePersister<ChatState, ChatEvent, Long> persist = new DefaultStateMachinePersister<>(new SM.InMemoryStateMachinePersist());
+                            persist.persist(state, user.getId());
+                            log.info(Long.toHexString(user.getId()));
+                            break;
+                    }
+                } else {
 
-        User user = userRepo.save(User.fromUser(src.from()));
+                }
+            }
+        }
+    }
 
-        Message msg = new Message();
-        msg.setId(Long.valueOf(src.messageId()));
-        msg.setSender(user);
-        msgRepo.save(msg);
+    public void handleUpdate(Update upd) {
+        try {
+            handleUpdate(UpdateHelper.from(upd));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
     }
+
 }