mirror of
https://github.com/Eyre-S/Coeur-Morny-Cono.git
synced 2024-11-23 11:37:38 +08:00
Compare commits
No commits in common. "b305f52493f9f2475d61a6cd1654e2e7b0749df0" and "0898d08fa7287be7383a4e6017f8d9ba26a127d8" have entirely different histories.
b305f52493
...
0898d08fa7
@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur
|
|||||||
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
||||||
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
|
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
|
||||||
|
|
||||||
VERSION = 1.1.1
|
VERSION = 1.1.1-alpha2
|
||||||
|
|
||||||
USE_DELTA = false
|
USE_DELTA = false
|
||||||
VERSION_DELTA =
|
VERSION_DELTA =
|
||||||
|
@ -4,7 +4,7 @@ import cc.sukazyo.cono.morny.MornyCoeur
|
|||||||
import cc.sukazyo.cono.morny.bot.api.EventListener
|
import cc.sukazyo.cono.morny.bot.api.EventListener
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||||
import com.pengrad.telegrambot.model.Update
|
import com.pengrad.telegrambot.model.Update
|
||||||
import com.pengrad.telegrambot.request.SendMessage
|
import com.pengrad.telegrambot.request.{AbstractSendRequest, SendMessage}
|
||||||
import com.pengrad.telegrambot.response.SendResponse
|
import com.pengrad.telegrambot.response.SendResponse
|
||||||
|
|
||||||
import scala.language.postfixOps
|
import scala.language.postfixOps
|
||||||
@ -33,7 +33,6 @@ class OnUserRandom (using coeur: MornyCoeur) {
|
|||||||
(if rand_half then "不" else "") + _con
|
(if rand_half then "不" else "") + _con
|
||||||
case _ => null
|
case _ => null
|
||||||
|
|
||||||
//noinspection DuplicatedCode
|
|
||||||
if result == null then return false
|
if result == null then return false
|
||||||
|
|
||||||
coeur.account exec SendMessage(
|
coeur.account exec SendMessage(
|
||||||
@ -48,24 +47,18 @@ class OnUserRandom (using coeur: MornyCoeur) {
|
|||||||
//noinspection NonAsciiCharacters
|
//noinspection NonAsciiCharacters
|
||||||
object 尊嘟假嘟 extends EventListener {
|
object 尊嘟假嘟 extends EventListener {
|
||||||
|
|
||||||
private val word_pattern = "^([\\w\\W]*)?(?:尊嘟假嘟|(?:O\\.o|o\\.O))$"r
|
|
||||||
private val keywords = Array("尊嘟假嘟", "O.o", "o.O")
|
private val keywords = Array("尊嘟假嘟", "O.o", "o.O")
|
||||||
|
|
||||||
override def onMessage (using event: Update): Boolean = {
|
override def onMessage (using event: Update): Boolean = {
|
||||||
|
|
||||||
if event.message.text == null then return false
|
if event.message.text == null then return false
|
||||||
|
|
||||||
var result: String|Null = null
|
if !(keywords contains event.message.text) then return false
|
||||||
import cc.sukazyo.cono.morny.util.UseRandom.rand_half
|
|
||||||
for (k <- keywords)
|
|
||||||
if event.message.text endsWith k then
|
|
||||||
result = if rand_half then "尊嘟" else "假嘟"
|
|
||||||
//noinspection DuplicatedCode
|
|
||||||
if result == null then return false
|
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.util.UseRandom.rand_half
|
||||||
coeur.account exec SendMessage(
|
coeur.account exec SendMessage(
|
||||||
event.message.chat.id,
|
event.message.chat.id,
|
||||||
result
|
if rand_half then "尊嘟" else "假嘟"
|
||||||
).replyToMessageId(event.message.messageId)
|
).replyToMessageId(event.message.messageId)
|
||||||
true
|
true
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
|
|||||||
import cc.sukazyo.cono.morny.MornyCoeur
|
import cc.sukazyo.cono.morny.MornyCoeur
|
||||||
import cc.sukazyo.cono.morny.daemon.MedicationTimer.calcNextRoutineTimestamp
|
import cc.sukazyo.cono.morny.daemon.MedicationTimer.calcNextRoutineTimestamp
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||||
import cc.sukazyo.cono.morny.util.CommonFormat
|
|
||||||
import com.pengrad.telegrambot.model.{Message, MessageEntity}
|
import com.pengrad.telegrambot.model.{Message, MessageEntity}
|
||||||
import com.pengrad.telegrambot.request.{EditMessageText, SendMessage}
|
import com.pengrad.telegrambot.request.{EditMessageText, SendMessage}
|
||||||
import com.pengrad.telegrambot.response.SendResponse
|
import com.pengrad.telegrambot.response.SendResponse
|
||||||
@ -37,13 +36,8 @@ class MedicationTimer (using coeur: MornyCoeur) extends Thread {
|
|||||||
logger info "Medication Timer started."
|
logger info "Medication Timer started."
|
||||||
while (!this.isInterrupted) {
|
while (!this.isInterrupted) {
|
||||||
try {
|
try {
|
||||||
val next_time = calcNextRoutineTimestamp(System.currentTimeMillis, use_timeZone, notify_atHour)
|
waitToNextRoutine()
|
||||||
logger info s"medication timer will send next notify at ${CommonFormat.formatDate(next_time, use_timeZone.getTotalSeconds/60/60)} with $use_timeZone [$next_time]"
|
|
||||||
val sleep_millis = next_time - System.currentTimeMillis
|
|
||||||
logger debug s"medication timer will sleep ${CommonFormat.formatDuration(sleep_millis)} [$sleep_millis]"
|
|
||||||
Thread sleep sleep_millis
|
|
||||||
sendNotification()
|
sendNotification()
|
||||||
logger info "medication notify sent."
|
|
||||||
} catch
|
} catch
|
||||||
case _: InterruptedException =>
|
case _: InterruptedException =>
|
||||||
interrupt()
|
interrupt()
|
||||||
@ -68,6 +62,11 @@ class MedicationTimer (using coeur: MornyCoeur) extends Thread {
|
|||||||
else lastNotify_messageId = None
|
else lastNotify_messageId = None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@throws[InterruptedException | IllegalArgumentException]
|
||||||
|
private def waitToNextRoutine (): Unit = {
|
||||||
|
Thread sleep calcNextRoutineTimestamp(System.currentTimeMillis, use_timeZone, notify_atHour)
|
||||||
|
}
|
||||||
|
|
||||||
def refreshNotificationWrite (edited: Message): Unit = {
|
def refreshNotificationWrite (edited: Message): Unit = {
|
||||||
if (lastNotify_messageId isEmpty) || (lastNotify_messageId.get != (edited.messageId toInt)) then return
|
if (lastNotify_messageId isEmpty) || (lastNotify_messageId.get != (edited.messageId toInt)) then return
|
||||||
import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
|
import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
|
||||||
|
@ -10,27 +10,21 @@ class MornyDaemons (using val coeur: MornyCoeur) {
|
|||||||
val eventHack: EventHacker = EventHacker()
|
val eventHack: EventHacker = EventHacker()
|
||||||
|
|
||||||
def start (): Unit = {
|
def start (): Unit = {
|
||||||
|
|
||||||
logger info "ALL Morny Daemons starting..."
|
logger info "ALL Morny Daemons starting..."
|
||||||
|
|
||||||
// TrackerDataManager.init();
|
// TrackerDataManager.init();
|
||||||
medicationTimer.start()
|
medicationTimer.start()
|
||||||
|
|
||||||
logger info "Morny Daemons started."
|
logger info "Morny Daemons started."
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def stop (): Unit = {
|
def stop (): Unit = {
|
||||||
|
|
||||||
logger.info("stopping All Morny Daemons...")
|
logger.info("stopping All Morny Daemons...")
|
||||||
|
|
||||||
// TrackerDataManager.DAEMON.interrupt();
|
// TrackerDataManager.DAEMON.interrupt();
|
||||||
medicationTimer.interrupt()
|
medicationTimer.interrupt()
|
||||||
// TrackerDataManager.trackingLock.lock();
|
// TrackerDataManager.trackingLock.lock();
|
||||||
try { medicationTimer.join() }
|
try { medicationTimer.join() }
|
||||||
catch case e: InterruptedException =>
|
catch case e: InterruptedException =>
|
||||||
e.printStackTrace(System.out)
|
e.printStackTrace(System.out)
|
||||||
|
|
||||||
logger.info("stopped ALL Morny Daemons.")
|
logger.info("stopped ALL Morny Daemons.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package cc.sukazyo.cono.morny.util
|
package cc.sukazyo.cono.morny.util
|
||||||
|
|
||||||
import cc.sukazyo.cono.morny.util.EpochDateTime.{DurationMillis, EpochMillis}
|
|
||||||
|
|
||||||
import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}
|
import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
@ -42,24 +40,11 @@ object CommonFormat {
|
|||||||
*
|
*
|
||||||
* @return the time-zone local date-time-millis [[String]] describes the timestamp.
|
* @return the time-zone local date-time-millis [[String]] describes the timestamp.
|
||||||
*/
|
*/
|
||||||
def formatDate (timestamp: EpochMillis, utcOffset: Int): String =
|
def formatDate (timestamp: Long, utcOffset: Int): String =
|
||||||
formatDate(timestamp, ZoneOffset.ofHours(utcOffset))
|
|
||||||
|
|
||||||
/** the formatted date-time-millis [[String]].
|
|
||||||
*
|
|
||||||
* time is formatted by pattern [[DATE_TIME_PATTERN_FULL_MILLIS]].
|
|
||||||
*
|
|
||||||
* @param timestamp millis timestamp. timestamp should be UTC alignment.
|
|
||||||
*
|
|
||||||
* @param tz the time-zone controls which local time describe will use.
|
|
||||||
*
|
|
||||||
* @return the time-zone local date-time-millis [[String]] describes the timestamp.
|
|
||||||
*/
|
|
||||||
def formatDate (timestamp: EpochMillis, tz: ZoneOffset): String =
|
|
||||||
DateTimeFormatter.ofPattern(DATE_TIME_PATTERN_FULL_MILLIS).format(
|
DateTimeFormatter.ofPattern(DATE_TIME_PATTERN_FULL_MILLIS).format(
|
||||||
LocalDateTime.ofInstant(
|
LocalDateTime.ofInstant(
|
||||||
Instant.ofEpochMilli(timestamp),
|
Instant.ofEpochMilli(timestamp),
|
||||||
ZoneId.ofOffset("UTC", tz)
|
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(utcOffset))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -79,7 +64,7 @@ object CommonFormat {
|
|||||||
* @param duration time duration, in milliseconds
|
* @param duration time duration, in milliseconds
|
||||||
* @return time duration, human readable
|
* @return time duration, human readable
|
||||||
*/
|
*/
|
||||||
def formatDuration (duration: DurationMillis): String =
|
def formatDuration (duration: Long): String =
|
||||||
val sb = new StringBuilder()
|
val sb = new StringBuilder()
|
||||||
if (duration > 1000 * 60 * 60 * 24) sb ++= (duration / (1000 * 60 * 60 * 24)).toString ++= "d "
|
if (duration > 1000 * 60 * 60 * 24) sb ++= (duration / (1000 * 60 * 60 * 24)).toString ++= "d "
|
||||||
if (duration > 1000 * 60 * 60) sb ++= (duration / (1000 * 60 * 60) % 24).toString ++= "h "
|
if (duration > 1000 * 60 * 60) sb ++= (duration / (1000 * 60 * 60) % 24).toString ++= "h "
|
||||||
|
@ -6,7 +6,6 @@ import java.time.format.DateTimeFormatter
|
|||||||
object EpochDateTime {
|
object EpochDateTime {
|
||||||
|
|
||||||
type EpochMillis = Long
|
type EpochMillis = Long
|
||||||
type DurationMillis = Long
|
|
||||||
|
|
||||||
object EpochMillis:
|
object EpochMillis:
|
||||||
/** convert a localtime with timezone to epoch milliseconds
|
/** convert a localtime with timezone to epoch milliseconds
|
||||||
|
@ -7,84 +7,26 @@ class CommonEncryptTest extends MornyTests with TableDrivenPropertyChecks {
|
|||||||
|
|
||||||
"while doing hash :" - {
|
"while doing hash :" - {
|
||||||
|
|
||||||
import cc.sukazyo.cono.morny.util.CommonEncrypt.{MD5, SHA1, SHA256, SHA512}
|
|
||||||
import cc.sukazyo.cono.morny.util.ConvertByteHex.toHex
|
|
||||||
|
|
||||||
val examples = Table(
|
val examples = Table(
|
||||||
(
|
("md5" , "text"),
|
||||||
"text",
|
("28be57d368b75051da76c068a6733284", "莲子"),
|
||||||
"md5",
|
("9644c5cbae223013228cd528817ba4f5", "莲子\n"),
|
||||||
"sha1",
|
("d41d8cd98f00b204e9800998ecf8427e", "")
|
||||||
"sha256",
|
|
||||||
"sha512"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"莲子",
|
|
||||||
"28be57d368b75051da76c068a6733284",
|
|
||||||
"556f7cadfcbffdcbf41b4aa1843528b4406e62e6",
|
|
||||||
"f7d6f172cb4a8d1a8f7513ee94c7b6bbf1cf5c8c643182ebbb1b9ab472704e9b",
|
|
||||||
"4be6cf5dc44582d913d4d716ef4528f04e08d94a45eee0f27395003d3c83be535d5b9a40f510625bc6fee3481f6a1de600057ceff6488c5953f6172641f4768d"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"莲子\n",
|
|
||||||
"9644c5cbae223013228cd528817ba4f5",
|
|
||||||
"86329fc40e4bab2c410e35ddbec7ab8a7b6574d6",
|
|
||||||
"3372ca6821832bebd681c705862c01d137cba9cf288f95465ee7876affc90ba0",
|
|
||||||
"bedf1c61330c0f945fa4f84aaccf2778b5c77926916689e8b8e4c3d1d567dc8b9d91643ff3451365e4fd04f789ca229e0b2ca61dd4976ad6f866f5600617430c"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"",
|
|
||||||
"d41d8cd98f00b204e9800998ecf8427e",
|
|
||||||
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
|
||||||
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
||||||
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
forAll (examples) { (text, md5, sha1, sha256, sha512) =>
|
import cc.sukazyo.cono.morny.util.CommonEncrypt.MD5
|
||||||
|
import cc.sukazyo.cono.morny.util.ConvertByteHex.toHex
|
||||||
|
forAll (examples) { (md5, text) =>
|
||||||
s"while hashing text \"$text\" :" - {
|
s"while hashing text \"$text\" :" - {
|
||||||
|
|
||||||
s"the MD5 value should be $md5" in { MD5(text).toHex shouldEqual md5 }
|
s"the MD5 value should be $md5" in { MD5(text).toHex shouldEqual md5 }
|
||||||
s"the SHA1 should be $sha1" in { SHA1(text).toHex shouldEqual sha1 }
|
|
||||||
s"the SHA256 should be $sha256" in { SHA256(text).toHex shouldEqual sha256 }
|
"other algorithms" in pending
|
||||||
s"the SHA512 should be $sha512" in { SHA512(text).toHex shouldEqual sha512 }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: binary file source
|
s"while hashing binary file $pending_val" in pending
|
||||||
case class ExampleHashValue (
|
|
||||||
md5: String,
|
|
||||||
sha1: String,
|
|
||||||
sha256: String,
|
|
||||||
sha512: String
|
|
||||||
)
|
|
||||||
val examples_binary = Table[String|Null, ExampleHashValue](
|
|
||||||
("file", "hashes"),
|
|
||||||
(
|
|
||||||
null, ExampleHashValue(
|
|
||||||
"d41d8cd98f00b204e9800998ecf8427e",
|
|
||||||
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
|
||||||
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
||||||
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
forAll(examples_binary) { (file, hashes) =>
|
|
||||||
val _name = if file == null then "empty file" else s"file $file"
|
|
||||||
val _data = if file == null then Array.empty[Byte] else throw NotImplementedError("does not applied file get yet")
|
|
||||||
|
|
||||||
s"while hashing binary $_name :" - {
|
|
||||||
|
|
||||||
s"the MD5 value should be ${hashes.md5}" in { MD5(_data).toHex shouldEqual hashes.md5 }
|
|
||||||
s"the SHA1 should be $hashes.sha1" in { SHA1(_data).toHex shouldEqual hashes.sha1 }
|
|
||||||
s"the SHA256 should be $hashes.sha256" in { SHA256(_data).toHex shouldEqual hashes.sha256 }
|
|
||||||
s"the SHA512 should be $hashes.sha512" in { SHA512(_data).toHex shouldEqual hashes.sha512 }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,70 +1,19 @@
|
|||||||
package cc.sukazyo.cono.morny.test.utils.tgapi
|
package cc.sukazyo.cono.morny.test.utils.tgapi
|
||||||
|
|
||||||
import cc.sukazyo.cono.morny.test.MornyTests
|
import cc.sukazyo.cono.morny.test.MornyTests
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
|
||||||
import org.scalatest.prop.TableDrivenPropertyChecks
|
|
||||||
|
|
||||||
class InputCommandTest extends MornyTests with TableDrivenPropertyChecks {
|
class InputCommandTest extends MornyTests {
|
||||||
|
|
||||||
"while create new InputCommand :" - {
|
"while create new InputCommand :" - {
|
||||||
|
|
||||||
val examples = Table[String|Array[String], String, String|Null, Array[String]](
|
s"while input is $pending_val:" - {
|
||||||
(
|
|
||||||
"source",
|
|
||||||
"command",
|
|
||||||
"target",
|
|
||||||
"args"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"/exit@sukazyo_deving_bot",
|
|
||||||
"/exit",
|
|
||||||
"sukazyo_deving_bot",
|
|
||||||
Array.empty[String]
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"/test@a@b",
|
|
||||||
"/test",
|
|
||||||
"a@b",
|
|
||||||
Array.empty[String]
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"/test-data@random#user",
|
|
||||||
"/test-data",
|
|
||||||
"random#user",
|
|
||||||
Array.empty[String]
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"/info@sukazyo_deving_bot stickers.ID_403",
|
|
||||||
"/info",
|
|
||||||
"sukazyo_deving_bot",
|
|
||||||
Array("stickers.ID_403")
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"/info some extra info",
|
|
||||||
"/info",
|
|
||||||
null,
|
|
||||||
Array("some", "extra", "info")
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
examples forEvery { (source, command, target, args) =>
|
s"command should be $pending_val" in pending
|
||||||
|
s"target should be $pending_val" in pending
|
||||||
|
|
||||||
val _source_describe = source match
|
"args array should always exists" in pending
|
||||||
case s: String => s
|
|
||||||
case r: Array[String] => r.mkString
|
|
||||||
s"while input is $_source_describe:" - {
|
|
||||||
|
|
||||||
val _ic: InputCommand = source match
|
s"args should parsed to array $pending_val" in pending
|
||||||
case s: String => InputCommand(s)
|
|
||||||
case r: Array[String] => InputCommand(r)
|
|
||||||
|
|
||||||
s"command should be '$command'" in { _ic.command shouldEqual command }
|
|
||||||
s"target should be '$target'" in {_ic.target shouldEqual target}
|
|
||||||
|
|
||||||
"args array should always exists" in { _ic.args shouldNot equal (null) }
|
|
||||||
s"args should parsed to array ${args.mkString}" in { _ic.args shouldEqual args }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user