Compare commits

..

2 commits

Author SHA1 Message Date
a1ea25ae74 Minor cleanup 2023-01-12 23:06:21 -08:00
c3d1930592 Added rate limit error 2023-01-12 23:06:07 -08:00
14 changed files with 71 additions and 60 deletions

View file

@ -13,6 +13,7 @@
<ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID>
<ID>MagicNumber:ChatGpt.kt$ChatGpt$400</ID> <ID>MagicNumber:ChatGpt.kt$ChatGpt$400</ID>
<ID>MagicNumber:ChatGpt.kt$ChatGpt.Companion$200</ID> <ID>MagicNumber:ChatGpt.kt$ChatGpt.Companion$200</ID>
<ID>MagicNumber:ChatGpt.kt$ChatGpt.Companion$429</ID>
<ID>MagicNumber:Comment.kt$Comment$3</ID> <ID>MagicNumber:Comment.kt$Comment$3</ID>
<ID>MagicNumber:CryptoPrices.kt$CryptoPrices$10</ID> <ID>MagicNumber:CryptoPrices.kt$CryptoPrices$10</ID>
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID>

View file

@ -50,16 +50,16 @@ import java.net.http.HttpResponse
class ChatGpt : AbstractModule() { class ChatGpt : AbstractModule() {
private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java)
override val name = "ChatGPT" override val name = CHATGPT_NAME
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 {
val answer = chat(args.trim(), properties[CHATGPT_API_KEY], properties[CHATGPT_MAX_TOKENS]!!.toInt()) val answer = chat(args.trim(), properties[API_KEY_PROP], properties[MAX_TOKENS_PROP]!!.toInt())
if (answer.isNotBlank()) { if (answer.isNotBlank()) {
event.sendMessage(WordUtils.wrap(answer, 400)) event.sendMessage(WordUtils.wrap(answer, 400))
} else { } else {
event.respond("ChatGPT is stumped.") event.respond("$name is stumped.")
} }
} catch (e: ModuleException) { } catch (e: ModuleException) {
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
@ -67,7 +67,7 @@ class ChatGpt : AbstractModule() {
event.respond(it) event.respond(it)
} }
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
if (logger.isErrorEnabled) logger.error("Invalid $CHATGPT_MAX_TOKENS property.", e) if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e)
event.respond("The $name module is misconfigured.") event.respond("The $name module is misconfigured.")
} }
} else { } else {
@ -77,20 +77,26 @@ class ChatGpt : AbstractModule() {
companion object { companion object {
/** /**
* The ChatGPT API Key property. * The service name.
*/ */
const val CHATGPT_API_KEY = "chatgpt-api-key" const val CHATGPT_NAME = "ChatGPT"
/** /**
* The ChatGPT max tokens property. * The API Key property.
*/ */
const val CHATGPT_MAX_TOKENS = "chatgpt-max-tokens" const val API_KEY_PROP = "chatgpt-api-key"
/**
* The max tokens property.
*/
const val MAX_TOKENS_PROP = "chatgpt-max-tokens"
// ChatGPT API URL
private const val API_URL = "https://api.openai.com/v1/completions"
// ChatGPT command // ChatGPT command
private const val CHATGPT_CMD = "chatgpt" private const val CHATGPT_CMD = "chatgpt"
// ChatGPT API URL
private const val API_URL = "https://api.openai.com/v1/completions"
@JvmStatic @JvmStatic
@Throws(ModuleException::class) @Throws(ModuleException::class)
@ -124,23 +130,28 @@ class ChatGpt : AbstractModule() {
return choices.getJSONObject(0).getString("text").trim() return choices.getJSONObject(0).getString("text").trim()
} catch (e: JSONException) { } catch (e: JSONException) {
throw ModuleException( throw ModuleException(
"chatgpt($query): JSON", "$CHATGPT_CMD($query): JSON",
"A JSON error has occurred while conversing with ChatGPT.", "A JSON error has occurred while conversing with $CHATGPT_NAME.",
e e
) )
} }
} else { } else {
throw IOException("Status Code: " + response.statusCode()) if (response.statusCode() == 429) {
throw ModuleException("$CHATGPT_CMD($query): Rate limit reached",
"Rate limit reached. Please try again later.")
} else {
throw IOException("HTTP Status Code: " + response.statusCode())
}
} }
} catch (e: IOException) { } catch (e: IOException) {
throw ModuleException( throw ModuleException(
"chatgpt($query): IO", "$CHATGPT_CMD($query): IO",
"An IO error has occurred while conversing with ChatGPT.", "An IO error has occurred while conversing with $CHATGPT_NAME.",
e e
) )
} }
} else { } else {
throw ModuleException("chatgpt($query)", "No ChatGPT API key specified.") throw ModuleException("$CHATGPT_CMD($query)", "No $CHATGPT_NAME API key specified.")
} }
} }
} }
@ -148,12 +159,12 @@ class ChatGpt : AbstractModule() {
init { init {
commands.add(CHATGPT_CMD) commands.add(CHATGPT_CMD)
with(help) { with(help) {
add("To get answers from ChatGPT:") add("To get answers from $name:")
add(Utils.helpFormat("%c $CHATGPT_CMD <query>")) add(Utils.helpFormat("%c $CHATGPT_CMD <query>"))
add("For example:") add("For example:")
add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms"))
add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?"))
} }
initProperties(CHATGPT_API_KEY, CHATGPT_MAX_TOKENS) initProperties(API_KEY_PROP, MAX_TOKENS_PROP)
} }
} }

View file

@ -65,7 +65,7 @@ class CryptoPrices : AbstractModule() {
} }
val debugMessage = "crypto($cmd $args)" val debugMessage = "crypto($cmd $args)"
if (args == CURRENCY_CODES_KEYWORD) { if (args == CODES_KEYWORD) {
event.sendMessage("The supported currencies are:") event.sendMessage("The supported currencies are:")
event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true) event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true)
} else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { } else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) {
@ -100,7 +100,7 @@ class CryptoPrices : AbstractModule() {
private val CURRENCIES: MutableMap<String, String> = mutableMapOf() private val CURRENCIES: MutableMap<String, String> = mutableMapOf()
// Currency codes keyword // Currency codes keyword
private const val CURRENCY_CODES_KEYWORD = "codes" private const val CODES_KEYWORD = "codes"
/** /**
* Get current market price. * Get current market price.
@ -153,7 +153,7 @@ class CryptoPrices : AbstractModule() {
add(helpFormat("%c $CRYPTO_CMD ETH EUR")) add(helpFormat("%c $CRYPTO_CMD ETH EUR"))
add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) add(helpFormat("%c $CRYPTO_CMD ETH2 GPB"))
add("To list the supported currencies:") add("To list the supported currencies:")
add(helpFormat("%c $CRYPTO_CMD $CURRENCY_CODES_KEYWORD")) add(helpFormat("%c $CRYPTO_CMD $CODES_KEYWORD"))
} }
loadCurrencies() loadCurrencies()
} }

View file

@ -67,8 +67,8 @@ class GoogleSearch : AbstractModule() {
try { try {
val results = searchGoogle( val results = searchGoogle(
args, args,
properties[GOOGLE_API_KEY_PROP], properties[API_KEY_PROP],
properties[GOOGLE_CSE_KEY_PROP], properties[CSE_KEY_PROP],
event.user.nick event.user.nick
) )
for (msg in results) { for (msg in results) {
@ -91,10 +91,10 @@ class GoogleSearch : AbstractModule() {
companion object { companion object {
// Google API Key property // Google API Key property
const val GOOGLE_API_KEY_PROP = "google-api-key" const val API_KEY_PROP = "google-api-key"
// Google Custom Search Engine ID property // Google Custom Search Engine ID property
const val GOOGLE_CSE_KEY_PROP = "google-cse-cx" const val CSE_KEY_PROP = "google-cse-cx"
// Google command // Google command
private const val GOOGLE_CMD = "google" private const val GOOGLE_CMD = "google"
@ -158,6 +158,6 @@ class GoogleSearch : AbstractModule() {
commands.add(GOOGLE_CMD) commands.add(GOOGLE_CMD)
help.add("To search Google:") help.add("To search Google:")
help.add(helpFormat("%c $GOOGLE_CMD <query>")) help.add(helpFormat("%c $GOOGLE_CMD <query>"))
initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP) initProperties(API_KEY_PROP, CSE_KEY_PROP)
} }
} }

View file

@ -63,7 +63,7 @@ class StockQuote : AbstractModule() {
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 {
val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) val messages = getQuote(args, properties[API_KEY_PROP])
for (msg in messages) { for (msg in messages) {
event.sendMessage(channel, msg) event.sendMessage(channel, msg)
} }
@ -80,17 +80,17 @@ class StockQuote : AbstractModule() {
companion object { companion object {
/** /**
* The Alpha Advantage property key. * The API property key.
*/ */
const val ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key" const val API_KEY_PROP = "alphavantage-api-key"
/** /**
* The Invalid Symbol error string. * The Invalid Symbol error string.
*/ */
const val INVALID_SYMBOL = "Invalid symbol." const val INVALID_SYMBOL = "Invalid symbol."
// Alpha Advantage URL // API URL
private const val ALPHAVANTAGE_URL = "https://www.alphavantage.co/query?function=" private const val API_URL = "https://www.alphavantage.co/query?function="
// Quote command // Quote command
private const val STOCK_CMD = "stock" private const val STOCK_CMD = "stock"
@ -145,7 +145,7 @@ class StockQuote : AbstractModule() {
with(messages) { with(messages) {
// Search for symbol/keywords // Search for symbol/keywords
response = URL( response = URL(
"${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey="
+ apiKey.encodeUrl() + apiKey.encodeUrl()
).reader().body ).reader().body
var json = getJsonResponse(response, debugMessage) var json = getJsonResponse(response, debugMessage)
@ -157,7 +157,7 @@ class StockQuote : AbstractModule() {
// Get quote for symbol // Get quote for symbol
response = URL( response = URL(
"${ALPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" "${API_URL}GLOBAL_QUOTE&symbol="
+ symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey="
+ apiKey.encodeUrl() + apiKey.encodeUrl()
).reader().body ).reader().body
@ -232,6 +232,6 @@ class StockQuote : AbstractModule() {
commands.add(STOCK_CMD) commands.add(STOCK_CMD)
help.add("To retrieve a stock quote:") help.add("To retrieve a stock quote:")
help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>")) help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>"))
initProperties(ALPHAVANTAGE_API_KEY_PROP) initProperties(API_KEY_PROP)
} }
} }

View file

@ -84,7 +84,7 @@ class Twitter : SocialModule() {
const val TOKEN_PROP = "twitter-token" const val TOKEN_PROP = "twitter-token"
const val TOKEN_SECRET_PROP = "twitter-tokenSecret" const val TOKEN_SECRET_PROP = "twitter-tokenSecret"
// Twitter command // Twitter commands
private const val TWITTER_CMD = "twitter" private const val TWITTER_CMD = "twitter"
private const val TWEET_CMD = "tweet" private const val TWEET_CMD = "tweet"
@ -118,7 +118,7 @@ class Twitter : SocialModule() {
dm.text dm.text
} }
} catch (e: TwitterException) { } catch (e: TwitterException) {
throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e) throw ModuleException("tweet($message)", "An error has occurred: ${e.message}", e)
} }
} }
} }
@ -126,7 +126,7 @@ class Twitter : SocialModule() {
init { init {
commands.add(TWITTER_CMD) commands.add(TWITTER_CMD)
commands.add(TWEET_CMD) commands.add(TWEET_CMD)
help.add("To tweet on Twitter:") help.add("To $TWEET_CMD on $name:")
help.add(helpFormat("%c $TWEET_CMD <message>")) help.add(helpFormat("%c $TWEET_CMD <message>"))
properties[AUTO_POST_PROP] = "false" properties[AUTO_POST_PROP] = "false"
initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP)

View file

@ -65,7 +65,7 @@ class Weather2 : AbstractModule() {
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 {
val messages = getWeather(args, properties[OWM_API_KEY_PROP]) val messages = getWeather(args, properties[API_KEY_PROP])
if (messages[0].isError) { if (messages[0].isError) {
helpResponse(event) helpResponse(event)
} else { } else {
@ -88,7 +88,7 @@ class Weather2 : AbstractModule() {
/** /**
* The OpenWeatherMap API Key property. * The OpenWeatherMap API Key property.
*/ */
const val OWM_API_KEY_PROP = "owm-api-key" const val API_KEY_PROP = "owm-api-key"
// Weather command // Weather command
private const val WEATHER_CMD = "weather" private const val WEATHER_CMD = "weather"
@ -246,6 +246,6 @@ class Weather2 : AbstractModule() {
add(helpFormat("%c $WEATHER_CMD paris, fr")) add(helpFormat("%c $WEATHER_CMD paris, fr"))
add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.") add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.")
} }
initProperties(OWM_API_KEY_PROP) initProperties(API_KEY_PROP)
} }
} }

View file

@ -66,9 +66,9 @@ class WolframAlpha : AbstractModule() {
units = if (query.size == 2) { units = if (query.size == 2) {
getUnits(query[1].trim()) getUnits(query[1].trim())
} else { } else {
getUnits(properties[WOLFRAM_UNITS_PROP]) getUnits(properties[UNITS_PROP])
}, },
appId = properties[WOLFRAM_APPID_KEY] appId = properties[APPID_KEY_PROP]
) )
) )
} catch (e: ModuleException) { } catch (e: ModuleException) {
@ -86,12 +86,13 @@ class WolframAlpha : AbstractModule() {
/** /**
* The Wolfram Alpha API Key property. * The Wolfram Alpha API Key property.
*/ */
const val WOLFRAM_APPID_KEY = "wolfram-appid" const val APPID_KEY_PROP = "wolfram-appid"
/** /**
* The Wolfram units properties * The Wolfram units properties
*/ */
const val WOLFRAM_UNITS_PROP = "wolfram-units" const val UNITS_PROP = "wolfram-units"
const val METRIC = "metric" const val METRIC = "metric"
const val IMPERIAL = "imperial" const val IMPERIAL = "imperial"
@ -137,6 +138,6 @@ class WolframAlpha : AbstractModule() {
add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas")) add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas"))
add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric")) add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric"))
} }
initProperties(WOLFRAM_APPID_KEY, WOLFRAM_UNITS_PROP) initProperties(APPID_KEY_PROP, UNITS_PROP)
} }
} }

View file

@ -34,10 +34,8 @@ package net.thauvin.erik.mobibot.modules
import assertk.assertThat import assertk.assertThat
import assertk.assertions.contains import assertk.assertions.contains
import assertk.assertions.hasNoCause import assertk.assertions.hasNoCause
import assertk.assertions.isEqualTo
import assertk.assertions.isFailure import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf import assertk.assertions.isInstanceOf
import assertk.assertions.message
import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.LocalProperties
import org.testng.annotations.Test import org.testng.annotations.Test
@ -52,7 +50,7 @@ class ChatGptTest : LocalProperties() {
@Test(groups = ["modules", "no-ci"]) @Test(groups = ["modules", "no-ci"])
fun testChat() { fun testChat() {
val apiKey = getProperty(ChatGpt.CHATGPT_API_KEY) val apiKey = getProperty(ChatGpt.API_KEY_PROP)
assertThat( assertThat(
ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100)
).contains("XMLHttpRequest") ).contains("XMLHttpRequest")

View file

@ -75,8 +75,8 @@ class GoogleSearchTest : LocalProperties() {
@Test(groups = ["no-ci", "modules"]) @Test(groups = ["no-ci", "modules"])
@Throws(ModuleException::class) @Throws(ModuleException::class)
fun testSearchGoogle() { fun testSearchGoogle() {
val apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP) val apiKey = getProperty(GoogleSearch.API_KEY_PROP)
val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP) val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP)
try { try {
var query = "mobibot" var query = "mobibot"

View file

@ -59,7 +59,7 @@ class StockQuoteTest : LocalProperties() {
@Test(groups = ["modules"]) @Test(groups = ["modules"])
@Throws(ModuleException::class) @Throws(ModuleException::class)
fun testGetQuote() { fun testGetQuote() {
val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP) val apiKey = getProperty(StockQuote.API_KEY_PROP)
try { try {
var symbol = "apple inc" var symbol = "apple inc"
val messages = getQuote(symbol, apiKey) val messages = getQuote(symbol, apiKey)

View file

@ -46,7 +46,7 @@ import assertk.assertions.prop
import net.aksingh.owmjapis.api.APIException import net.aksingh.owmjapis.api.APIException
import net.aksingh.owmjapis.core.OWM import net.aksingh.owmjapis.core.OWM
import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.LocalProperties
import net.thauvin.erik.mobibot.modules.Weather2.Companion.OWM_API_KEY_PROP import net.thauvin.erik.mobibot.modules.Weather2.Companion.API_KEY_PROP
import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather
@ -86,7 +86,7 @@ class Weather2Test : LocalProperties() {
@Throws(ModuleException::class) @Throws(ModuleException::class)
fun testWeather() { fun testWeather() {
var query = "98204" var query = "98204"
var messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) var messages = getWeather(query, getProperty(API_KEY_PROP))
assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all {
contains("Everett, United States") contains("Everett, United States")
contains("US") contains("US")
@ -94,7 +94,7 @@ class Weather2Test : LocalProperties() {
assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS") assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS")
query = "San Francisco" query = "San Francisco"
messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) messages = getWeather(query, getProperty(API_KEY_PROP))
assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all {
contains("San Francisco") contains("San Francisco")
contains("US") contains("US")
@ -102,7 +102,7 @@ class Weather2Test : LocalProperties() {
assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959") assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959")
query = "London, GB" query = "London, GB"
messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) messages = getWeather(query, getProperty(API_KEY_PROP))
assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all {
contains("London, United Kingdom") contains("London, United Kingdom")
contains("GB") contains("GB")
@ -111,7 +111,7 @@ class Weather2Test : LocalProperties() {
try { try {
query = "Foo, US" query = "Foo, US"
getWeather(query, getProperty(OWM_API_KEY_PROP)) getWeather(query, getProperty(API_KEY_PROP))
} catch (e: ModuleException) { } catch (e: ModuleException) {
assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java) assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java)
} }

View file

@ -58,7 +58,7 @@ class WolframAlphaTest : LocalProperties() {
@Test(groups = ["modules", "no-ci"]) @Test(groups = ["modules", "no-ci"])
@Throws(ModuleException::class) @Throws(ModuleException::class)
fun queryWolframTest() { fun queryWolframTest() {
val apiKey = getProperty(WolframAlpha.WOLFRAM_APPID_KEY) val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP)
try { try {
var query = "SFO to SEA" var query = "SFO to SEA"
assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles") assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles")

View file

@ -1,9 +1,9 @@
#Generated by the Semver Plugin for Gradle #Generated by the Semver Plugin for Gradle
#Thu Jan 12 00:57:56 PST 2023 #Thu Jan 12 23:03:48 PST 2023
version.buildmeta=975 version.buildmeta=982
version.major=0 version.major=0
version.minor=8 version.minor=8
version.patch=0 version.patch=0
version.prerelease=rc version.prerelease=rc
version.project=mobibot version.project=mobibot
version.semver=0.8.0-rc+975 version.semver=0.8.0-rc+982