diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml
index afd1e14..a4065f6 100644
--- a/config/detekt/baseline.xml
+++ b/config/detekt/baseline.xml
@@ -15,6 +15,7 @@
LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )
LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )
NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)
+ NestedBlockDepth:Bitcoin.kt$Bitcoin$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)
NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )
NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)
NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean): Boolean
@@ -38,10 +39,12 @@
NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )
ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean
ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean
+ ThrowsCount:Bitcoin.kt$Bitcoin.Companion$ @JvmStatic @Throws(ModuleException::class) fun marketPrice(currency: String): List<Message>
ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message>
ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message>
ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject
ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message>
+ TooGenericExceptionCaught:Bitcoin.kt$Bitcoin.Companion$e: NullPointerException
TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception
TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception
TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception
diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt
index bf30f72..72f0e89 100644
--- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt
+++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt
@@ -64,6 +64,7 @@ import net.thauvin.erik.mobibot.commands.links.View
import net.thauvin.erik.mobibot.commands.tell.Tell
import net.thauvin.erik.mobibot.entries.EntriesMgr
import net.thauvin.erik.mobibot.entries.EntryLink
+import net.thauvin.erik.mobibot.modules.Bitcoin
import net.thauvin.erik.mobibot.modules.Calc
import net.thauvin.erik.mobibot.modules.CurrencyConverter
import net.thauvin.erik.mobibot.modules.Dice
@@ -677,6 +678,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
addons.add(View(this), p)
// Load the modules
+ addons.add(Bitcoin(this), p)
addons.add(Calc(this), p)
addons.add(CurrencyConverter(this), p)
addons.add(Dice(this), p)
diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt
new file mode 100644
index 0000000..0289bf2
--- /dev/null
+++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Bitcoin.kt
@@ -0,0 +1,123 @@
+/*
+ * Bitcoin.kt
+ *
+ * Copyright (c) 2004-2021, 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.modules
+
+import net.thauvin.erik.mobibot.Mobibot
+import net.thauvin.erik.mobibot.Utils
+import net.thauvin.erik.mobibot.msg.ErrorMessage
+import net.thauvin.erik.mobibot.msg.Message
+import net.thauvin.erik.mobibot.msg.NoticeMessage
+import net.thauvin.erik.mobibot.msg.PublicMessage
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.IOException
+import java.net.URL
+
+/**
+ * The Bitcoin module.
+ */
+class Bitcoin(bot: Mobibot) : ThreadedModule(bot) {
+ // Currencies
+ private val currencies = listOf(
+ "USD", "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "DKK", "EUR", "GBP", "HKD", "INR", "ISK", "JPY", "KRW",
+ "NZD", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD");
+
+ /**
+ * Returns the bitcoin market price from [Blockchain.info](https://blockchain.info/ticker).
+ */
+ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
+ with(bot) {
+ val arg = args.trim().uppercase()
+ @Suppress("MagicNumber")
+ if (!currencies.contains(arg)) {
+ helpResponse(sender, isPrivate)
+ send(sender, "The supported currencies are: ", isPrivate)
+ @Suppress("MagicNumber")
+ sendList(sender, currencies, 12, isPrivate, isIndent = true)
+ } else {
+ try {
+ val messages = marketPrice(arg)
+ for (msg in messages) {
+ send(sender, msg)
+ }
+ } catch (e: ModuleException) {
+ if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
+ send(e.message)
+ }
+ }
+ }
+ }
+
+ companion object {
+ // Blockchain Ticker URL
+ private const val TICKER_URL = "https://blockchain.info/ticker"
+
+ // Quote command
+ private const val BITCOIN_CMD = "bitcoin"
+
+ /**
+ * Retrieves the bitcoin market price.
+ */
+ @JvmStatic
+ @Throws(ModuleException::class)
+ fun marketPrice(currency: String): List {
+ val debugMessage = "marketPrice($currency)"
+ val messages = mutableListOf()
+ try {
+ val response = Utils.urlReader(URL("$TICKER_URL"))
+ val json = JSONObject(response)
+ val bpi = json.getJSONObject(currency.trim().uppercase())
+ val symbol = bpi.getString("symbol");
+ with(messages) {
+ add(PublicMessage("BTC: $symbol" + bpi.getBigDecimal("last") + " [$currency]"))
+ add(NoticeMessage(" 15m: $symbol" + bpi.getBigDecimal("15m")))
+ add(NoticeMessage(" Buy: $symbol" + bpi.getBigDecimal("buy")))
+ add(NoticeMessage(" Sell: $symbol" + bpi.getBigDecimal("sell")))
+ }
+ return messages
+ } catch (e: IOException) {
+ throw ModuleException(debugMessage, "An IO error has occurred retrieving the bitcoin market price.", e)
+ } catch (e: NullPointerException) {
+ throw ModuleException(debugMessage, "An error has occurred retrieving the bitcoin market price.", e)
+ } catch (e: org.json.JSONException) {
+ throw ModuleException(
+ debugMessage, "A parsing error has occurred retriving the bitcoin market price.", e)
+ }
+ }
+ }
+
+ init {
+ commands.add(BITCOIN_CMD)
+ help.add("To retrieve the bitcoin market price:")
+ help.add(Utils.helpFormat("%c $BITCOIN_CMD "))
+ }
+}
diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt
new file mode 100644
index 0000000..8453420
--- /dev/null
+++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/BitcoinTest.kt
@@ -0,0 +1,55 @@
+/*
+ * BitcoinTest.kt
+ *
+ * Copyright (c) 2004-2021, 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.modules
+
+import net.thauvin.erik.mobibot.LocalProperties
+import net.thauvin.erik.mobibot.modules.Bitcoin.Companion.marketPrice
+import org.assertj.core.api.Assertions.assertThat
+import org.testng.annotations.Test
+
+/**
+ * The `BitcoinTest` class.
+ */
+class BitcoinTest : LocalProperties() {
+ @Test
+ @Throws(ModuleException::class)
+ fun testMarketPrice() {
+ var messages = marketPrice("USD")
+ assertThat(messages).`as`("not empty").isNotEmpty
+ assertThat(messages[0].msg).`as`("btc & $").startsWith("BTC").contains("$")
+ assertThat(messages[1].msg).`as`("15m").contains("15m")
+
+ messages = marketPrice("GBP")
+ assertThat(messages[0].msg).`as`("BPB btc & £").startsWith("BTC").contains("£")
+ assertThat(messages[1].msg).`as`("GBP 15m").contains("15m")
+ }
+}
diff --git a/version.properties b/version.properties
index 9d6cb44..dcde182 100644
--- a/version.properties
+++ b/version.properties
@@ -1,9 +1,9 @@
#Generated by the Semver Plugin for Gradle
-#Mon May 03 14:17:39 PDT 2021
-version.buildmeta=576
+#Tue May 04 01:01:14 PDT 2021
+version.buildmeta=606
version.major=0
version.minor=8
version.patch=0
version.prerelease=beta
version.project=mobibot
-version.semver=0.8.0-beta+576
+version.semver=0.8.0-beta+606