diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 7186f7e..77be960 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -173,6 +173,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro event?.let { with(event.getBot()) { LinksMgr.twitter.notification("$nick disconnected from irc://$serverHostname") + seen.add(userChannelDao.getChannel(channel).users) } } LinksMgr.twitter.shutdown() @@ -197,8 +198,10 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot()) { if (user.nick == nick) { LinksMgr.twitter.notification("$nick has joined ${event.channel.name} on irc://$serverHostname") + seen.add(userChannelDao.getChannel(channel).users) } else { tell.send(event) + seen.add(user.nick) } } } @@ -228,7 +231,13 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro } override fun onNickChange(event: NickChangeEvent?) { - event?.let { tell.send(event) } + event?.let { + tell.send(event) + if (!it.oldNick.equals(it.newNick, true)) { + seen.add(it.oldNick) + } + seen.add(it.newNick) + } } override fun onPart(event: PartEvent?) { @@ -236,6 +245,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot()) { if (user.nick == nick) { LinksMgr.twitter.notification("$nick has left ${event.channel.name} on irc://$serverHostname") + seen.add(userChannelDao.getChannel(channel).users) } else { seen.add(user.nick) } @@ -424,10 +434,10 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(Ping()) addons.add(RockPaperScissors()) addons.add(StockQuote()) - addons.add(Weather2()) - addons.add(WorldTime()) addons.add(War()) + addons.add(Weather2()) addons.add(WolframAlpha()) + addons.add(WorldTime()) // Sort the addons addons.names.sort() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 0812339..511afb1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -214,7 +214,7 @@ object Utils { } catch (e: IOException) { logger.error("An IO error occurred loading the ${description}.", e) } catch (e: ClassNotFoundException) { - logger.error("An error occurred loading the {$description}.", e) + logger.error("An error occurred loading the ${description}.", e) } } return default diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt new file mode 100644 index 0000000..8e251b0 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -0,0 +1,44 @@ +/* + * SeenComparator.kt + * + * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.seen + +import java.io.Serializable + +class NickComparator: Comparator, Serializable { + override fun compare(a: String, b: String): Int { + return a.lowercase().compareTo(b.lowercase()) + } + companion object { + private const val serialVersionUID = 1L + } +} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index cb02cad..de7f70b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -32,6 +32,7 @@ package net.thauvin.erik.mobibot.commands.seen +import com.google.common.collect.ImmutableSortedSet import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.loadData @@ -39,13 +40,16 @@ import net.thauvin.erik.mobibot.Utils.saveData import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime +import org.pircbotx.User import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory +import java.util.TreeMap + class Seen(private val serialObject: String) : AbstractCommand() { private val logger: Logger = LoggerFactory.getLogger(Seen::class.java) - val seenNicks: MutableList = mutableListOf() + val seenNicks = TreeMap(NickComparator()) override val name = "seen" override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name ")) @@ -53,6 +57,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { override val isPublic = true override val isVisible = true + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isEnabled()) { if (args.isNotBlank() && !args.contains(' ')) { @@ -63,12 +68,11 @@ class Seen(private val serialObject: String) : AbstractCommand() { return } } - seenNicks.forEach { - if (it.nick.equals(args, true)) { - val lastSeen = System.currentTimeMillis() - it.last - event.sendMessage("${it.nick} was last seen on $channel ${lastSeen.toUptime()} ago.") - return - } + if (seenNicks.containsKey(args)) { + val seenNick = seenNicks.getValue(args) + val lastSeen = System.currentTimeMillis() - seenNick.lastSeen + event.sendMessage("${seenNick.nick} was last seen on $channel ${lastSeen.toUptime()} ago.") + return } event.sendMessage("I haven't seen $args on $channel lately.") } else { @@ -79,15 +83,16 @@ class Seen(private val serialObject: String) : AbstractCommand() { fun add(nick: String) { if (isEnabled()) { - seenNicks.forEach { - if (it.nick.equals(nick, true)) { - if (it.nick != nick) it.nick = nick - it.last = System.currentTimeMillis() - save() - return - } + seenNicks[nick] = SeenNick(nick, System.currentTimeMillis()) + save() + } + } + + fun add(users: ImmutableSortedSet) { + if (isEnabled()) { + users.forEach { + seenNicks[it.nick] = SeenNick(it.nick, System.currentTimeMillis()) } - seenNicks.add(SeenNick(nick)) save() } } @@ -99,12 +104,14 @@ class Seen(private val serialObject: String) : AbstractCommand() { fun load() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") - seenNicks += loadData( - serialObject, - mutableListOf(), - logger, - "seen nicknames" - ) as MutableList + seenNicks.putAll( + loadData( + serialObject, + TreeMap(), + logger, + "seen nicknames" + ) as TreeMap + ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 0211dcc..b53414d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -34,7 +34,7 @@ package net.thauvin.erik.mobibot.commands.seen import java.io.Serializable -data class SeenNick(var nick: String, var last: Long = System.currentTimeMillis()) : Serializable { +data class SeenNick(val nick: String, val lastSeen: Long) : Serializable { companion object { private const val serialVersionUID = 1L } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 3e28f32..59fa2b7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -35,6 +35,7 @@ package net.thauvin.erik.mobibot.commands.seen import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isGreaterThan +import assertk.assertions.isNotEqualTo import assertk.assertions.isTrue import org.testng.annotations.AfterClass import org.testng.annotations.BeforeClass @@ -45,6 +46,7 @@ import kotlin.io.path.fileSize class SeenTest { private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") private val seen = Seen(tmpFile.toAbsolutePath().toString()) + private val nick = "ErikT" @BeforeClass fun saveTest() { @@ -57,12 +59,21 @@ class SeenTest { tmpFile.deleteIfExists() } - @Test + @Test(priority = 1) fun loadTest() { - val nick = seen.seenNicks[0] seen.clear() + assertThat(seen.seenNicks.isEmpty(), "nicknames map is not empty").isTrue() seen.load() - assertThat(seen.seenNicks[0] == nick, "nick is different").isTrue() + assertThat(seen.seenNicks.containsKey(nick), "nick is missing").isTrue() + } + + @Test + fun addTest() { + val last = seen.seenNicks[nick]?.lastSeen + seen.add(nick.lowercase()) + assertThat(seen.seenNicks.size, "nick is duplicated").isEqualTo(1) + assertThat(seen.seenNicks[nick]?.lastSeen, "last seen is not different").isNotEqualTo(last) + assertThat(seen.seenNicks[nick]?.nick, "nick is not lowercase").isEqualTo(nick.lowercase()) } @Test(priority = 10) diff --git a/version.properties b/version.properties index 70f832b..e4dfa98 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Sep 16 00:01:57 PDT 2022 -version.buildmeta=525 +#Fri Sep 16 10:29:48 PDT 2022 +version.buildmeta=542 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+525 +version.semver=0.8.0-rc+542 diff --git a/website/index.html b/website/index.html index 6904a5f..8b9831f 100644 --- a/website/index.html +++ b/website/index.html @@ -100,7 +100,7 @@
mobibot: time UK
mobibot: time GMT
-
  • Sending messages to people on join/activity: +
  • Sending messages to people on join/activity
    mobibot: tell nickname Give me a call when you see this.
  • Recapping public channel messages @@ -109,6 +109,9 @@
  • Listing the users on the channel
    /msg mobibot users
  • +
  • Viewing when a nickname was last seen +
    /msg mobibot seen nickname
    +
  • Random jokes from The Internet Chuck Norris Database
    mobibot: joke