forked from hannibal002/SkyHanni
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: Remind Command (hannibal002#1708)
Co-authored-by: Zickles <zicklesistaken@gmail.com> Co-authored-by: Cal <cwolfson58@gmail.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
- Loading branch information
1 parent
f059831
commit 71d34ff
Showing
6 changed files
with
318 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
src/main/java/at/hannibal2/skyhanni/config/features/misc/RemindersConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package at.hannibal2.skyhanni.config.features.misc; | ||
|
||
import com.google.gson.annotations.Expose; | ||
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; | ||
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; | ||
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; | ||
|
||
public class RemindersConfig { | ||
@Expose | ||
@ConfigOption(name = "Auto Delete Reminders", desc = "Automatically deletes reminders after they have been shown once.") | ||
@ConfigEditorBoolean | ||
public boolean autoDeleteReminders = false; | ||
|
||
@Expose | ||
@ConfigOption( | ||
name = "Reminder Interval", | ||
desc = "The interval in minutes in which reminders are shown again, after they have been shown once." | ||
) | ||
@ConfigEditorSlider(minValue = 0f, maxValue = 60f, minStep = 1f) | ||
public float interval = 5f; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
src/main/java/at/hannibal2/skyhanni/features/misc/reminders/Reminder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package at.hannibal2.skyhanni.features.misc.reminders | ||
|
||
import at.hannibal2.skyhanni.utils.SimpleTimeMark | ||
import com.google.gson.annotations.Expose | ||
import java.time.Instant | ||
import java.time.LocalDate | ||
import java.time.ZoneId | ||
import java.time.ZonedDateTime | ||
import java.time.format.DateTimeFormatter | ||
import java.time.format.FormatStyle | ||
import java.util.Locale | ||
import kotlin.time.Duration | ||
|
||
data class Reminder( | ||
@Expose var reason: String, | ||
@Expose var remindAt: SimpleTimeMark, | ||
@Expose var lastReminder: SimpleTimeMark = SimpleTimeMark.farPast(), | ||
) { | ||
|
||
fun formatShort(): String { | ||
val time = getRemindTime() | ||
val date = time.toLocalDate() | ||
if (date.isEqual(LocalDate.now())) { | ||
return time.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withDefaultLocale()) | ||
} | ||
return date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withDefaultLocale()) | ||
} | ||
|
||
fun formatFull(): String = getRemindTime().format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withDefaultLocale()) | ||
|
||
fun shouldRemind(interval: Duration) = remindAt.isInPast() && lastReminder.passedSince() >= interval | ||
|
||
private fun getRemindTime(): ZonedDateTime = Instant.ofEpochMilli(remindAt.toMillis()).atZone(ZoneId.systemDefault()) | ||
|
||
private fun DateTimeFormatter.withDefaultLocale(): DateTimeFormatter = withLocale(Locale.getDefault()) | ||
} | ||
|
249 changes: 249 additions & 0 deletions
249
src/main/java/at/hannibal2/skyhanni/features/misc/reminders/ReminderManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
package at.hannibal2.skyhanni.features.misc.reminders | ||
|
||
import at.hannibal2.skyhanni.SkyHanniMod | ||
import at.hannibal2.skyhanni.events.SecondPassedEvent | ||
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule | ||
import at.hannibal2.skyhanni.utils.ChatUtils | ||
import at.hannibal2.skyhanni.utils.SimpleTimeMark | ||
import at.hannibal2.skyhanni.utils.StringUtils | ||
import at.hannibal2.skyhanni.utils.TimeUtils | ||
import at.hannibal2.skyhanni.utils.TimeUtils.format | ||
import at.hannibal2.skyhanni.utils.TimeUtils.minutes | ||
import at.hannibal2.skyhanni.utils.chat.Text | ||
import at.hannibal2.skyhanni.utils.chat.Text.asComponent | ||
import at.hannibal2.skyhanni.utils.chat.Text.center | ||
import at.hannibal2.skyhanni.utils.chat.Text.command | ||
import at.hannibal2.skyhanni.utils.chat.Text.fitToChat | ||
import at.hannibal2.skyhanni.utils.chat.Text.hover | ||
import at.hannibal2.skyhanni.utils.chat.Text.send | ||
import at.hannibal2.skyhanni.utils.chat.Text.style | ||
import at.hannibal2.skyhanni.utils.chat.Text.suggest | ||
import at.hannibal2.skyhanni.utils.chat.Text.wrap | ||
import net.minecraft.util.EnumChatFormatting | ||
import net.minecraft.util.IChatComponent | ||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent | ||
import kotlin.time.Duration | ||
import kotlin.time.Duration.Companion.seconds | ||
|
||
@SkyHanniModule | ||
object ReminderManager { | ||
|
||
private const val REMINDERS_PER_PAGE = 10 | ||
|
||
// Random numbers chosen, this will be used to delete the old list and action messages | ||
private const val REMINDERS_LIST_ID = -546745 | ||
private const val REMINDERS_ACTION_ID = -546746 | ||
private const val REMINDERS_MESSAGE_ID = -546747 | ||
|
||
private val storage get() = SkyHanniMod.feature.storage.reminders | ||
private val config get() = SkyHanniMod.feature.misc.reminders | ||
|
||
private var listPage = 1 | ||
|
||
private fun getSortedReminders() = storage.entries.sortedBy { it.value.remindAt } | ||
|
||
private fun sendMessage(message: String) = Text.join("§e[Reminder]", " ", message).send(REMINDERS_ACTION_ID) | ||
|
||
private fun createDivider() = Text.HYPHEN.fitToChat().style { | ||
strikethrough = true | ||
color = EnumChatFormatting.BLUE | ||
} | ||
|
||
private fun parseDuration(text: String): Duration? = try { | ||
val duration = TimeUtils.getDuration(text) | ||
if (duration <= 1.seconds) null else duration | ||
} catch (e: Exception) { | ||
null | ||
} | ||
|
||
private fun listReminders(page: Int) { | ||
val reminders = getSortedReminders() | ||
val maxPage = (reminders.size + REMINDERS_PER_PAGE - 1) / REMINDERS_PER_PAGE | ||
|
||
listPage = page.coerceIn(0, maxPage) | ||
|
||
val text: MutableList<IChatComponent> = mutableListOf() | ||
|
||
text.add(createDivider()) | ||
|
||
text.add( | ||
Text.join( | ||
if (listPage > 1) "§6§l<<".asComponent { | ||
hover = "§eClick to view page ${listPage - 1}".asComponent() | ||
command = "/shremind list ${listPage - 1}" | ||
} else null, | ||
" ", | ||
"§6Reminders (Page $listPage of $maxPage)", | ||
" ", | ||
if (listPage < maxPage) "§6§l>>".asComponent { | ||
hover = "§eClick to view page ${listPage + 1}".asComponent() | ||
command = "/shremind list ${listPage + 1}" | ||
} else null, | ||
).center(), | ||
) | ||
|
||
if (reminders.isNotEmpty()) { | ||
for (i in (listPage - 1) * REMINDERS_PER_PAGE until listPage * REMINDERS_PER_PAGE) { | ||
if (i >= reminders.size) break | ||
val (id, reminder) = reminders[i] | ||
|
||
text.add( | ||
Text.join( | ||
"§c✕".asComponent { | ||
hover = "§7Click to remove".asComponent() | ||
command = "/shremind remove -l $id" | ||
}.wrap("§8[", "§8]"), | ||
" ", | ||
"§e✎".asComponent { | ||
hover = "§7Click to start editing".asComponent() | ||
suggest = "/shremind edit -l $id ${reminder.reason} " | ||
}.wrap("§8[", "§8]"), | ||
" ", | ||
"§6${reminder.formatShort()}".asComponent { | ||
hover = "§7${reminder.formatFull()}".asComponent() | ||
}.wrap("§8[", "§8]"), | ||
" ", | ||
"§7${reminder.reason}", | ||
), | ||
) | ||
} | ||
} else { | ||
text.add(Text.EMPTY) | ||
text.add("§cNo reminders found.".asComponent().center()) | ||
text.add(Text.EMPTY) | ||
} | ||
|
||
text.add(createDivider()) | ||
|
||
Text.join(*text.toTypedArray(), separator = Text.NEWLINE).send(REMINDERS_LIST_ID) | ||
} | ||
|
||
private fun createReminder(args: Array<String>) { | ||
if (args.size < 2) return help() | ||
|
||
val time = parseDuration(args.first()) ?: return ChatUtils.userError("Invalid time format") | ||
val reminder = args.drop(1).joinToString(" ") | ||
val remindAt = SimpleTimeMark.now().plus(time) | ||
|
||
storage[StringUtils.generateRandomId()] = Reminder(reminder, remindAt) | ||
sendMessage("§6Reminder set for ${time.format()}") | ||
} | ||
|
||
private fun actionReminder( | ||
args: List<String>, | ||
command: String, | ||
vararg arguments: String, | ||
action: (List<String>, Reminder) -> String, | ||
) { | ||
val argumentText = arguments.joinToString(" ") | ||
if (args.size < arguments.size) return ChatUtils.userError("/shremind $command $argumentText") | ||
|
||
if (args.first() == "-l") { | ||
if (args.size < arguments.size + 1) return ChatUtils.userError("/shremind $command -l $argumentText") | ||
if (storage[args.drop(1).first()] == null) return ChatUtils.userError("Reminder not found!") | ||
action(args.drop(2), storage[args.drop(1).first()]!!).apply { | ||
listReminders(listPage) | ||
sendMessage(this) | ||
} | ||
} else if (storage[args.first()] == null) { | ||
return ChatUtils.userError("Reminder not found!") | ||
} else { | ||
sendMessage(action(args.drop(1), storage[args.first()]!!)) | ||
} | ||
} | ||
|
||
private fun removeReminder(args: List<String>) = actionReminder( | ||
args, | ||
"remove", | ||
"[id]", | ||
) { _, reminder -> | ||
storage.values.remove(reminder) | ||
"§cReminder deleted." | ||
} | ||
|
||
private fun editReminder(args: List<String>) = actionReminder( | ||
args, | ||
"edit", | ||
"[id]", | ||
"[reminder]", | ||
) { arguments, reminder -> | ||
reminder.reason = arguments.joinToString(" ") | ||
"§6Reminder edited." | ||
} | ||
|
||
private fun moveReminder(args: List<String>) = actionReminder( | ||
args, | ||
"move", | ||
"[id]", | ||
"[time]", | ||
) { arguments, reminder -> | ||
val time = parseDuration(arguments.first()) ?: return@actionReminder "§cInvalid time format!" | ||
reminder.remindAt = SimpleTimeMark.now().plus(time) | ||
"§6Reminder moved to ${time.format()}" | ||
} | ||
|
||
private fun help() { | ||
createDivider().send() | ||
"§6SkyHanni Reminder Commands:".asComponent().send() | ||
"§e/shremind <time> <reminder> - §bCreates a new reminder".asComponent().send() | ||
"§e/shremind list <page> - §bLists all reminders".asComponent().send() | ||
"§e/shremind remove <id> - §bRemoves a reminder".asComponent().send() | ||
"§e/shremind edit <id> <reminder> - §bEdits a reminder".asComponent().send() | ||
"§e/shremind move <id> <time> - §bMoves a reminder".asComponent().send() | ||
"§e/shremind help - §bShows this help message".asComponent().send() | ||
createDivider().send() | ||
} | ||
|
||
@SubscribeEvent | ||
fun onSecondPassed(event: SecondPassedEvent) { | ||
val remindersToSend = mutableListOf<IChatComponent>() | ||
|
||
for ((id, reminder) in getSortedReminders()) { | ||
if (!reminder.shouldRemind(config.interval.minutes)) continue | ||
reminder.lastReminder = SimpleTimeMark.now() | ||
var actionsComponent: IChatComponent? = null | ||
|
||
if (!config.autoDeleteReminders) { | ||
actionsComponent = Text.join( | ||
" ", | ||
"§a✔".asComponent { | ||
hover = "§7Click to dismiss".asComponent() | ||
command = "/shremind remove $id" | ||
}.wrap("§8[", "§8]"), | ||
" ", | ||
"§e§l⟳".asComponent { | ||
hover = "§7Click to move".asComponent() | ||
suggest = "/shremind move $id 1m" | ||
}.wrap("§8[", "§8]"), | ||
) | ||
} else { | ||
storage.remove(id) | ||
} | ||
|
||
remindersToSend.add( | ||
Text.join( | ||
"§e[Reminder]".asComponent { | ||
hover = "§7Reminders by SkyHanni".asComponent() | ||
}, | ||
actionsComponent, | ||
" ", | ||
"§6${reminder.reason}", | ||
), | ||
) | ||
} | ||
|
||
if (remindersToSend.isNotEmpty()) { | ||
val id = if (config.autoDeleteReminders) 0 else REMINDERS_MESSAGE_ID | ||
Text.join(remindersToSend, separator = Text.NEWLINE).send(id) | ||
} | ||
} | ||
|
||
fun command(args: Array<String>) = when (args.firstOrNull()) { | ||
"list" -> listReminders(args.drop(1).firstOrNull()?.toIntOrNull() ?: 1) | ||
"remove", "delete" -> removeReminder(args.drop(1)) | ||
"edit", "update" -> editReminder(args.drop(1)) | ||
"move" -> moveReminder(args.drop(1)) | ||
"help" -> help() | ||
else -> createReminder(args) | ||
} | ||
} |