This commit is contained in:
Erik C. Thauvin 2020-12-08 01:40:09 -08:00
parent e01de36909
commit 629e26b292
25 changed files with 75 additions and 103 deletions

View file

@ -12,6 +12,7 @@
<ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID>
<ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID>
<ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID>
<ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List&lt;String&gt;, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID>
<ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID>
<ID>MagicNumber:AddLog.kt$AddLog$4</ID>
<ID>MagicNumber:Comment.kt$Comment$3</ID>

View file

@ -69,8 +69,8 @@ class Addons {
*/
fun add(command: AbstractCommand, props: Properties) {
with(command) {
if (hasProperties()) {
getPropertyKeys().forEach {
if (properties.isNotEmpty()) {
properties.keys.forEach {
setProperty(it, props.getProperty(it, ""))
}
}
@ -111,13 +111,14 @@ class Addons {
/**
* Match a command.
*/
fun match(sender: String, login: String, message: String, isOp: Boolean, isPrivate: Boolean) {
fun match(sender: String, login: String, message: String, isOp: Boolean, isPrivate: Boolean): Boolean {
for (command in commands) {
if (command.matches(message)) {
command.commandResponse(sender, login, message, isOp, isPrivate)
break
return true
}
}
return false
}
/**

View file

@ -397,12 +397,10 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
sleep(3)
quitServer("The Bot Is Out There!")
exitProcess(0)
} else {
if (!addons.exec(sender, login, cmd, args, isOp, true)) {
} else if (!addons.exec(sender, login, cmd, args, isOp, true)) { // Execute command or module
helpDefault(sender, isOp, true)
}
}
}
override fun onAction(sender: String, login: String, hostname: String, target: String, action: String) {
if (channel == target) {
@ -463,13 +461,18 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
maxPerLine: Int,
isPrivate: Boolean,
isBold: Boolean = false,
isIndent: Boolean = false while (i < list.size) {
isIndent: Boolean = false
) {
var i = 0
while (i < list.size) {
send(
nick,
helpFormat(
list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(" ", truncated = ""),
isBold,
isIndent isPrivate
isIndent
),
isPrivate
)
i += maxPerLine
}

View file

@ -38,6 +38,7 @@ import java.io.BufferedReader
import java.io.File
import java.io.IOException
import java.io.InputStreamReader
import java.lang.NumberFormatException
import java.net.URL
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
@ -60,12 +61,28 @@ object Utils {
@JvmStatic
fun bold(i: Int): String = bold(i.toString())
/**
* Makes the given long bold.
*/
@JvmStatic
fun bold(i: Long): String = bold(i.toString())
/**
* Makes the given string bold.
*/
@JvmStatic
fun bold(s: String?): String = colorize(s, Colors.BOLD)
/**
* Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's
* nick.
*/
@JvmStatic
fun buildCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String {
val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick)
return StringUtils.replaceEach(text, searchFlags, replace)
}
/**
* Colorize a string.
*/
@ -89,6 +106,7 @@ object Utils {
/**
* URL encodes the given string.
*/
@JvmStatic
fun encodeUrl(s: String): String = URLEncoder.encode(s, StandardCharsets.UTF_8)
/**
@ -120,14 +138,10 @@ object Utils {
*/
@JvmStatic
fun getIntProperty(property: String?, def: Int): Int {
return if (property == null) {
return try {
property?.toInt() ?: def
} catch (nfe: NumberFormatException) {
def
} else {
try {
property.toInt()
} catch (ignore: NumberFormatException) {
def
}
}
}
@ -170,6 +184,12 @@ object Utils {
} else s
}
/**
* Returns the plural form of a word, if count &gt; 1.
*/
@JvmStatic
fun plural(count: Int, word: String, plural: String): String = plural(count.toLong(), word, plural)
/**
* Returns the plural form of a word, if count &gt; 1.
*/
@ -269,10 +289,6 @@ object Utils {
*/
@JvmStatic
fun utcDateTime(date: LocalDateTime?): String {
return if (date != null) {
date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
} else {
""
}
return date?.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) ?: ""
}
}

View file

@ -43,7 +43,7 @@ abstract class AbstractCommand(val bot: Mobibot) {
abstract val isPublic: Boolean
abstract val isVisible: Boolean
private val properties: MutableMap<String, String> = ConcurrentHashMap()
val properties: MutableMap<String, String> = ConcurrentHashMap()
abstract fun commandResponse(
sender: String,
@ -63,18 +63,6 @@ abstract class AbstractCommand(val bot: Mobibot) {
return false
}
open fun getProperty(key: String): String? {
return properties[key]
}
open fun getPropertyKeys(): Set<String> {
return properties.keys
}
open fun hasProperties(): Boolean {
return properties.isNotEmpty()
}
open fun initProperties(vararg keys: String) {
keys.forEach {
properties[it] = ""

View file

@ -40,7 +40,7 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) {
override val name = channel
override val help = listOf(
"To list the last 5 posts from the channel's weblog feed:",
Utils.helpIndent("%c $channel")
Utils.helpFormat("%c $channel")
)
override val isOp = false
override val isPublic = true
@ -61,7 +61,7 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) {
isOp: Boolean,
isPrivate: Boolean
) {
with(getProperty(FEED_PROP)) {
with(properties[FEED_PROP]) {
if (!isNullOrBlank()) {
Thread(FeedReader(bot, sender, this)).start()
} else {

View file

@ -32,12 +32,13 @@
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Constants
import net.thauvin.erik.mobibot.Mobibot
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.config.Configurator
class Debug(bot: Mobibot) : AbstractCommand(bot) {
override val name = "debug"
override val name = Constants.DEBUG_CMD
override val help = emptyList<String>()
override val isOp = true
override val isPublic = false

View file

@ -35,7 +35,6 @@ package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import net.thauvin.erik.mobibot.commands.links.LinksMgr
import java.util.*
class Ignore(bot: Mobibot) : AbstractCommand(bot) {
private val me = "me"
@ -67,7 +66,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) {
companion object {
const val IGNORE_CMD = "ignore"
const val IGNORE_PROP = IGNORE_CMD
private val ignored = TreeSet<String>()
private val ignored = mutableSetOf<String>()
@JvmStatic
fun isNotIgnored(nick: String): Boolean {
@ -150,7 +149,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) {
override fun setProperty(key: String, value: String) {
super.setProperty(key, value)
if (IGNORE_PROP == key) {
ignored.addAll(value.split(LinksMgr.LINK_MATCH.toRegex()))
ignored.addAll(value.split(LinksMgr.TAG_MATCH.toRegex()))
}
}

View file

@ -43,7 +43,7 @@ import java.lang.management.ManagementFactory;
import java.util.List;
public class Info extends AbstractCommand {
private final List<String> version = List.of(
private final List<String> allVersions = List.of(
StringUtils.capitalize(ReleaseInfo.PROJECT) + " " + ReleaseInfo.VERSION
+ " (" + Utils.green(ReleaseInfo.WEBSITE) + ')',
"Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')');
@ -102,7 +102,7 @@ public class Info extends AbstractCommand {
}
}
info.append(", Recap: ").append(Recap.recapCount()).append(']');
info.append(", Recap: ").append(Recap.recaps.size()).append(']');
getBot().send(sender, info.toString(), isPrivate);
}

View file

@ -48,19 +48,11 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) {
override val isVisible = true
companion object {
private val recaps = mutableListOf<String>()
@JvmStatic
fun recapCount(): Int {
return recaps.size
}
@JvmField
val recaps = mutableListOf<String>()
/**
* Stores the last 10 public messages and actions.
*
* @param sender The nick of the person who sent the private message.
* @param message The actual message sent.
* @param isAction Set to `true` if the message is an action.
*/
@JvmStatic
fun storeRecap(sender: String, message: String, isAction: Boolean) {

View file

@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import org.jibble.pircbot.User
class Users(bot: Mobibot) : AbstractCommand(bot) {
override val name = "users"
@ -54,9 +53,8 @@ class Users(bot: Mobibot) : AbstractCommand(bot) {
isOp: Boolean,
isPrivate: Boolean
) {
val users: Array<User> = bot.getUsers(bot.channel)
val nicks = mutableListOf<String>()
users.forEach { user ->
bot.getUsers(bot.channel).forEach { user ->
if (bot.isOp(user.nick)) {
nicks.add("@${user.nick}")
} else {

View file

@ -89,7 +89,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
fun startup(current: String, backlogs: String, channel: String) {
startDate = EntriesMgr.loadEntries(current, channel, entries)
if (Utils.today() != startDate) {
this.entries.clear()
entries.clear()
startDate = Utils.today()
}
EntriesMgr.loadBacklogs(backlogs, history)

View file

@ -148,10 +148,10 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) {
private fun showEntry(index: Int) {
val entry: EntryLink = entries[index]
bot.send(EntriesUtils.buildLink(index, entry))
if (entry.hasTags()) {
if (entry.tags.isNotEmpty()) {
bot.send(EntriesUtils.buildTags(index, entry))
}
if (entry.hasComments()) {
if (entry.comments.isNotEmpty()) {
val comments = entry.comments
for (i in comments.indices) {
bot.send(EntriesUtils.buildComment(index, i, comments[i]))

View file

@ -76,7 +76,7 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) {
bot.send(sender, "Please ask a channel op to change the tags for you.", isPrivate)
}
} else {
if (entry.hasTags()) {
if (entry.tags.isNotEmpty()) {
bot.send(EntriesUtils.buildTags(index, entry))
} else {
bot.send(sender, "The entry has no tags. Why don't add some?", isPrivate)

View file

@ -35,7 +35,7 @@ import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils.bold
import net.thauvin.erik.mobibot.Utils.getIntProperty
import net.thauvin.erik.mobibot.Utils.helpFormat
import net.thauvin.erik.mobibot.Utils.helpIndent
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
import net.thauvin.erik.mobibot.Utils.plural
import net.thauvin.erik.mobibot.Utils.reverseColor
import net.thauvin.erik.mobibot.Utils.utcDateTime
@ -63,7 +63,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
*/
private fun clean(): Boolean {
if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.")
return TellMessagesMgr.clean(messages, maxDays)
return TellMessagesMgr.clean(messages, maxDays.toLong())
}
// Delete message.
@ -88,7 +88,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
} else {
var found = false
for (message in messages) {
found = message.isMatchId(id)
found = (message.id == id)
if (found && (message.sender.equals(sender, ignoreCase = true) || bot.isOp(sender))) {
messages.remove(message)
save()
@ -122,7 +122,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
"To view queued and sent messages:",
helpFormat("%c $name ${View.VIEW_CMD}"),
"Messages are kept for " + bold(maxDays)
+ plural(maxDays.toLong(), " day.", " days.")
+ plural(maxDays, " day.", " days.")
)
override val isOp: Boolean
get() = false
@ -313,7 +313,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
)
bot.send(
sender,
"Messages are kept for ${bold(maxDays)}${plural(maxDays.toLong(), " day.", " days.")}",
"Messages are kept for ${bold(maxDays)}${plural(maxDays, " day.", " days.")}",
isPrivate
)
}

View file

@ -86,7 +86,6 @@ class TellMessage internal constructor(
*/
var receptionDate: LocalDateTime = LocalDateTime.MIN
/**
* Matches the message sender or recipient.
*/
@ -94,13 +93,6 @@ class TellMessage internal constructor(
return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true)
}
/**
* Match the message ID.
*/
fun isMatchId(id: String): Boolean {
return this.id == id
}
override fun toString(): String {
return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " +
"queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}")

View file

@ -46,13 +46,13 @@ import java.time.LocalDateTime
/**
* The Tell Messages Manager.
*/
internal object TellMessagesMgr {
object TellMessagesMgr {
/**
* Cleans the messages queue.
*/
fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Int): Boolean {
fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Long): Boolean {
val today = LocalDateTime.now(Clock.systemUTC())
return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays.toLong()).isBefore(today) }
return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays).isBefore(today) }
}
/**

View file

@ -57,7 +57,7 @@ object EntriesUtils {
fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String {
val buff = StringBuilder().append(buildLinkCmd(entryIndex)).append(": ")
.append('[').append(entry.nick).append(']')
if (isView && entry.hasComments()) {
if (isView && entry.comments.isNotEmpty()) {
buff.append("[+").append(entry.comments.size).append(']')
}
buff.append(' ')

View file

@ -107,8 +107,8 @@ class EntryLink : Serializable {
/**
* Adds a new comment.
*/
fun addComment(comment: String?, nick: String?): Int {
comments.add(EntryComment(comment!!, nick!!))
fun addComment(comment: String, nick: String): Int {
comments.add(EntryComment(comment, nick))
return comments.size - 1
}
@ -139,16 +139,6 @@ class EntryLink : Serializable {
return pinboardTags.toString()
}
/**
* Returns true if the entry has comments.
*/
fun hasComments(): Boolean = comments.isNotEmpty()
/**
* Returns true if the entry has tags.
*/
fun hasTags(): Boolean = tags.isNotEmpty()
/**
* Returns true if a string is contained in the link, title, or nick.
*/

View file

@ -75,6 +75,6 @@ class Calc(bot: Mobibot) : AbstractModule(bot) {
init {
commands.add(CALC_CMD)
help.add("To solve a mathematical calculation:")
help.add(Utils.helpIndent("%c $CALC_CMD <calculation>"))
help.add(Utils.helpFormat("%c $CALC_CMD <calculation>"))
}
}

View file

@ -80,14 +80,6 @@ class ModuleException : Exception {
}
}
/**
* Return `true` if the exception has a cause.
*/
@Suppress("unused")
fun hasCause(): Boolean {
return cause != null
}
companion object {
private const val serialVersionUID = 1L
}

View file

@ -58,7 +58,6 @@ class TellMessageTest {
Assertions.assertThat(tellMessage.isMatch(sender)).`as`("match sender").isTrue
Assertions.assertThat(tellMessage.isMatch(recipient)).`as`("match recipient").isTrue
Assertions.assertThat(tellMessage.isMatch("foo")).`as`("foo is no match").isFalse
Assertions.assertThat(tellMessage.isMatchId(tellMessage.id)).`as`("is match ID").isTrue
tellMessage.isReceived = true
Assertions.assertThat(tellMessage.isReceived).`as`("is received").isTrue
Assertions.assertThat(isValidDate(tellMessage.receptionDate)).`as`("received is valid date/time").isTrue

View file

@ -67,7 +67,7 @@ class EntryLinkTest {
while (entryLink.comments.size > 0) {
entryLink.deleteComment(r.nextInt(entryLink.comments.size))
}
Assertions.assertThat(entryLink.hasComments()).`as`("hasComments()").isFalse
Assertions.assertThat(entryLink.comments.isNotEmpty()).`as`("hasComments()").isFalse
entryLink.addComment("nothing", "nobody")
entryLink.setComment(0, "something", "somebody")
Assertions.assertThat(entryLink.getComment(0).nick).`as`("getNick(somebody)").isEqualTo("somebody")
@ -81,7 +81,7 @@ class EntryLinkTest {
Assertions.assertThat(tag.name).`as`("tag.getName($i)").isEqualTo("tag" + (i + 1))
}
Assertions.assertThat(entryLink.tags.size).`as`("getTags().size() is 5").isEqualTo(5)
Assertions.assertThat(entryLink.hasTags()).`as`("hasTags() is true").isTrue
Assertions.assertThat(entryLink.tags.isNotEmpty()).`as`("hasTags() is true").isTrue
entryLink.setTags("-tag5")
entryLink.setTags("+mobitopia")
entryLink.setTags("tag4")

View file

@ -62,7 +62,7 @@ class GoogleSearchTest : LocalProperties() {
.`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause()
} catch (e: ModuleException) {
// Avoid displaying api keys in CI logs
if ("true" == System.getenv("CI") && !apiKey.isBlank() && !cseKey.isBlank()) {
if ("true" == System.getenv("CI") && apiKey.isNotBlank() && cseKey.isNotBlank()) {
throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey))
} else {
throw e

View file

@ -63,7 +63,7 @@ class StockQuoteTest : LocalProperties() {
.isInstanceOf(ModuleException::class.java).hasNoCause()
} catch (e: ModuleException) {
// Avoid displaying api keys in CI logs
if ("true" == System.getenv("CI") && !apiKey.isBlank()) {
if ("true" == System.getenv("CI") && apiKey.isNotBlank()) {
throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey))
} else {
throw e