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 {
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 'io.gitlab.arturbosch.detekt' version '1.19.0'
id 'java'
@ -78,7 +78,7 @@ dependencies {
testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25'
// testImplementation 'org.mockito.kotlin:mockito-kotlin: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 {

View file

@ -42,7 +42,8 @@
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID>
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</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: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>

View file

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

View file

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

View file

@ -31,7 +31,9 @@
*/
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.links.LinksMgr
import net.thauvin.erik.mobibot.modules.AbstractModule
import org.pircbotx.hooks.events.PrivateMessageEvent
import org.pircbotx.hooks.types.GenericMessageEvent
@ -40,7 +42,10 @@ import java.util.Properties
/**
* 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 modules: MutableList<AbstractModule> = mutableListOf()
val modulesNames: MutableList<String> = mutableListOf()
@ -50,8 +55,9 @@ class Addons {
/**
* Add a module with properties.
*/
fun add(module: AbstractModule, props: Properties) {
fun add(module: AbstractModule) {
with(module) {
if (disabledModules.notContains(name, true)) {
if (hasProperties()) {
propertyKeys.forEach {
setProperty(it, props.getProperty(it, ""))
@ -60,17 +66,19 @@ class Addons {
if (isEnabled) {
modules.add(this)
modulesNames.add(javaClass.simpleName)
modulesNames.add(name)
names.addAll(commands)
}
}
}
}
/**
* Add a command with properties.
*/
fun add(command: AbstractCommand, props: Properties) {
fun add(command: AbstractCommand) {
with(command) {
if (disableCommands.notContains(name, true)) {
if (properties.isNotEmpty()) {
properties.keys.forEach {
setProperty(it, props.getProperty(it, ""))
@ -88,6 +96,7 @@ class Addons {
}
}
}
}
/**
* Execute a command or module.

View file

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

View file

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

View file

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

View file

@ -47,6 +47,8 @@ import java.io.IOException
class CryptoPrices : ThreadedModule() {
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).
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -45,6 +45,8 @@ import java.time.temporal.ChronoField
* The WorldTime module.
*/
class WorldTime : AbstractModule() {
override val name = "WorldTime"
companion object {
/**
* 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.links.Comment
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.Lookup
import net.thauvin.erik.mobibot.modules.RockPaperScissors
import net.thauvin.erik.mobibot.modules.Twitter
import net.thauvin.erik.mobibot.modules.War
import org.testng.annotations.Test
import java.util.Properties
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
fun addTest() {
val p = Properties()
// Modules
addons.add(Joke(), p)
addons.add(RockPaperScissors(), p)
addons.add(Twitter(), p) // no properties, disabled.
addons.add(Joke())
addons.add(RockPaperScissors())
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.modulesNames, "module names").containsExactly("Joke", "RockPaperScissors")
// Commands
addons.add(View(), p)
addons.add(Comment(), p)
addons.add(Cycle(), p)
addons.add(Die(), p) // invisible
addons.add(ChannelFeed("channel"), p) // no properties, disabled
addons.add(Ignore(), p.apply { put(Ignore.IGNORE_PROP, "nick") })
assertThat(addons.commands.size, "commands = 4").isEqualTo(5)
addons.add(View())
addons.add(Comment())
addons.add(Cycle())
addons.add(Die()) // invisible
addons.add(ChannelFeed("channel")) // no properties, disabled
p[Ignore.IGNORE_PROP] = "nick"
addons.add(Ignore())
assertThat(addons.commands.size, "commands = 3").isEqualTo(3)
assertThat(addons.ops, "ops").containsExactly("cycle")
@ -78,8 +86,6 @@ class AddonsTest {
"rock",
"paper",
"scissors",
"view",
"comment",
"ignore"
)
}

View file

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