Added support for disabled-modules and disabled-commands properties.

This commit is contained in:
Erik C. Thauvin 2022-01-08 23:11:44 -08:00
parent 12bc76406b
commit e4dd6d5254
23 changed files with 144 additions and 83 deletions

View file

@ -1,6 +1,6 @@
plugins { plugins {
id 'application' id 'application'
id 'com.github.ben-manes.versions' version '0.40.0' id 'com.github.ben-manes.versions' version '0.41.0'
id 'idea' id 'idea'
id 'io.gitlab.arturbosch.detekt' version '1.19.0' id 'io.gitlab.arturbosch.detekt' version '1.19.0'
id 'java' id 'java'
@ -78,7 +78,7 @@ dependencies {
testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25'
// testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0'
// testImplementation "org.mockito:mockito-core:4.0.0" // testImplementation "org.mockito:mockito-core:4.0.0"
testImplementation 'org.testng:testng:7.4.0' testImplementation 'org.testng:testng:7.5'
} }
test { test {

View file

@ -42,7 +42,8 @@
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID>
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID>
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID>
<ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand)</ID>
<ID>NestedBlockDepth:Addons.kt$Addons$ fun add(module: AbstractModule)</ID>
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID>
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List&lt;String?&gt;)</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List&lt;String?&gt;)</ID>

View file

@ -1,5 +1,5 @@
channel=#mobitopia channel=#mobitopia
server=irc.freenode.net server=irc.libera.chat
#port=6667 #port=6667
login=mobibot login=mobibot
nick=mobibot nick=mobibot
@ -24,6 +24,9 @@ backlogs=http://www.mobitopia.org/mobibot/logs
tell-max-days=5 tell-max-days=5
tell-max-size=50 tell-max-size=50
#disabled-commands=die, ignore
#disabled-modules=dice, joke
# #
# Credentials for: http://pinboard.in/ # Credentials for: http://pinboard.in/
# #

View file

@ -75,6 +75,12 @@ public final class War extends AbstractModule {
help.add(Utils.helpFormat("%c " + WAR_CMD)); help.add(Utils.helpFormat("%c " + WAR_CMD));
} }
@NotNull
@Override
public String getName() {
return "War";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View file

@ -31,7 +31,9 @@
*/ */
package net.thauvin.erik.mobibot package net.thauvin.erik.mobibot
import net.thauvin.erik.mobibot.Utils.notContains
import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.AbstractCommand
import net.thauvin.erik.mobibot.commands.links.LinksMgr
import net.thauvin.erik.mobibot.modules.AbstractModule import net.thauvin.erik.mobibot.modules.AbstractModule
import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.events.PrivateMessageEvent
import org.pircbotx.hooks.types.GenericMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent
@ -40,7 +42,10 @@ import java.util.Properties
/** /**
* Modules and Commands addons. * Modules and Commands addons.
*/ */
class Addons { class Addons(private val props: Properties) {
private val disabledModules = props.getProperty("disabled-modules", "").split(LinksMgr.TAG_MATCH.toRegex())
private val disableCommands = props.getProperty("disabled-commands", "").split(LinksMgr.TAG_MATCH.toRegex())
val commands: MutableList<AbstractCommand> = mutableListOf() val commands: MutableList<AbstractCommand> = mutableListOf()
val modules: MutableList<AbstractModule> = mutableListOf() val modules: MutableList<AbstractModule> = mutableListOf()
val modulesNames: MutableList<String> = mutableListOf() val modulesNames: MutableList<String> = mutableListOf()
@ -50,18 +55,20 @@ class Addons {
/** /**
* Add a module with properties. * Add a module with properties.
*/ */
fun add(module: AbstractModule, props: Properties) { fun add(module: AbstractModule) {
with(module) { with(module) {
if (hasProperties()) { if (disabledModules.notContains(name, true)) {
propertyKeys.forEach { if (hasProperties()) {
setProperty(it, props.getProperty(it, "")) propertyKeys.forEach {
setProperty(it, props.getProperty(it, ""))
}
} }
}
if (isEnabled) { if (isEnabled) {
modules.add(this) modules.add(this)
modulesNames.add(javaClass.simpleName) modulesNames.add(name)
names.addAll(commands) names.addAll(commands)
}
} }
} }
} }
@ -69,20 +76,22 @@ class Addons {
/** /**
* Add a command with properties. * Add a command with properties.
*/ */
fun add(command: AbstractCommand, props: Properties) { fun add(command: AbstractCommand) {
with(command) { with(command) {
if (properties.isNotEmpty()) { if (disableCommands.notContains(name, true)) {
properties.keys.forEach { if (properties.isNotEmpty()) {
setProperty(it, props.getProperty(it, "")) properties.keys.forEach {
setProperty(it, props.getProperty(it, ""))
}
} }
} if (isEnabled()) {
if (isEnabled()) { commands.add(this)
commands.add(this) if (isVisible) {
if (isVisible) { if (isOpOnly) {
if (isOpOnly) { ops.add(name)
ops.add(name) } else {
} else { names.add(name)
names.add(name) }
} }
} }
} }

View file

@ -112,7 +112,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
private val config: Configuration private val config: Configuration
// Commands and Modules // Commands and Modules
private val addons = Addons() private val addons: Addons
// Tell module // Tell module
private val tell: Tell private val tell: Tell
@ -391,46 +391,48 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
pinboard.setApiToken(p.getProperty("pinboard-api-token", "")) pinboard.setApiToken(p.getProperty("pinboard-api-token", ""))
} }
addons = Addons(p)
// Load the commands // Load the commands
addons.add(ChannelFeed(channel.removePrefix("#")), p) addons.add(ChannelFeed(channel.removePrefix("#")))
addons.add(Comment(), p) addons.add(Comment())
addons.add(Cycle(), p) addons.add(Cycle())
addons.add(Die(), p) addons.add(Die())
addons.add(Ignore(), p) addons.add(Ignore())
addons.add(LinksMgr(), p) addons.add(LinksMgr())
addons.add(Me(), p) addons.add(Me())
addons.add(Msg(), p) addons.add(Msg())
addons.add(Nick(), p) addons.add(Nick())
addons.add(Posting(), p) addons.add(Posting())
addons.add(Recap(), p) addons.add(Recap())
addons.add(Say(), p) addons.add(Say())
addons.add(Tags(), p) addons.add(Tags())
// Tell command // Tell command
tell = Tell("${logsDirPath}${nickname}.ser") tell = Tell("${logsDirPath}${nickname}.ser")
addons.add(tell, p) addons.add(tell)
addons.add(LinksMgr.twitter, p) addons.add(LinksMgr.twitter)
addons.add(Users(), p) addons.add(Users())
addons.add(Versions(), p) addons.add(Versions())
addons.add(View(), p) addons.add(View())
// Load the modules // Load the modules
addons.add(Calc(), p) addons.add(Calc())
addons.add(CryptoPrices(), p) addons.add(CryptoPrices())
addons.add(CurrencyConverter(), p) addons.add(CurrencyConverter())
addons.add(Dice(), p) addons.add(Dice())
addons.add(GoogleSearch(), p) addons.add(GoogleSearch())
addons.add(Info(tell), p) addons.add(Info(tell))
addons.add(Joke(), p) addons.add(Joke())
addons.add(Lookup(), p) addons.add(Lookup())
addons.add(Modules(addons.modulesNames), p) addons.add(Modules(addons.modulesNames))
addons.add(Ping(), p) addons.add(Ping())
addons.add(RockPaperScissors(), p) addons.add(RockPaperScissors())
addons.add(StockQuote(), p) addons.add(StockQuote())
addons.add(Weather2(), p) addons.add(Weather2())
addons.add(WorldTime(), p) addons.add(WorldTime())
addons.add(War(), p) addons.add(War())
// Sort the addons // Sort the addons
addons.sort() addons.sort()

View file

@ -180,7 +180,7 @@ object Utils {
} }
/** /**
* Return the last item of a list of strings or empty if none. * Returns the last item of a list of strings or empty if none.
*/ */
@JvmStatic @JvmStatic
fun List<String>.lastOrEmpty(): String { fun List<String>.lastOrEmpty(): String {
@ -190,6 +190,12 @@ object Utils {
"" ""
} }
/**
* Returns {@code true} if the list does not contain the given string.
*/
@JvmStatic
fun List<String>.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) }
/** /**
* Obfuscates the given string. * Obfuscates the given string.
*/ */

View file

@ -41,6 +41,11 @@ import org.pircbotx.hooks.types.GenericMessageEvent
* The `Module` abstract class. * The `Module` abstract class.
*/ */
abstract class AbstractModule { abstract class AbstractModule {
/**
* The module name.
*/
abstract val name: String
/** /**
* The module's commands, if any. * The module's commands, if any.
*/ */

View file

@ -46,6 +46,8 @@ import java.text.DecimalFormat
class Calc : AbstractModule() { class Calc : AbstractModule() {
private val logger: Logger = LoggerFactory.getLogger(Calc::class.java) private val logger: Logger = LoggerFactory.getLogger(Calc::class.java)
override val name = "Calc"
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
if (args.isNotBlank()) { if (args.isNotBlank()) {
try { try {

View file

@ -47,6 +47,8 @@ import java.io.IOException
class CryptoPrices : ThreadedModule() { class CryptoPrices : ThreadedModule() {
private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java) private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java)
override val name = "CryptoPrices"
/** /**
* Returns the cryptocurrency market price from [Coinbase](https://developers.coinbase.com/api/v2#get-spot-price). * Returns the cryptocurrency market price from [Coinbase](https://developers.coinbase.com/api/v2#get-spot-price).
*/ */

View file

@ -59,6 +59,8 @@ import javax.xml.XMLConstants
class CurrencyConverter : ThreadedModule() { class CurrencyConverter : ThreadedModule() {
private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java) private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java)
override val name = "CurrencyConverter"
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
synchronized(this) { synchronized(this) {
if (pubDate != today()) { if (pubDate != today()) {

View file

@ -40,6 +40,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent
* The Dice module. * The Dice module.
*/ */
class Dice : AbstractModule() { class Dice : AbstractModule() {
override val name = "Dice"
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
val botRoll = roll() val botRoll = roll()
val roll = roll() val roll = roll()

View file

@ -55,6 +55,8 @@ import java.net.URL
class GoogleSearch : ThreadedModule() { class GoogleSearch : ThreadedModule() {
private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java) private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java)
override val name = "GoogleSearch"
/** /**
* Searches Google. * Searches Google.
*/ */

View file

@ -54,6 +54,8 @@ import java.net.URL
class Joke : ThreadedModule() { class Joke : ThreadedModule() {
private val logger: Logger = LoggerFactory.getLogger(Joke::class.java) private val logger: Logger = LoggerFactory.getLogger(Joke::class.java)
override val name = "Joke"
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
runBlocking { runBlocking {
launch { run(channel, cmd, args, event) } launch { run(channel, cmd, args, event) }

View file

@ -48,6 +48,8 @@ import java.net.UnknownHostException
class Lookup : AbstractModule() { class Lookup : AbstractModule() {
private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java) private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java)
override val name = "Lookup"
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
if (args.matches("(\\S.)+(\\S)+".toRegex())) { if (args.matches("(\\S.)+(\\S)+".toRegex())) {
try { try {

View file

@ -39,9 +39,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent
* The Ping module. * The Ping module.
*/ */
class Ping : AbstractModule() { class Ping : AbstractModule() {
/** override val name = "Ping"
* {@inheritDoc}
*/
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
event.bot().sendIRC().action(channel, randomPing()) event.bot().sendIRC().action(channel, randomPing())
} }

View file

@ -42,6 +42,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent
* Simple module example in Kotlin. * Simple module example in Kotlin.
*/ */
class RockPaperScissors : AbstractModule() { class RockPaperScissors : AbstractModule() {
override val name = "RockPaperScissors"
init { init {
with(commands) { with(commands) {
add(Hands.ROCK.name.lowercase()) add(Hands.ROCK.name.lowercase())

View file

@ -55,6 +55,8 @@ import java.net.URL
class StockQuote : ThreadedModule() { class StockQuote : ThreadedModule() {
private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java) private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java)
override val name = "StockQuote"
/** /**
* Returns the specified stock quote from Alpha Vantage. * Returns the specified stock quote from Alpha Vantage.
*/ */

View file

@ -57,6 +57,8 @@ class Twitter : ThreadedModule() {
// Twitter auto-posts. // Twitter auto-posts.
private val entries: MutableSet<Int> = HashSet() private val entries: MutableSet<Int> = HashSet()
override val name = "Twitter"
/** /**
* Add an entry to be posted on Twitter. * Add an entry to be posted on Twitter.
*/ */

View file

@ -57,6 +57,8 @@ import kotlin.math.roundToInt
class Weather2 : ThreadedModule() { class Weather2 : ThreadedModule() {
private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java) private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java)
override val name = "Weather"
/** /**
* Fetches the weather data from a specific city. * Fetches the weather data from a specific city.
*/ */

View file

@ -45,6 +45,8 @@ import java.time.temporal.ChronoField
* The WorldTime module. * The WorldTime module.
*/ */
class WorldTime : AbstractModule() { class WorldTime : AbstractModule() {
override val name = "WorldTime"
companion object { companion object {
/** /**
* Beats (Internet Time) keyword * Beats (Internet Time) keyword

View file

@ -41,35 +41,43 @@ import net.thauvin.erik.mobibot.commands.Die
import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.Ignore
import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.Comment
import net.thauvin.erik.mobibot.commands.links.View import net.thauvin.erik.mobibot.commands.links.View
import net.thauvin.erik.mobibot.modules.Dice
import net.thauvin.erik.mobibot.modules.Joke import net.thauvin.erik.mobibot.modules.Joke
import net.thauvin.erik.mobibot.modules.Lookup
import net.thauvin.erik.mobibot.modules.RockPaperScissors import net.thauvin.erik.mobibot.modules.RockPaperScissors
import net.thauvin.erik.mobibot.modules.Twitter import net.thauvin.erik.mobibot.modules.Twitter
import net.thauvin.erik.mobibot.modules.War
import org.testng.annotations.Test import org.testng.annotations.Test
import java.util.Properties import java.util.Properties
class AddonsTest { class AddonsTest {
private val addons = Addons() private val p = Properties().apply {
put("disabled-modules", "war,dice Lookup")
put("disabled-commands", "View | comment")
}
private val addons = Addons(p)
@Test @Test
fun addTest() { fun addTest() {
val p = Properties()
// Modules // Modules
addons.add(Joke(), p) addons.add(Joke())
addons.add(RockPaperScissors(), p) addons.add(RockPaperScissors())
addons.add(Twitter(), p) // no properties, disabled. addons.add(Twitter()) // no properties, disabled.
addons.add(War())
addons.add(Dice())
addons.add(Lookup())
assertThat(addons.modules.size, "modules = 2").isEqualTo(2) assertThat(addons.modules.size, "modules = 2").isEqualTo(2)
assertThat(addons.modulesNames, "module names").containsExactly("Joke", "RockPaperScissors") assertThat(addons.modulesNames, "module names").containsExactly("Joke", "RockPaperScissors")
// Commands // Commands
addons.add(View(), p) addons.add(View())
addons.add(Comment(), p) addons.add(Comment())
addons.add(Cycle(), p) addons.add(Cycle())
addons.add(Die(), p) // invisible addons.add(Die()) // invisible
addons.add(ChannelFeed("channel"), p) // no properties, disabled addons.add(ChannelFeed("channel")) // no properties, disabled
addons.add(Ignore(), p.apply { put(Ignore.IGNORE_PROP, "nick") }) p[Ignore.IGNORE_PROP] = "nick"
assertThat(addons.commands.size, "commands = 4").isEqualTo(5) addons.add(Ignore())
assertThat(addons.commands.size, "commands = 3").isEqualTo(3)
assertThat(addons.ops, "ops").containsExactly("cycle") assertThat(addons.ops, "ops").containsExactly("cycle")
@ -78,8 +86,6 @@ class AddonsTest {
"rock", "rock",
"paper", "paper",
"scissors", "scissors",
"view",
"comment",
"ignore" "ignore"
) )
} }

View file

@ -1,9 +1,9 @@
#Generated by the Semver Plugin for Gradle #Generated by the Semver Plugin for Gradle
#Sat Jan 01 06:34:39 PST 2022 #Sat Jan 08 23:07:31 PST 2022
version.buildmeta=2428 version.buildmeta=2440
version.major=0 version.major=0
version.minor=8 version.minor=8
version.patch=0 version.patch=0
version.prerelease=beta version.prerelease=beta
version.project=mobibot version.project=mobibot
version.semver=0.8.0-beta+2428 version.semver=0.8.0-beta+2440