Implemented module to query Wolfram Alpha

This commit is contained in:
Erik C. Thauvin 2022-09-15 23:28:08 -07:00
parent 0880d53894
commit 3ba2dfba4a
6 changed files with 215 additions and 0 deletions

View file

@ -66,6 +66,8 @@
<ID>ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException</ID> <ID>ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException</ID>
<ID>SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException</ID> <ID>SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException</ID>
<ID>SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException</ID> <ID>SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException</ID>
<ID>SwallowedException:WolframAlpha.kt$WolframAlpha.Companion$ioe: IOException</ID>
<ID>SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException</ID>
<ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List&lt;Message&gt;</ID> <ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List&lt;Message&gt;</ID>
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List&lt;Message&gt;</ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List&lt;Message&gt;</ID>
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID> <ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID>

View file

@ -63,3 +63,9 @@ tell-max-size=50
# Get Alpha Vantage Stock Quote API key from: https://www.alphavantage.co/support/#api-key # Get Alpha Vantage Stock Quote API key from: https://www.alphavantage.co/support/#api-key
# #
#alphavantage-api-key= #alphavantage-api-key=
#
# Get Wolfram Alpa API key from: https://developer.wolframalpha.com/portal/
#
#wolfram-api-key=
#wolfram-units=imperial

View file

@ -79,6 +79,7 @@ import net.thauvin.erik.mobibot.modules.RockPaperScissors
import net.thauvin.erik.mobibot.modules.StockQuote import net.thauvin.erik.mobibot.modules.StockQuote
import net.thauvin.erik.mobibot.modules.War import net.thauvin.erik.mobibot.modules.War
import net.thauvin.erik.mobibot.modules.Weather2 import net.thauvin.erik.mobibot.modules.Weather2
import net.thauvin.erik.mobibot.modules.WolframAlpha
import net.thauvin.erik.mobibot.modules.WorldTime import net.thauvin.erik.mobibot.modules.WorldTime
import net.thauvin.erik.semver.Version import net.thauvin.erik.semver.Version
import org.pircbotx.Configuration import org.pircbotx.Configuration
@ -426,6 +427,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
addons.add(Weather2()) addons.add(Weather2())
addons.add(WorldTime()) addons.add(WorldTime())
addons.add(War()) addons.add(War())
addons.add(WolframAlpha())
// Sort the addons // Sort the addons
addons.names.sort() addons.names.sort()

View file

@ -0,0 +1,133 @@
/*
* WolframAlpha.kt
*
* Copyright (c) 2004-2022, 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.Utils
import net.thauvin.erik.mobibot.Utils.encodeUrl
import net.thauvin.erik.mobibot.Utils.reader
import net.thauvin.erik.mobibot.Utils.sendMessage
import org.pircbotx.hooks.types.GenericMessageEvent
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.IOException
import java.net.URL
class WolframAlpha : ThreadedModule() {
private val logger: Logger = LoggerFactory.getLogger(WolframAlpha::class.java)
override val name = "WolframAlpha"
private fun getUnits(unit: String?): String {
println("--> $unit")
return if (unit?.lowercase() == METRIC) {
METRIC
} else {
IMPERIAL
}
}
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
if (args.isNotBlank()) {
try {
val query = args.trim().split("units=", limit = 2, ignoreCase = true)
event.sendMessage(
queryWolfram(
query[0].trim(),
units = if (query.size == 2) {
getUnits(query[1].trim())
} else {
getUnits(properties[WOLFRAM_UNITS_PROP])
},
apiKey = properties[WOLFRAM_API_KEY_PROP]
)
)
} catch (e: ModuleException) {
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
e.message?.let {
event.sendMessage(it)
}
}
} else {
helpResponse(event)
}
}
companion object {
/**
* The Wolfram Alpha API Key property.
*/
const val WOLFRAM_API_KEY_PROP = "wolfram-api-key"
/**
* The Wolfram units properties
*/
const val WOLFRAM_UNITS_PROP = "wolfram-units"
const val METRIC = "metric"
const val IMPERIAL = "imperial"
// Wolfram command
private const val WOLFRAM_CMD = "wolfram"
// Wolfram Alpha API URL
private const val API_URL = "http://api.wolframalpha.com/v1/spoken?appid="
@JvmStatic
@Throws(ModuleException::class)
fun queryWolfram(query: String, units: String = IMPERIAL, apiKey: String?): String {
if (!apiKey.isNullOrEmpty()) {
try {
return URL("${API_URL}${apiKey}&units=${units}&i=" + query.encodeUrl()).reader()
} catch (ioe: IOException) {
throw ModuleException(
"wolfram($query): IOE",
"Looks like Wolfram Alpha isn't able to answer that.",
)
}
} else {
throw ModuleException("wolfram($query): No API Key", "No Wolfram Alpha API key specified.")
}
}
}
init {
commands.add(WOLFRAM_CMD)
with(help) {
add("To get answers from Wolfram Alpha:")
add(Utils.helpFormat("%c $WOLFRAM_CMD <query> [units=(${METRIC}|${IMPERIAL})]"))
add("For example:")
add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas"))
add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric"))
}
initProperties(WOLFRAM_API_KEY_PROP, WOLFRAM_UNITS_PROP)
}
}

View file

@ -0,0 +1,68 @@
/*
* WolframAlphaTest.kt
*
* Copyright (c) 2004-2022, 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 assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
import net.thauvin.erik.mobibot.LocalProperties
import org.testng.annotations.Test
class WolframAlphaTest : LocalProperties() {
@Test
@Throws(ModuleException::class)
fun queryWolframTest() {
val apiKey = getProperty(WolframAlpha.WOLFRAM_API_KEY_PROP)
try {
assertThat(WolframAlpha.queryWolfram("SFO to SEA", apiKey = apiKey), "SFO to SEA").contains("miles")
assertThat(
WolframAlpha.queryWolfram("SFO to LAX", WolframAlpha.METRIC, apiKey),
"SFO to LA"
).contains("kilometers")
assertThat { WolframAlpha.queryWolfram("1 gallon to liter", apiKey = "") }
.isFailure()
.isInstanceOf(ModuleException::class.java)
} catch (e: ModuleException) {
// Avoid displaying api key in CI logs
if ("true" == System.getenv("CI")) {
throw e.sanitize(apiKey)
} else {
throw e
}
}
}
}

View file

@ -80,6 +80,10 @@
<li>Performing Google searches <li>Performing Google searches
<div><code>mobibot: google mobitopia on irc</code></div> <div><code>mobibot: google mobitopia on irc</code></div>
</li> </li>
<li>Getting answers from <a href="https://wolframalpha.com/">Wolfram Alpha</a>
<div><code>mobibot: wolfram days until christmas</code></div>
<div><code>mobibot: wolfram 1 gallon to liter</code></div>
</li>
<li>Displaying weather information <li>Displaying weather information
<div><code>mobibot: weather san francisco</code></div> <div><code>mobibot: weather san francisco</code></div>
<div><code>mobibot: weather 94123 </code></div> <div><code>mobibot: weather 94123 </code></div>