mizo0203/lily-white-line-notify

View on GitHub
src/main/java/com/mizo0203/lilywhite/domain/ReminderUseCase.java

Summary

Maintainability
C
1 day
Test Coverage
package com.mizo0203.lilywhite.domain;

import com.linecorp.bot.model.action.Action;
import com.linecorp.bot.model.action.DatetimePickerAction;
import com.linecorp.bot.model.action.PostbackAction;
import com.linecorp.bot.model.action.URIAction;
import com.linecorp.bot.model.event.MessageEvent;
import com.linecorp.bot.model.event.PostbackEvent;
import com.linecorp.bot.model.event.message.TextMessageContent;
import com.linecorp.bot.model.message.Message;
import com.linecorp.bot.model.message.TemplateMessage;
import com.linecorp.bot.model.message.TextMessage;
import com.linecorp.bot.model.message.template.ButtonsTemplate;
import com.linecorp.bot.model.message.template.ConfirmTemplate;
import com.linecorp.bot.model.message.template.Template;
import com.mizo0203.lilywhite.repo.OfyRepository;
import com.mizo0203.lilywhite.repo.ReminderState;
import com.mizo0203.lilywhite.repo.Repository;
import com.mizo0203.lilywhite.repo.line.data.ResponseStatusData;
import com.mizo0203.lilywhite.repo.objectify.entity.LineTalkRoomConfig;
import com.mizo0203.lilywhite.repo.objectify.entity.Reminder;

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

public class ReminderUseCase implements AutoCloseable {

  private static final String ACTION_DATA_REQUEST_REMINDER_CANCELLATION =
      "ACTION_DATA_REQUEST_REMINDER_CANCELLATION";
  private static final String ACTION_DATA_REQUEST_ACCESS_TOKEN_COMPLETION =
      "ACTION_DATA_REQUEST_ACCESS_TOKEN_COMPLETION";
  private static final String ACTION_DATA_CANCEL_REMINDER = "ACTION_DATA_CANCEL_REMINDER";
  private static final String ACTION_DATA_NOT_CANCEL_REMINDER = "ACTION_DATA_NOT_CANCEL_REMINDER";
  private static final String ACTION_DATA_CREATE_REMINDER = "ACTION_DATA_CREATE_REMINDER";

  @SuppressWarnings("unused")
  private static final Logger LOG = Logger.getLogger(ReminderUseCase.class.getName());

  private static final String ACTION_DATA_REQUEST_REMINDER_DATE_SET =
      "ACTION_DATA_REQUEST_REMINDER_DATE_SET";
  private final Repository mRepository;
  private final OfyRepository mOfyRepository;
  private final LineTalkRoomConfig mConfig;
  private final Reminder mReminder;
  private final ReminderState mReminderState;
  private final Translator mTranslator;
  private boolean mDelete;

  /* package */ ReminderUseCase(Repository repository, LineTalkRoomConfig config) {
    mRepository = repository;
    mOfyRepository = new OfyRepository();
    mConfig = config;
    Long editingReminderId = config.getEditingReminderId();
    Reminder reminder = null;
    if (editingReminderId != null) {
      reminder = mOfyRepository.loadReminder(editingReminderId);
    }
    if (reminder == null) {
      reminder = mOfyRepository.factoryReminder();
      config.setEditingReminderId(reminder.getId());
    }
    mReminder = reminder;
    mReminderState = getReminderState(reminder);
    mTranslator = new Translator();
  }

  private static ReminderState getReminderState(Reminder reminder) {
    if (reminder.getReminderMessage() == null) {
      return ReminderState.NO_REMINDER_MESSAGE;
    } else if (reminder.getAccessToken() == null) {
      return ReminderState.NO_ACCESS_TOKEN;
    } else if (!reminder.isReminderEnqueued()) {
      return ReminderState.HAS_ACCESS_TOKEN;
    } else if (!reminder.isCancellationConfirm()) {
      return ReminderState.REMINDER_ENQUEUED;
    } else {
      return ReminderState.REMINDER_CANCELLATION_CONFIRM;
    }
  }

  @Override
  public void close() {
    if (mDelete) {
      mOfyRepository.deleteReminder(mReminder.getId());
    } else {
      mOfyRepository.saveReminder(mReminder);
    }
  }

  /* package */ void onLineTextMessage(MessageEvent event, TextMessageContent message) {
    switch (mReminderState) {
      case NO_REMINDER_MESSAGE:
        onResponseReminderMessage(event, message.getText().split("\n")[0]);
        break;
      case NO_ACCESS_TOKEN:
      case HAS_ACCESS_TOKEN:
      case REMINDER_ENQUEUED:
      case REMINDER_CANCELLATION_CONFIRM:
      default:
        // NOP
        break;
    }
  }

  private void onResponseReminderMessage(MessageEvent event, String reminderMessage) {
    mReminder.setReminderMessage(reminderMessage);
    mRepository.replyMessage(event.getReplyToken(), createMessageToRequestAccessToken());
  }

  private void onResponseCompleteAccessToken(final PostbackEvent event) {
    switch (mReminderState) {
      case NO_REMINDER_MESSAGE:
        // NOP
        break;
      case NO_ACCESS_TOKEN:
        mRepository.replyMessage(event.getReplyToken(), createMessageToRequestAccessToken());
        break;
      case HAS_ACCESS_TOKEN:
        mRepository.status(mReminder, createStatusCallback(event));
        break;
      case REMINDER_ENQUEUED:
      case REMINDER_CANCELLATION_CONFIRM:
      default:
        // NOP
        break;
    }
  }

  private Repository.Callback<ResponseStatusData> createStatusCallback(final PostbackEvent event) {
    return (apiRateLimit, res) -> {
      if (res != null && res.getTarget() != null) {
        mRepository.replyMessage(event.getReplyToken(), createMessageToRequestReminderDate());
      } else {
        mReminder.setAccessToken(null);
        mRepository.replyMessage(event.getReplyToken(), createMessageToRequestAccessToken());
      }
    };
  }

  private Message createMessageToRequestAccessToken() {
    return new TemplateMessage(
        "テンプレートメッセージはiOS版およびAndroid版のLINE 6.7.0以降で対応しています。", createButtonsTemplateToAccessToken());
  }

  private Template createButtonsTemplateToAccessToken() {
    return ButtonsTemplate.builder()
        .text(
            "1. 通知を送信するトークルームを選択してください\n"
                + "2. LINE の公式アカウント \"LINE Notify\" をトークルームに招待してください\n"
                + "3. 完了ボタンを押してくささい")
        .actions(createPostbackActionsToRequestAccessToken())
        .build();
  }

  private List<Action> createPostbackActionsToRequestAccessToken() {
    return Arrays.asList(
        new URIAction(
            "トークルームを選択",
            "https://lily-white-line-notify.appspot.com/hello?state=" + mReminder.getId()),
        new PostbackAction("完了", ACTION_DATA_REQUEST_ACCESS_TOKEN_COMPLETION));
  }

  private Message createMessageToRequestReminderDate() {
    return new TemplateMessage(
        "テンプレートメッセージはiOS版およびAndroid版のLINE 6.7.0以降で対応しています。",
        createButtonsTemplateToRequestReminderDate());
  }

  private Template createButtonsTemplateToRequestReminderDate() {
    return ButtonsTemplate.builder()
        .text("リマインダー日時をセットしますよー")
        .actions(createDateTimePickerActionsToRequestReminderDate())
        .build();
  }

  private List<Action> createDateTimePickerActionsToRequestReminderDate() {
    return Collections.singletonList(createDateTimePickerActionToRequestReminderDate());
  }

  private Action createDateTimePickerActionToRequestReminderDate() {
    return new DatetimePickerAction(
        "セット", ACTION_DATA_REQUEST_REMINDER_DATE_SET, Define.Mode.DATE_TIME.toString());
  }

  /* package */ void onLinePostBackNoParam(PostbackEvent event) {

    String data = getData(event);
    if (ACTION_DATA_REQUEST_REMINDER_CANCELLATION.equals(data)) {
      onResponseReminderCancellation(event, getReminderId(event));
    } else if (ACTION_DATA_REQUEST_ACCESS_TOKEN_COMPLETION.equals(data)) {
      onResponseCompleteAccessToken(event);
    } else if (ACTION_DATA_CANCEL_REMINDER.equals(data)) {
      onResponseCancelReminder(event);
    } else if (ACTION_DATA_NOT_CANCEL_REMINDER.equals(data)) {
      onResponseNotCancelReminder(event);
    } else if (ACTION_DATA_CREATE_REMINDER.equals(data)) {
      onResponseReset(event);
    }
  }

  private String getData(PostbackEvent event) {
    String data = event.getPostbackContent().getData();
    if (data.contains("+")) {
      return data.substring(0, data.indexOf("+"));
    } else {
      return data;
    }
  }

  private long getReminderId(PostbackEvent event) {
    String data = event.getPostbackContent().getData();
    if (data.contains("+")) {
      return Long.parseLong(data.substring(data.indexOf("+") + 1));
    } else {
      throw new RuntimeException();
    }
  }

  /* package */ void onLinePostBackDateParam(PostbackEvent event, Date date) {
    String data = event.getPostbackContent().getData();
    if (ACTION_DATA_REQUEST_REMINDER_DATE_SET.equals(data)) {
      onResponseReminderDate(event, date);
    }
  }

  private void onResponseReminderDate(PostbackEvent event, Date date) {
    switch (mReminderState) {
      case NO_REMINDER_MESSAGE:
      case NO_ACCESS_TOKEN:
        // NOP
        break;
      case HAS_ACCESS_TOKEN:
        enqueueReminderTask(date);
        replyReminderConfirmMessage(event.getReplyToken(), date);
        notifyReminderConfirmMessage(date);
        mConfig.setEditingReminderId(null);
        break;
      case REMINDER_ENQUEUED:
      case REMINDER_CANCELLATION_CONFIRM:
      default:
        // NOP
        break;
    }
  }

  private void onResponseReminderCancellation(PostbackEvent event, long reminderId) {
    if (mOfyRepository.loadReminder(reminderId) == null) {
      mRepository.replyMessage(event.getReplyToken(), new TextMessage("リマインダーを送信済みです"));
      return;
    }
    deleteReminder();
    mConfig.setEditingReminderId(reminderId);
    try (ReminderUseCase reminderUseCase = new ReminderUseCase(mRepository, mConfig)) {
      reminderUseCase.setCancellationConfirm(true);
      reminderUseCase.replyReminderCancellationConfirmMessage(event.getReplyToken());
    }
  }

  private void onResponseCancelReminder(PostbackEvent event) {
    switch (mReminderState) {
      case NO_REMINDER_MESSAGE:
      case NO_ACCESS_TOKEN:
      case HAS_ACCESS_TOKEN:
      case REMINDER_ENQUEUED:
        // NOP
        break;
      case REMINDER_CANCELLATION_CONFIRM:
        deleteReminder();
        replyCanceledReminderMessage(event.getReplyToken());
        break;
      default:
        // NOP
        break;
    }
  }

  private void onResponseNotCancelReminder(PostbackEvent event) {
    switch (mReminderState) {
      case NO_REMINDER_MESSAGE:
      case NO_ACCESS_TOKEN:
      case HAS_ACCESS_TOKEN:
      case REMINDER_ENQUEUED:
        // NOP
        break;
      case REMINDER_CANCELLATION_CONFIRM:
        setCancellationConfirm(false);
        replyNotCanceledReminderMessage(event.getReplyToken());
        mConfig.setEditingReminderId(null);
        break;
      default:
        // NOP
        break;
    }
  }

  private void onResponseReset(PostbackEvent event) {
    switch (mReminderState) {
      case NO_REMINDER_MESSAGE:
        replyMessageToRequestReminderMessage(event.getReplyToken());
        break;
      case NO_ACCESS_TOKEN:
      case HAS_ACCESS_TOKEN:
      case REMINDER_ENQUEUED:
      case REMINDER_CANCELLATION_CONFIRM:
      default:
        // NOP
        break;
    }
  }

  private Message createTemplateMessageToReset() {
    return new TemplateMessage(
        "テンプレートメッセージはiOS版およびAndroid版のLINE 6.7.0以降で対応しています。", createTemplateToReset());
  }

  private Template createTemplateToReset() {
    return new ButtonsTemplate(
        null,
        null,
        "キャンセルしましたー",
        Collections.singletonList(createPostbackActionToCreateReminder()));
  }

  private Action createPostbackActionToCreateReminder() {
    return new PostbackAction("新しいリマインダーを作成", ACTION_DATA_CREATE_REMINDER);
  }

  private void enqueueReminderTask(Date date) {
    mRepository.enqueueReminderTask(mReminder, date.getTime());
  }

  private void replyReminderConfirmMessage(String replyToken, Date date) {
    mRepository.replyMessage(
        replyToken,
        createMessageToConfirmReminder("リマインダーをセットしましたー\n" + mTranslator.formatDate(date)));
  }

  private void notifyReminderConfirmMessage(Date date) {
    mRepository.notify(
        mReminder,
        mConfig.getNickname() + "さんが\n" + mTranslator.formatDate(date) + " に\n" + "リマインダーをセットしました");
  }

  private Message createMessageToConfirmReminder(String text) {
    return new TemplateMessage(
        "テンプレートメッセージはiOS版およびAndroid版のLINE 6.7.0以降で対応しています。",
        createButtonsTemplateToConfirmReminder(text));
  }

  private Template createButtonsTemplateToConfirmReminder(String text) {
    return ButtonsTemplate.builder()
        .text(text)
        .actions(
            Arrays.asList(
                createPostbackActionToRequestReminderCancellation(),
                createPostbackActionToCreateReminder()))
        .build();
  }

  private Action createPostbackActionToRequestReminderCancellation() {
    return new PostbackAction(
        "キャンセル",
        ACTION_DATA_REQUEST_REMINDER_CANCELLATION + "+" + String.valueOf(mReminder.getId()));
  }

  private void replyReminderCancellationConfirmMessage(String replyToken) {
    mRepository.replyMessage(replyToken, createMessageToConfirmCancellationReminder());
  }

  private Message createMessageToConfirmCancellationReminder() {
    return new TemplateMessage(
        "テンプレートメッセージはiOS版およびAndroid版のLINE 6.7.0以降で対応しています。",
        createConfirmTemplateToConfirmCancellationReminder());
  }

  private Template createConfirmTemplateToConfirmCancellationReminder() {
    return new ConfirmTemplate(
        "本当にキャンセルしますかー?", createPostbackActionsToConfirmCancellationReminder());
  }

  private List<Action> createPostbackActionsToConfirmCancellationReminder() {
    return Arrays.asList(
        createPostbackActionToCancelReminder(), createPostbackActionToNotCancelReminder());
  }

  private Action createPostbackActionToCancelReminder() {
    return new PostbackAction("はい", ACTION_DATA_CANCEL_REMINDER);
  }

  private Action createPostbackActionToNotCancelReminder() {
    return new PostbackAction("いいえ", ACTION_DATA_NOT_CANCEL_REMINDER);
  }

  private void replyCanceledReminderMessage(String replyToken) {
    mRepository.replyMessage(replyToken, createTemplateMessageToReset());
  }

  private void replyNotCanceledReminderMessage(String replyToken) {
    mRepository.replyMessage(replyToken, new TextMessage("そのままですー"));
  }

  private void setCancellationConfirm(boolean cancellationConfirm) {
    mReminder.setCancellationConfirm(cancellationConfirm);
  }

  private void replyMessageToRequestReminderMessage(String replyToken) {
    mRepository.replyMessage(
        replyToken, new TextMessage("リマインダーをセットしますよー\nメッセージを入力してくださいー\n例) 春ですよー"));
  }

  /* package */ void deleteReminder() {
    deleteReminderTask();
    mRepository.notify(mReminder, mConfig.getNickname() + "さんがリマインダーをキャンセルしました");
    revokeAccessToken();
    mConfig.setEditingReminderId(null);
    mDelete = true;
  }

  private void revokeAccessToken() {
    String accessToken = mReminder.getAccessToken();
    mRepository.revoke(accessToken);
    mReminder.setAccessToken(null);
  }

  private void deleteReminderTask() {
    String taskName = mReminder.getReminderEnqueuedTaskName();
    mRepository.deleteReminderTask(taskName);
    mReminder.setReminderEnqueuedTaskName(null);
  }
}