From e4dd6d5254e0cd7c8fb483c4aefd1d80274a5f17 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 8 Jan 2022 23:11:44 -0800 Subject: [PATCH] Added support for disabled-modules and disabled-commands properties. --- build.gradle | 4 +- config/detekt/baseline.xml | 3 +- properties/mobibot.properties | 5 +- .../net/thauvin/erik/mobibot/modules/War.java | 6 ++ .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 53 ++++++++------ .../net/thauvin/erik/mobibot/Mobibot.kt | 70 ++++++++++--------- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 8 ++- .../erik/mobibot/modules/AbstractModule.kt | 5 ++ .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 + .../erik/mobibot/modules/CryptoPrices.kt | 2 + .../erik/mobibot/modules/CurrencyConverter.kt | 2 + .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 + .../erik/mobibot/modules/GoogleSearch.kt | 2 + .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 + .../thauvin/erik/mobibot/modules/Lookup.kt | 2 + .../net/thauvin/erik/mobibot/modules/Ping.kt | 5 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 + .../erik/mobibot/modules/StockQuote.kt | 2 + .../thauvin/erik/mobibot/modules/Twitter.kt | 2 + .../thauvin/erik/mobibot/modules/Weather2.kt | 2 + .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 + .../net/thauvin/erik/mobibot/AddonsTest.kt | 38 +++++----- version.properties | 6 +- 23 files changed, 144 insertions(+), 83 deletions(-) diff --git a/build.gradle b/build.gradle index 143d5f1..f62606e 100644 --- a/build.gradle +++ b/build.gradle @@ -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 { diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 7f49882..5f0626e 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -42,7 +42,8 @@ MagicNumber:WorldTime.kt$WorldTime.Companion$3600 MagicNumber:WorldTime.kt$WorldTime.Companion$60 MagicNumber:WorldTime.kt$WorldTime.Companion$86.4 - NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties) + NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand) + NestedBlockDepth:Addons.kt$Addons$ fun add(module: AbstractModule) NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 67326f3..28f5a8d 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -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/ # diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 16102b4..5565ecb 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -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} */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index e530ae8..4e95ad0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -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 = mutableListOf() val modules: MutableList = mutableListOf() val modulesNames: MutableList = mutableListOf() @@ -50,18 +55,20 @@ class Addons { /** * Add a module with properties. */ - fun add(module: AbstractModule, props: Properties) { + fun add(module: AbstractModule) { with(module) { - if (hasProperties()) { - propertyKeys.forEach { - setProperty(it, props.getProperty(it, "")) + if (disabledModules.notContains(name, true)) { + if (hasProperties()) { + propertyKeys.forEach { + setProperty(it, props.getProperty(it, "")) + } } - } - if (isEnabled) { - modules.add(this) - modulesNames.add(javaClass.simpleName) - names.addAll(commands) + if (isEnabled) { + modules.add(this) + modulesNames.add(name) + names.addAll(commands) + } } } } @@ -69,20 +76,22 @@ class Addons { /** * Add a command with properties. */ - fun add(command: AbstractCommand, props: Properties) { + fun add(command: AbstractCommand) { with(command) { - if (properties.isNotEmpty()) { - properties.keys.forEach { - setProperty(it, props.getProperty(it, "")) + if (disableCommands.notContains(name, true)) { + if (properties.isNotEmpty()) { + properties.keys.forEach { + setProperty(it, props.getProperty(it, "")) + } } - } - if (isEnabled()) { - commands.add(this) - if (isVisible) { - if (isOpOnly) { - ops.add(name) - } else { - names.add(name) + if (isEnabled()) { + commands.add(this) + if (isVisible) { + if (isOpOnly) { + ops.add(name) + } else { + names.add(name) + } } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index ef6f842..02467d6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -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() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 38fafa7..b68ee47 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -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.lastOrEmpty(): String { @@ -190,6 +190,12 @@ object Utils { "" } + /** + * Returns {@code true} if the list does not contain the given string. + */ + @JvmStatic + fun List.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) } + /** * Obfuscates the given string. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index dd5d3d5..25e7c9d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -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. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 29c0805..76f3786 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -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 { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 57e3dc4..8cb3396 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -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). */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 275e99c..f6de896 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -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()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 667a0b0..9e7725d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -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() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index a0658bc..6adffdd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -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. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index ff5c030..6922698 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -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) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index f6ae628..90a0316 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -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 { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index b4876de..0818120 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -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()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 95eb5e2..c092548 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -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()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 240ea0e..63d358c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -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. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 61fbd1b..74f0ba0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -57,6 +57,8 @@ class Twitter : ThreadedModule() { // Twitter auto-posts. private val entries: MutableSet = HashSet() + override val name = "Twitter" + /** * Add an entry to be posted on Twitter. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index bdc5de9..96ae93e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -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. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 7160d15..fc0edf6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -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 diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 33cca2e..54346a9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -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" ) } diff --git a/version.properties b/version.properties index 41eca0f..4aea8d8 100644 --- a/version.properties +++ b/version.properties @@ -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