Upgraded to Koltin 1.9.0

This commit is contained in:
Erik C. Thauvin 2023-07-06 10:23:04 -07:00
parent 5834a23b7e
commit 2313d36584
76 changed files with 549 additions and 645 deletions

View file

@ -1,6 +1,5 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<state> <state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" /> <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state> </state>
</component> </component>

2
.idea/kotlinc.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.22" /> <option name="version" value="1.9.0" />
</component> </component>
</project> </project>

View file

@ -1,7 +1,7 @@
# mobibot # mobibot
[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Kotlin](https://img.shields.io/badge/kotlin-1.8.22-7f52ff.svg)](https://kotlinlang.org) [![Kotlin](https://img.shields.io/badge/kotlin-1.9.0-7f52ff.svg)](https://kotlinlang.org)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot)
[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml)
[![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master)

View file

@ -10,8 +10,8 @@ plugins {
id 'io.gitlab.arturbosch.detekt' version '1.23.0' id 'io.gitlab.arturbosch.detekt' version '1.23.0'
id 'java' id 'java'
id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'net.thauvin.erik.gradle.semver' version '1.0.4'
id 'org.jetbrains.kotlin.jvm' version '1.8.22' id 'org.jetbrains.kotlin.jvm' version '1.9.0'
id 'org.jetbrains.kotlin.kapt' version '1.8.22' id 'org.jetbrains.kotlin.kapt' version '1.9.0'
id 'org.jetbrains.kotlinx.kover' version '0.7.2' id 'org.jetbrains.kotlinx.kover' version '0.7.2'
id 'org.sonarqube' version '4.2.1.3168' id 'org.sonarqube' version '4.2.1.3168'
id 'pmd' id 'pmd'

View file

@ -46,6 +46,15 @@
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID>
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID>
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID> <ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID>
<ID>MaxLineLength:DiceTest.kt$DiceTest$.</ID>
<ID>MaxLineLength:Lookup.kt$Lookup$("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")</ID>
<ID>MaxLineLength:Mastodon.kt$Mastodon.Companion$mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct")</ID>
<ID>MaxLineLength:Mobibot.kt$Mobibot$helpCmdSyntax("%c ${Constants.HELP_CMD} &lt;command&gt;", event.bot().nick, event is PrivateMessageEvent)</ID>
<ID>MaxLineLength:PinboardTest.kt$PinboardTest$URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&amp;tag=test&amp;" + url.encodeUrl()).reader().body</ID>
<ID>MaxLineLength:StockQuote.kt$StockQuote.Companion$+</ID>
<ID>MaxLineLength:Utils.kt$Utils$list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = "")</ID>
<ID>MaxLineLength:View.kt$View$helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent)</ID>
<ID>MaxLineLength:Weather2.kt$Weather2.Companion$country.name.replace('_', ' ').capitalizeWords()</ID>
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean</ID> <ID>NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean</ID>
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID> <ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID>
<ID>NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String</ID> <ID>NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String</ID>
@ -87,6 +96,23 @@
<ID>TooManyFunctions:EntryLink.kt$EntryLink : Serializable</ID> <ID>TooManyFunctions:EntryLink.kt$EntryLink : Serializable</ID>
<ID>TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter</ID> <ID>TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter</ID>
<ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID>
<ID>WildcardImport:AddonsTest.kt$import net.thauvin.erik.mobibot.modules.*</ID>
<ID>WildcardImport:EntryLinkTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:FeedMgrTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:FeedReaderTest.kt$import assertk.assertions.*</ID> <ID>WildcardImport:FeedReaderTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:FeedsManager.kt$import com.rometools.rome.feed.synd.*</ID>
<ID>WildcardImport:GoogleSearchTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:JokeTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:Mobibot.kt$import java.io.*</ID>
<ID>WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.*</ID>
<ID>WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.links.*</ID>
<ID>WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.modules.*</ID>
<ID>WildcardImport:Mobibot.kt$import org.pircbotx.hooks.events.*</ID>
<ID>WildcardImport:ModuleExceptionTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:SeenTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:StockQuoteTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:TellMessagesMgrTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:Utils.kt$import java.io.*</ID>
<ID>WildcardImport:Weather2Test.kt$import assertk.assertions.*</ID>
</CurrentIssues> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View file

@ -72,6 +72,12 @@ public final class War extends AbstractModule {
help.add(Utils.helpFormat("%c " + WAR_CMD)); help.add(Utils.helpFormat("%c " + WAR_CMD));
} }
@NotNull
@Override
public String getName() {
return "War";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -99,10 +105,4 @@ public final class War extends AbstractModule {
} while (i == y); } while (i == y);
} }
@NotNull
@Override
public String getName() {
return "War";
}
} }

View file

@ -38,7 +38,7 @@ import org.pircbotx.hooks.events.PrivateMessageEvent
import org.pircbotx.hooks.types.GenericMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.util.Properties import java.util.*
/** /**
* Modules and Commands addons. * Modules and Commands addons.

View file

@ -63,7 +63,7 @@ object Constants {
* User-Agent * User-Agent
*/ */
const val USER_AGENT = const val USER_AGENT =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
/** /**
* The help command. * The help command.

View file

@ -45,67 +45,24 @@ import net.thauvin.erik.mobibot.Utils.lastOrEmpty
import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.sendList
import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.sendMessage
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
import net.thauvin.erik.mobibot.commands.ChannelFeed import net.thauvin.erik.mobibot.commands.*
import net.thauvin.erik.mobibot.commands.Cycle
import net.thauvin.erik.mobibot.commands.Die
import net.thauvin.erik.mobibot.commands.Ignore
import net.thauvin.erik.mobibot.commands.Info
import net.thauvin.erik.mobibot.commands.Me
import net.thauvin.erik.mobibot.commands.Modules
import net.thauvin.erik.mobibot.commands.Msg
import net.thauvin.erik.mobibot.commands.Nick
import net.thauvin.erik.mobibot.commands.Recap
import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap
import net.thauvin.erik.mobibot.commands.Say import net.thauvin.erik.mobibot.commands.links.*
import net.thauvin.erik.mobibot.commands.Users
import net.thauvin.erik.mobibot.commands.Versions
import net.thauvin.erik.mobibot.commands.links.Comment
import net.thauvin.erik.mobibot.commands.links.LinksManager
import net.thauvin.erik.mobibot.commands.links.Posting
import net.thauvin.erik.mobibot.commands.links.Tags
import net.thauvin.erik.mobibot.commands.links.View
import net.thauvin.erik.mobibot.commands.seen.Seen import net.thauvin.erik.mobibot.commands.seen.Seen
import net.thauvin.erik.mobibot.commands.tell.Tell import net.thauvin.erik.mobibot.commands.tell.Tell
import net.thauvin.erik.mobibot.modules.Calc import net.thauvin.erik.mobibot.modules.*
import net.thauvin.erik.mobibot.modules.ChatGpt
import net.thauvin.erik.mobibot.modules.CryptoPrices
import net.thauvin.erik.mobibot.modules.CurrencyConverter
import net.thauvin.erik.mobibot.modules.Dice
import net.thauvin.erik.mobibot.modules.GoogleSearch
import net.thauvin.erik.mobibot.modules.Joke
import net.thauvin.erik.mobibot.modules.Lookup
import net.thauvin.erik.mobibot.modules.Mastodon
import net.thauvin.erik.mobibot.modules.Ping
import net.thauvin.erik.mobibot.modules.RockPaperScissors
import net.thauvin.erik.mobibot.modules.StockQuote
import net.thauvin.erik.mobibot.modules.War
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.semver.Version import net.thauvin.erik.semver.Version
import org.pircbotx.Configuration import org.pircbotx.Configuration
import org.pircbotx.PircBotX import org.pircbotx.PircBotX
import org.pircbotx.hooks.ListenerAdapter import org.pircbotx.hooks.ListenerAdapter
import org.pircbotx.hooks.events.ActionEvent import org.pircbotx.hooks.events.*
import org.pircbotx.hooks.events.DisconnectEvent
import org.pircbotx.hooks.events.JoinEvent
import org.pircbotx.hooks.events.MessageEvent
import org.pircbotx.hooks.events.NickChangeEvent
import org.pircbotx.hooks.events.PartEvent
import org.pircbotx.hooks.events.PrivateMessageEvent
import org.pircbotx.hooks.events.QuitEvent
import org.pircbotx.hooks.types.GenericMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.BufferedOutputStream import java.io.*
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.PrintStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
import java.util.Properties import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -140,9 +97,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
event.sendMessage("Type a URL on $channel to post it.") event.sendMessage("Type a URL on $channel to post it.")
event.sendMessage("For more information on a specific command, type:") event.sendMessage("For more information on a specific command, type:")
event.sendMessage( event.sendMessage(
helpFormat( helpFormat(
helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent)
) )
) )
event.sendMessage("The commands are:") event.sendMessage("The commands are:")
event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) event.sendList(addons.names.commands, 8, isBold = true, isIndent = true)
@ -204,7 +161,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
with(event.getBot<PircBotX>()) { with(event.getBot<PircBotX>()) {
if (user.nick == nick) { if (user.nick == nick) {
LinksManager.socialManager.notification( LinksManager.socialManager.notification(
"$nick has joined ${event.channel.name} on $serverHostname" "$nick has joined ${event.channel.name} on $serverHostname"
) )
seen.add(userChannelDao.getChannel(channel).users) seen.add(userChannelDao.getChannel(channel).users)
} else { } else {
@ -252,7 +209,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
with(event.getBot<PircBotX>()) { with(event.getBot<PircBotX>()) {
if (user.nick == nick) { if (user.nick == nick) {
LinksManager.socialManager.notification( LinksManager.socialManager.notification(
"$nick has left ${event.channel.name} on $serverHostname" "$nick has left ${event.channel.name} on $serverHostname"
) )
seen.add(userChannelDao.getChannel(channel).users) seen.add(userChannelDao.getChannel(channel).users)
} else { } else {
@ -275,22 +232,22 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
// Set up the command line options // Set up the command line options
val parser = ArgParser(Constants.CLI_CMD) val parser = ArgParser(Constants.CLI_CMD)
val debug by parser.option( val debug by parser.option(
ArgType.Boolean, ArgType.Boolean,
Constants.DEBUG_ARG, Constants.DEBUG_ARG,
Constants.DEBUG_ARG.substring(0, 1), Constants.DEBUG_ARG.substring(0, 1),
"Print debug & logging data directly to the console" "Print debug & logging data directly to the console"
).default(false) ).default(false)
val property by parser.option( val property by parser.option(
ArgType.String, ArgType.String,
Constants.PROPS_ARG, Constants.PROPS_ARG,
Constants.PROPS_ARG.substring(0, 1), Constants.PROPS_ARG.substring(0, 1),
"Use alternate properties file" "Use alternate properties file"
).default("./${ReleaseInfo.PROJECT}.properties") ).default("./${ReleaseInfo.PROJECT}.properties")
val version by parser.option( val version by parser.option(
ArgType.Boolean, ArgType.Boolean,
Constants.VERSION_ARG, Constants.VERSION_ARG,
Constants.VERSION_ARG.substring(0, 1), Constants.VERSION_ARG.substring(0, 1),
"Print version info" "Print version info"
).default(false) ).default(false)
// Parse the command line // Parse the command line
@ -299,8 +256,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
if (version) { if (version) {
// Output the version // Output the version
println( println(
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" +
" (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})"
) )
println(ReleaseInfo.WEBSITE) println(ReleaseInfo.WEBSITE)
} else { } else {
@ -308,7 +265,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
val p = Properties() val p = Properties()
try { try {
Files.newInputStream( Files.newInputStream(
Paths.get(property) Paths.get(property)
).use { fis -> ).use { fis ->
p.load(fis) p.load(fis)
} }
@ -327,11 +284,11 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
if (!debug) { if (!debug) {
try { try {
val stdout = PrintStream( val stdout = PrintStream(
BufferedOutputStream( BufferedOutputStream(
FileOutputStream( FileOutputStream(
logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true
) )
), true ), true
) )
System.setOut(stdout) System.setOut(stdout)
} catch (ignore: IOException) { } catch (ignore: IOException) {
@ -340,9 +297,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
} }
try { try {
val stderr = PrintStream( val stderr = PrintStream(
BufferedOutputStream( BufferedOutputStream(
FileOutputStream("$logsDir$nickname.err", true) FileOutputStream("$logsDir$nickname.err", true)
), true ), true
) )
System.setErr(stderr) System.setErr(stderr)
} catch (ignore: IOException) { } catch (ignore: IOException) {
@ -367,8 +324,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
login = p.getProperty("login", nickname) login = p.getProperty("login", nickname)
realName = p.getProperty("realname", nickname) realName = p.getProperty("realname", nickname)
addServer( addServer(
ircServer, ircServer,
p.getIntProperty("port", Constants.DEFAULT_PORT) p.getIntProperty("port", Constants.DEFAULT_PORT)
) )
addAutoJoinChannel(channel) addAutoJoinChannel(channel)
addListener(this@Mobibot) addListener(this@Mobibot)

View file

@ -37,7 +37,7 @@ import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.Date import java.util.*
/** /**
* Handles posts to pinboard.in. * Handles posts to pinboard.in.
@ -92,7 +92,7 @@ class Pinboard {
*/ */
private fun Date.toTimestamp(): String { private fun Date.toTimestamp(): String {
return ZonedDateTime.ofInstant( return ZonedDateTime.ofInstant(
toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault()
).format(DateTimeFormatter.ISO_INSTANT) ).format(DateTimeFormatter.ISO_INSTANT)
} }

View file

@ -39,11 +39,7 @@ import org.pircbotx.PircBotX
import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.events.PrivateMessageEvent
import org.pircbotx.hooks.types.GenericMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent
import org.slf4j.Logger import org.slf4j.Logger
import java.io.BufferedInputStream import java.io.*
import java.io.BufferedOutputStream
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import java.nio.file.Files import java.nio.file.Files
@ -51,8 +47,7 @@ import java.nio.file.Paths
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.Date import java.util.*
import java.util.Properties
import kotlin.io.path.exists import kotlin.io.path.exists
import kotlin.io.path.fileSize import kotlin.io.path.fileSize
@ -220,7 +215,7 @@ object Utils {
if (serialFile.exists() && serialFile.fileSize() > 0) { if (serialFile.exists() && serialFile.fileSize() > 0) {
try { try {
ObjectInputStream( ObjectInputStream(
BufferedInputStream(Files.newInputStream(serialFile)) BufferedInputStream(Files.newInputStream(serialFile))
).use { input -> ).use { input ->
if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") if (logger.isDebugEnabled) logger.debug("Loading the ${description}.")
return input.readObject() return input.readObject()
@ -307,20 +302,20 @@ object Utils {
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun GenericMessageEvent.sendList( fun GenericMessageEvent.sendList(
list: List<String>, list: List<String>,
maxPerLine: Int, maxPerLine: Int,
separator: String = " ", separator: String = " ",
isBold: Boolean = false, isBold: Boolean = false,
isIndent: Boolean = false isIndent: Boolean = false
) { ) {
var i = 0 var i = 0
while (i < list.size) { while (i < list.size) {
sendMessage( sendMessage(
helpFormat( helpFormat(
list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""),
isBold, isBold,
isIndent isIndent
), ),
) )
i += maxPerLine i += maxPerLine
} }
@ -419,8 +414,8 @@ object Utils {
fun URL.reader(): UrlReaderResponse { fun URL.reader(): UrlReaderResponse {
val connection = this.openConnection() as HttpURLConnection val connection = this.openConnection() as HttpURLConnection
connection.setRequestProperty( connection.setRequestProperty(
"User-Agent", "User-Agent",
"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0"
) )
return if (connection.responseCode.isHttpSuccess()) { return if (connection.responseCode.isHttpSuccess()) {
UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() })

View file

@ -50,15 +50,15 @@ class Ignore : AbstractCommand() {
override val name = IGNORE_CMD override val name = IGNORE_CMD
override val help = listOf( override val help = listOf(
"To ignore a link posted to the channel:", "To ignore a link posted to the channel:",
helpFormat("https://www.foo.bar %n"), helpFormat("https://www.foo.bar %n"),
"To check your ignore status:", "To check your ignore status:",
helpFormat("%c $name"), helpFormat("%c $name"),
"To toggle your ignore status:", "To toggle your ignore status:",
helpFormat("%c $name $me") helpFormat("%c $name $me")
) )
private val helpOp = help.plus( private val helpOp = help.plus(
arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]"))
) )
override val isOpOnly = false override val isOpOnly = false

View file

@ -48,8 +48,8 @@ import kotlin.time.toDuration
class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() {
private val allVersions = listOf( private val allVersions = listOf(
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})",
"Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})"
) )
override val name = "info" override val name = "info"
override val help = listOf("To view information about the bot:", helpFormat("%c $name")) override val help = listOf("To view information about the bot:", helpFormat("%c $name"))
@ -104,9 +104,9 @@ class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() {
event.sendList(allVersions, 1) event.sendList(allVersions, 1)
val info = StringBuilder() val info = StringBuilder()
info.append("Uptime: ") info.append("Uptime: ")
.append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime())
.append(" [Entries: ") .append(" [Entries: ")
.append(LinksManager.entries.links.size) .append(LinksManager.entries.links.size)
if (seen.isEnabled()) { if (seen.isEnabled()) {
info.append(", Seen: ").append(seen.count()) info.append(", Seen: ").append(seen.count())
} }

View file

@ -39,8 +39,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent
class Msg : AbstractCommand() { class Msg : AbstractCommand() {
override val name = "msg" override val name = "msg"
override val help = listOf( override val help = listOf(
"To have the bot send a private message to someone:", "To have the bot send a private message to someone:",
helpFormat("%c $name <nick> <text>") helpFormat("%c $name <nick> <text>")
) )
override val isOpOnly = true override val isOpOnly = true
override val isPublic = false override val isPublic = false

View file

@ -41,8 +41,8 @@ import java.time.LocalDateTime
class Recap : AbstractCommand() { class Recap : AbstractCommand() {
override val name = "recap" override val name = "recap"
override val help = listOf( override val help = listOf(
"To list the last 10 public channel messages:", "To list the last 10 public channel messages:",
helpFormat("%c $name") helpFormat("%c $name")
) )
override val isOpOnly = false override val isOpOnly = false
override val isPublic = true override val isPublic = true
@ -60,8 +60,8 @@ class Recap : AbstractCommand() {
@JvmStatic @JvmStatic
fun storeRecap(sender: String, message: String, isAction: Boolean) { fun storeRecap(sender: String, message: String, isAction: Boolean) {
recaps.add( recaps.add(
LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() LocalDateTime.now(Clock.systemUTC()).toUtcDateTime()
+ " - $sender" + (if (isAction) " " else ": ") + message + " - $sender" + (if (isAction) " " else ": ") + message
) )
if (recaps.size > MAX_RECAPS) { if (recaps.size > MAX_RECAPS) {
recaps.removeFirst() recaps.removeFirst()

View file

@ -40,10 +40,10 @@ import org.pircbotx.hooks.types.GenericMessageEvent
class Versions : AbstractCommand() { class Versions : AbstractCommand() {
private val allVersions = listOf( private val allVersions = listOf(
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})",
"${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" +
", JVM ${System.getProperty("java.runtime.version")}", ", JVM ${System.getProperty("java.runtime.version")}",
"Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}"
) )
override val name = "versions" override val name = "versions"
override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name"))

View file

@ -45,13 +45,13 @@ import org.pircbotx.hooks.types.GenericMessageEvent
class Comment : AbstractCommand() { class Comment : AbstractCommand() {
override val name = COMMAND override val name = COMMAND
override val help = listOf( override val help = listOf(
"To add a comment:", "To add a comment:",
helpFormat("${Constants.LINK_CMD}1:This is a comment"), helpFormat("${Constants.LINK_CMD}1:This is a comment"),
"I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1",
"To edit a comment, use its label: ", "To edit a comment, use its label: ",
helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"),
"To delete a comment, use its label and a minus sign: ", "To delete a comment, use its label and a minus sign: ",
helpFormat("${Constants.LINK_CMD}1.1:-") helpFormat("${Constants.LINK_CMD}1.1:-")
) )
override val isOpOnly = false override val isOpOnly = false
override val isPublic = true override val isPublic = true
@ -100,12 +100,12 @@ class Comment : AbstractCommand() {
} }
private fun changeAuthor( private fun changeAuthor(
channel: String, channel: String,
cmd: String, cmd: String,
entry: EntryLink, entry: EntryLink,
entryIndex: Int, entryIndex: Int,
commentIndex: Int, commentIndex: Int,
event: GenericMessageEvent event: GenericMessageEvent
) { ) {
if (event.isChannelOp(channel) && cmd.length > 1) { if (event.isChannelOp(channel) && cmd.length > 1) {
val comment = entry.getComment(commentIndex) val comment = entry.getComment(commentIndex)
@ -118,11 +118,11 @@ class Comment : AbstractCommand() {
} }
private fun deleteComment( private fun deleteComment(
channel: String, channel: String,
entry: EntryLink, entry: EntryLink,
entryIndex: Int, entryIndex: Int,
commentIndex: Int, commentIndex: Int,
event: GenericMessageEvent event: GenericMessageEvent
) { ) {
if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) {
entry.deleteComment(commentIndex) entry.deleteComment(commentIndex)
@ -134,11 +134,11 @@ class Comment : AbstractCommand() {
} }
private fun setComment( private fun setComment(
cmd: String, cmd: String,
entry: EntryLink, entry: EntryLink,
entryIndex: Int, entryIndex: Int,
commentIndex: Int, commentIndex: Int,
event: GenericMessageEvent event: GenericMessageEvent
) { ) {
entry.setComment(commentIndex, cmd, event.user.nick) entry.setComment(commentIndex, cmd, event.user.nick)
event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex)))

View file

@ -161,8 +161,8 @@ class LinksManager : AbstractCommand() {
internal fun fetchTitle(link: String): String { internal fun fetchTitle(link: String): String {
try { try {
val html = Jsoup.connect(link) val html = Jsoup.connect(link)
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0")
.get() .get()
val title = html.title() val title = html.title()
if (title.isNotBlank()) { if (title.isNotBlank()) {
return title return title
@ -178,7 +178,7 @@ class LinksManager : AbstractCommand() {
return try { return try {
val match = entries.links.single { it.link == link } val match = entries.links.single { it.link == link }
event.sendMessage( event.sendMessage(
"Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match)
) )
true true
} catch (ignore: NoSuchElementException) { } catch (ignore: NoSuchElementException) {

View file

@ -47,16 +47,16 @@ import org.pircbotx.hooks.types.GenericMessageEvent
class Posting : AbstractCommand() { class Posting : AbstractCommand() {
override val name = "posting" override val name = "posting"
override val help = listOf( override val help = listOf(
"Post a URL, by saying it on a line on its own:", "Post a URL, by saying it on a line on its own:",
helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"),
"I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1",
"To add a title, use its label and a pipe:", "To add a title, use its label and a pipe:",
helpFormat("${Constants.LINK_CMD}1:|This is the title"), helpFormat("${Constants.LINK_CMD}1:|This is the title"),
"To add a comment:", "To add a comment:",
helpFormat("${Constants.LINK_CMD}1:This is a comment"), helpFormat("${Constants.LINK_CMD}1:This is a comment"),
"I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1",
"To edit a comment, see: ", "To edit a comment, see: ",
helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}")
) )
override val isOpOnly = false override val isOpOnly = false
override val isPublic = true override val isPublic = true

View file

@ -44,8 +44,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent
class Tags : AbstractCommand() { class Tags : AbstractCommand() {
override val name = COMMAND override val name = COMMAND
override val help = listOf( override val help = listOf(
"To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:", "To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:",
helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]") helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]")
) )
override val isOpOnly = false override val isOpOnly = false
override val isPublic = true override val isPublic = true

View file

@ -46,8 +46,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent
class View : AbstractCommand() { class View : AbstractCommand() {
override val name = VIEW_CMD override val name = VIEW_CMD
override val help = listOf( override val help = listOf(
"To list or search the current URL posts:", "To list or search the current URL posts:",
helpFormat("%c $name [<start>] [<query>]") helpFormat("%c $name [<start>] [<query>]")
) )
override val isOpOnly = false override val isOpOnly = false
override val isPublic = true override val isPublic = true
@ -107,9 +107,9 @@ class View : AbstractCommand() {
if (sent == MAX_ENTRIES && index < entries.links.size) { if (sent == MAX_ENTRIES && index < entries.links.size) {
event.sendMessage("To view more, try: ") event.sendMessage("To view more, try: ")
event.sendMessage( event.sendMessage(
helpFormat( helpFormat(
helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent)
) )
) )
} }
} }

View file

@ -58,7 +58,7 @@ class Seen(private val serialObject: String) : AbstractCommand() {
override val name = "seen" override val name = "seen"
override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>"))
private val helpOp = help.plus( private val helpOp = help.plus(
arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword"))
) )
override val isOpOnly = false override val isOpOnly = false
override val isPublic = true override val isPublic = true
@ -130,12 +130,12 @@ class Seen(private val serialObject: String) : AbstractCommand() {
if (isEnabled()) { if (isEnabled()) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
seenNicks.putAll( seenNicks.putAll(
loadSerialData( loadSerialData(
serialObject, serialObject,
TreeMap<String, SeenNick>(), TreeMap<String, SeenNick>(),
logger, logger,
"seen nicknames" "seen nicknames"
) as TreeMap<String, SeenNick> ) as TreeMap<String, SeenNick>
) )
} }
} }

View file

@ -66,11 +66,11 @@ class Tell(private val serialObject: String) : AbstractCommand() {
override val name = "tell" override val name = "tell"
override val help = listOf( override val help = listOf(
"To send a message to someone when they join the channel:", "To send a message to someone when they join the channel:",
helpFormat("%c $name <nick> <message>"), helpFormat("%c $name <nick> <message>"),
"To view queued and sent messages:", "To view queued and sent messages:",
helpFormat("%c $name ${View.VIEW_CMD}"), helpFormat("%c $name ${View.VIEW_CMD}"),
"Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.'
) )
override val isOpOnly: Boolean = false override val isOpOnly: Boolean = false
override val isPublic: Boolean = isEnabled() override val isPublic: Boolean = isEnabled()
@ -118,9 +118,9 @@ class Tell(private val serialObject: String) : AbstractCommand() {
} }
} else { } else {
if (messages.removeIf { if (messages.removeIf {
it.id == id && it.id == id &&
(it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel))
}) { }) {
save() save()
event.sendMessage("The message was deleted from the queue.") event.sendMessage("The message was deleted from the queue.")
} else { } else {
@ -180,7 +180,7 @@ class Tell(private val serialObject: String) : AbstractCommand() {
if (message.sender == nickname) { if (message.sender == nickname) {
if (event !is MessageEvent) { if (event !is MessageEvent) {
event.user.send().message( event.user.send().message(
"${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}"
) )
message.isReceived = true message.isReceived = true
message.isNotified = true message.isNotified = true
@ -188,17 +188,17 @@ class Tell(private val serialObject: String) : AbstractCommand() {
} }
} else { } else {
event.user.send().message( event.user.send().message(
"${message.sender} wanted me to tell you: ${message.message.reverseColor()}" "${message.sender} wanted me to tell you: ${message.message.reverseColor()}"
) )
message.isReceived = true message.isReceived = true
save() save()
} }
} else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived
&& !message.isNotified && !message.isNotified
) { ) {
event.user.send().message( event.user.send().message(
"Your message ${"[ID ${message.id}]".reverseColor()} was sent to " "Your message ${"[ID ${message.id}]".reverseColor()} was sent to "
+ "${message.recipient.bold()} on ${message.receptionDate}" + "${message.recipient.bold()} on ${message.receptionDate}"
) )
message.isNotified = true message.isNotified = true
save() save()
@ -219,8 +219,8 @@ class Tell(private val serialObject: String) : AbstractCommand() {
if (messages.isNotEmpty()) { if (messages.isNotEmpty()) {
for (message in messages) { for (message in messages) {
event.sendMessage( event.sendMessage(
"${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " +
(if (message.isReceived) "DELIVERED]" else "QUEUED]") (if (message.isReceived) "DELIVERED]" else "QUEUED]")
) )
} }
} else { } else {
@ -238,13 +238,13 @@ class Tell(private val serialObject: String) : AbstractCommand() {
} }
if (message.isReceived) { if (message.isReceived) {
event.sendMessage( event.sendMessage(
message.sender.bold() + ARROW + message.recipient.bold() + message.sender.bold() + ARROW + message.recipient.bold() +
" [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]"
) )
} else { } else {
event.sendMessage( event.sendMessage(
message.sender.bold() + ARROW + message.recipient.bold() + message.sender.bold() + ARROW + message.recipient.bold() +
" [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]"
) )
} }
event.sendMessage(helpFormat(message.message)) event.sendMessage(helpFormat(message.message))
@ -254,9 +254,9 @@ class Tell(private val serialObject: String) : AbstractCommand() {
} else { } else {
event.sendMessage("To delete one or all delivered messages:") event.sendMessage("To delete one or all delivered messages:")
event.sendMessage( event.sendMessage(
helpFormat( helpFormat(
helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true)
) )
) )
event.sendMessage(help.last()) event.sendMessage(help.last())
} }

View file

@ -39,20 +39,20 @@ import java.time.format.DateTimeFormatter
* Tell Message. * Tell Message.
*/ */
class TellMessage( class TellMessage(
/** /**
* Returns the message's sender. * Returns the message's sender.
*/ */
val sender: String, val sender: String,
/** /**
* Returns the message's recipient. * Returns the message's recipient.
*/ */
val recipient: String, val recipient: String,
/** /**
* Returns the message text. * Returns the message text.
*/ */
val message: String val message: String
) : Serializable { ) : Serializable {
/** /**
* Returns the queued date/time. * Returns the queued date/time.

View file

@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.entries
import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.today
class Entries( class Entries(
var channel: String = "", var channel: String = "",
var ircServer: String = "", var ircServer: String = "",
var logsDir: String = "", var logsDir: String = "",
var backlogs: String = "" var backlogs: String = ""
) { ) {
val links = mutableListOf<EntryLink>() val links = mutableListOf<EntryLink>()

View file

@ -43,7 +43,7 @@ object EntriesUtils {
*/ */
@JvmStatic @JvmStatic
fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String =
("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}")
/** /**
* Prints an entry's link for display on the channel. * Prints an entry's link for display on the channel.
@ -52,7 +52,7 @@ object EntriesUtils {
@JvmOverloads @JvmOverloads
fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String {
val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ")
.append('[').append(entry.nick).append(']') .append('[').append(entry.nick).append(']')
if (isView && entry.comments.isNotEmpty()) { if (isView && entry.comments.isNotEmpty()) {
buff.append("[+").append(entry.comments.size).append(']') buff.append("[+").append(entry.comments.size).append(']')
} }
@ -73,7 +73,7 @@ object EntriesUtils {
*/ */
@JvmStatic @JvmStatic
fun printTags(entryIndex: Int, entry: EntryLink): String = fun printTags(entryIndex: Int, entry: EntryLink): String =
entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ")
/** /**
* Builds link label based on its index. e.g: L1 * Builds link label based on its index. e.g: L1

View file

@ -34,47 +34,46 @@ import com.rometools.rome.feed.synd.SyndCategory
import com.rometools.rome.feed.synd.SyndCategoryImpl import com.rometools.rome.feed.synd.SyndCategoryImpl
import net.thauvin.erik.mobibot.commands.links.LinksManager import net.thauvin.erik.mobibot.commands.links.LinksManager
import java.io.Serializable import java.io.Serializable
import java.util.Calendar import java.util.*
import java.util.Date
/** /**
* The class used to store link entries. * The class used to store link entries.
*/ */
class EntryLink( class EntryLink(
// Link's comments // Link's comments
val comments: MutableList<EntryComment> = mutableListOf(), val comments: MutableList<EntryComment> = mutableListOf(),
// Tags/categories // Tags/categories
val tags: MutableList<SyndCategory> = mutableListOf(), val tags: MutableList<SyndCategory> = mutableListOf(),
// Channel // Channel
var channel: String, var channel: String,
// Creation date // Creation date
var date: Date = Calendar.getInstance().time, var date: Date = Calendar.getInstance().time,
// Link's URL // Link's URL
var link: String, var link: String,
// Author's login // Author's login
var login: String = "", var login: String = "",
// Author's nickname // Author's nickname
var nick: String, var nick: String,
// Link's title // Link's title
var title: String var title: String
) : Serializable { ) : Serializable {
/** /**
* Creates a new entry. * Creates a new entry.
*/ */
constructor( constructor(
link: String, link: String,
title: String, title: String,
nick: String, nick: String,
login: String, login: String,
channel: String, channel: String,
tags: List<String?> tags: List<String?>
) : this(link = link, title = title, nick = nick, login = login, channel = channel) { ) : this(link = link, title = title, nick = nick, login = login, channel = channel) {
setTags(tags) setTags(tags)
} }
@ -83,12 +82,12 @@ class EntryLink(
* Creates a new entry. * Creates a new entry.
*/ */
constructor( constructor(
link: String, link: String,
title: String, title: String,
nick: String, nick: String,
channel: String, channel: String,
date: Date, date: Date,
tags: List<SyndCategory> tags: List<SyndCategory>
) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) {
this.tags.addAll(tags) this.tags.addAll(tags)
} }

View file

@ -30,11 +30,7 @@
*/ */
package net.thauvin.erik.mobibot.entries package net.thauvin.erik.mobibot.entries
import com.rometools.rome.feed.synd.SyndContentImpl import com.rometools.rome.feed.synd.*
import com.rometools.rome.feed.synd.SyndEntry
import com.rometools.rome.feed.synd.SyndEntryImpl
import com.rometools.rome.feed.synd.SyndFeed
import com.rometools.rome.feed.synd.SyndFeedImpl
import com.rometools.rome.io.FeedException import com.rometools.rome.io.FeedException
import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.SyndFeedInput
import com.rometools.rome.io.SyndFeedOutput import com.rometools.rome.io.SyndFeedOutput
@ -48,7 +44,7 @@ import java.io.OutputStreamWriter
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
import java.util.Calendar import java.util.*
import kotlin.io.path.exists import kotlin.io.path.exists
/** /**
@ -76,7 +72,7 @@ class FeedsManager private constructor() {
if (xml.exists()) { if (xml.exists()) {
val input = SyndFeedInput() val input = SyndFeedInput()
InputStreamReader( InputStreamReader(
Files.newInputStream(xml), StandardCharsets.UTF_8 Files.newInputStream(xml), StandardCharsets.UTF_8
).use { reader -> ).use { reader ->
val feed = input.build(reader) val feed = input.build(reader)
pubDate = feed.publishedDate.toIsoLocalDate() pubDate = feed.publishedDate.toIsoLocalDate()
@ -85,12 +81,12 @@ class FeedsManager private constructor() {
for (i in items.indices.reversed()) { for (i in items.indices.reversed()) {
with(items[i]) { with(items[i]) {
entry = EntryLink( entry = EntryLink(
link, link,
title, title,
author.substring(author.lastIndexOf('(') + 1, author.length - 1), author.substring(author.lastIndexOf('(') + 1, author.length - 1),
entries.channel, entries.channel,
publishedDate, publishedDate,
categories categories
) )
var split: List<String> var split: List<String>
for (comment in description.value.split("<br/>")) { for (comment in description.value.split("<br/>")) {
@ -123,7 +119,7 @@ class FeedsManager private constructor() {
val items: MutableList<SyndEntry> = mutableListOf() val items: MutableList<SyndEntry> = mutableListOf()
var item: SyndEntry var item: SyndEntry
OutputStreamWriter( OutputStreamWriter(
Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8
).use { fw -> ).use { fw ->
with(rss) { with(rss) {
feedType = "rss_2.0" feedType = "rss_2.0"
@ -138,13 +134,13 @@ class FeedsManager private constructor() {
with(entries.links[i]) { with(entries.links[i]) {
buff.setLength(0) buff.setLength(0)
buff.append("Posted by <b>") buff.append("Posted by <b>")
.append(nick) .append(nick)
.append("</b> on <a href=\"irc://") .append("</b> on <a href=\"irc://")
.append(entries.ircServer).append('/') .append(entries.ircServer).append('/')
.append(channel) .append(channel)
.append("\"><b>") .append("\"><b>")
.append(channel) .append(channel)
.append("</b></a>") .append("</b></a>")
if (comments.size > 0) { if (comments.size > 0) {
buff.append(" <br/><br/>") buff.append(" <br/><br/>")
for (j in comments.indices) { for (j in comments.indices) {
@ -169,11 +165,11 @@ class FeedsManager private constructor() {
output.output(rss, fw) output.output(rss, fw)
} }
OutputStreamWriter( OutputStreamWriter(
Files.newOutputStream( Files.newOutputStream(
Paths.get( Paths.get(
entries.logsDir + today() + dotXml entries.logsDir + today() + dotXml
) )
), StandardCharsets.UTF_8 ), StandardCharsets.UTF_8
).use { fw -> output.output(rss, fw) } ).use { fw -> output.output(rss, fw) }
} catch (e: FeedException) { } catch (e: FeedException) {
if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e) if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e)

View file

@ -55,8 +55,10 @@ class ChatGpt : 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 answer = chat(args.trim(), properties[API_KEY_PROP], val answer = chat(
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()) args.trim(), properties[API_KEY_PROP],
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()
)
if (answer.isNotBlank()) { if (answer.isNotBlank()) {
event.sendMessage(WordUtils.wrap(answer, 400)) event.sendMessage(WordUtils.wrap(answer, 400))
} else { } else {
@ -105,13 +107,13 @@ class ChatGpt : AbstractModule() {
if (!apiKey.isNullOrEmpty()) { if (!apiKey.isNullOrEmpty()) {
val prompt = JSONWriter.valueToString("Q:$query\nA:") val prompt = JSONWriter.valueToString("Q:$query\nA:")
val request = HttpRequest.newBuilder() val request = HttpRequest.newBuilder()
.uri(URI.create(API_URL)) .uri(URI.create(API_URL))
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("Authorization", "Bearer $apiKey") .header("Authorization", "Bearer $apiKey")
.header("User-Agent", Constants.USER_AGENT) .header("User-Agent", Constants.USER_AGENT)
.POST( .POST(
HttpRequest.BodyPublishers.ofString( HttpRequest.BodyPublishers.ofString(
"""{ """{
"model": "text-davinci-003", "model": "text-davinci-003",
"prompt": $prompt, "prompt": $prompt,
"temperature": 0, "temperature": 0,
@ -120,9 +122,9 @@ class ChatGpt : AbstractModule() {
"frequency_penalty": 0, "frequency_penalty": 0,
"presence_penalty": 0 "presence_penalty": 0
}""".trimIndent() }""".trimIndent()
)
) )
) .build()
.build()
try { try {
val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
@ -132,16 +134,16 @@ 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_CMD($query): JSON", "$CHATGPT_CMD($query): JSON",
"A JSON error has occurred while conversing with $CHATGPT_NAME.", "A JSON error has occurred while conversing with $CHATGPT_NAME.",
e e
) )
} }
} else { } else {
if (response.statusCode() == 429) { if (response.statusCode() == 429) {
throw ModuleException( throw ModuleException(
"$CHATGPT_CMD($query): Rate limit reached", "$CHATGPT_CMD($query): Rate limit reached",
"Rate limit reached. Please try again later." "Rate limit reached. Please try again later."
) )
} else { } else {
throw IOException("HTTP Status Code: " + response.statusCode()) throw IOException("HTTP Status Code: " + response.statusCode())
@ -149,9 +151,9 @@ class ChatGpt : AbstractModule() {
} }
} catch (e: IOException) { } catch (e: IOException) {
throw ModuleException( throw ModuleException(
"$CHATGPT_CMD($query): IO", "$CHATGPT_CMD($query): IO",
"An IO error has occurred while conversing with $CHATGPT_NAME.", "An IO error has occurred while conversing with $CHATGPT_NAME.",
e e
) )
} }
} else { } else {

View file

@ -134,9 +134,9 @@ class CryptoPrices : AbstractModule() {
} }
} catch (e: CryptoException) { } catch (e: CryptoException) {
throw ModuleException( throw ModuleException(
"loadCurrencies(): CE", "loadCurrencies(): CE",
"An error has occurred while retrieving the currencies table.", "An error has occurred while retrieving the currencies table.",
e e
) )
} }
} }

View file

@ -45,7 +45,7 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
import java.util.TreeMap import java.util.*
/** /**
@ -99,15 +99,15 @@ class CurrencyConverter : AbstractModule() {
event.sendMessage("To convert from one currency to another:") event.sendMessage("To convert from one currency to another:")
event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)))
event.sendMessage( event.sendMessage(
helpFormat( helpFormat(
helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled)
) )
) )
event.sendMessage("To list the supported currency codes:") event.sendMessage("To list the supported currency codes:")
event.sendMessage( event.sendMessage(
helpFormat( helpFormat(
helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled)
) )
) )
} }
return true return true
@ -146,7 +146,7 @@ class CurrencyConverter : AbstractModule() {
if (json.getBoolean("success")) { if (json.getBoolean("success")) {
PublicMessage( PublicMessage(
"${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}" "${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}"
) )
} else { } else {
ErrorMessage("Sorry, an error occurred while converting the currencies.") ErrorMessage("Sorry, an error occurred while converting the currencies.")
@ -178,9 +178,9 @@ class CurrencyConverter : AbstractModule() {
} }
} catch (e: IOException) { } catch (e: IOException) {
throw ModuleException( throw ModuleException(
"loadCodes(): IOE", "loadCodes(): IOE",
"An IO error has occurred while retrieving the currencies.", "An IO error has occurred while retrieving the currencies.",
e e
) )
} }
} }

View file

@ -65,10 +65,10 @@ class GoogleSearch : AbstractModule() {
if (args.isNotBlank()) { if (args.isNotBlank()) {
try { try {
val results = searchGoogle( val results = searchGoogle(
args, args,
properties[API_KEY_PROP], properties[API_KEY_PROP],
properties[CSE_KEY_PROP], properties[CSE_KEY_PROP],
event.user.nick event.user.nick
) )
for (msg in results) { for (msg in results) {
if (msg.isError) { if (msg.isError) {
@ -104,23 +104,23 @@ class GoogleSearch : AbstractModule() {
@JvmStatic @JvmStatic
@Throws(ModuleException::class) @Throws(ModuleException::class)
fun searchGoogle( fun searchGoogle(
query: String, query: String,
apiKey: String?, apiKey: String?,
cseKey: String?, cseKey: String?,
quotaUser: String = ReleaseInfo.PROJECT quotaUser: String = ReleaseInfo.PROJECT
): List<Message> { ): List<Message> {
if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) {
throw ModuleException( throw ModuleException(
"${GoogleSearch::class.java.name} is disabled.", "${GoogleSearch::class.java.name} is disabled.",
"${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing."
) )
} }
val results = mutableListOf<Message>() val results = mutableListOf<Message>()
if (query.isNotBlank()) { if (query.isNotBlank()) {
try { try {
val url = URL( val url = URL(
"https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" +
"&quotaUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" "&quotaUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json"
) )
val json = JSONObject(url.reader().body) val json = JSONObject(url.reader().body)
if (json.has("items")) { if (json.has("items")) {
@ -141,9 +141,9 @@ class GoogleSearch : AbstractModule() {
throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e)
} catch (e: JSONException) { } catch (e: JSONException) {
throw ModuleException( throw ModuleException(
"searchGoogle($query): JSON", "searchGoogle($query): JSON",
"A JSON error has occurred searching Google.", "A JSON error has occurred searching Google.",
e e
) )
} }
} else { } else {

View file

@ -55,9 +55,9 @@ class Lookup : AbstractModule() {
event.respondWith(nslookup(args).prependIndent()) event.respondWith(nslookup(args).prependIndent())
} catch (ignore: UnknownHostException) { } catch (ignore: UnknownHostException) {
if (args.matches( if (args.matches(
("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")
.toRegex() .toRegex()
) )
) { ) {
try { try {
val lines = whois(args) val lines = whois(args)

View file

@ -65,7 +65,7 @@ class Mastodon : SocialModule() {
private fun formatTags(entry: EntryLink): String { private fun formatTags(entry: EntryLink): String {
return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) }
.joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" }
} }
/** /**
@ -74,11 +74,11 @@ class Mastodon : SocialModule() {
@Throws(ModuleException::class) @Throws(ModuleException::class)
override fun post(message: String, isDm: Boolean): String { override fun post(message: String, isDm: Boolean): String {
return toot( return toot(
apiKey = properties[ACCESS_TOKEN_PROP], apiKey = properties[ACCESS_TOKEN_PROP],
instance = properties[INSTANCE_PROP], instance = properties[INSTANCE_PROP],
handle = handle, handle = handle,
message = message, message = message,
isDm = isDm isDm = isDm
) )
} }
@ -99,21 +99,21 @@ class Mastodon : SocialModule() {
@Throws(ModuleException::class) @Throws(ModuleException::class)
fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String {
val request = HttpRequest.newBuilder() val request = HttpRequest.newBuilder()
.uri(URI.create("https://$instance/api/v1/statuses")) .uri(URI.create("https://$instance/api/v1/statuses"))
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("Authorization", "Bearer $apiKey") .header("Authorization", "Bearer $apiKey")
.POST( .POST(
HttpRequest.BodyPublishers.ofString( HttpRequest.BodyPublishers.ofString(
JSONWriter.valueToString( JSONWriter.valueToString(
if (isDm) { if (isDm) {
mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct")
} else { } else {
mapOf("status" to message) mapOf("status" to message)
} }
) )
)
) )
) .build()
.build()
try { try {
val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() == 200) { if (response.statusCode() == 200) {

View file

@ -34,9 +34,9 @@ package net.thauvin.erik.mobibot.modules
* The `ModuleException` class. * The `ModuleException` class.
*/ */
class ModuleException @JvmOverloads constructor( class ModuleException @JvmOverloads constructor(
val debugMessage: String, val debugMessage: String,
message: String? = null, message: String? = null,
cause: Throwable? = null cause: Throwable? = null
) : Exception(message, cause) { ) : Exception(message, cause) {
companion object { companion object {
private const val serialVersionUID = 1L private const val serialVersionUID = 1L

View file

@ -50,18 +50,18 @@ class Ping : AbstractModule() {
*/ */
@JvmField @JvmField
val PINGS = listOf( val PINGS = listOf(
"is barely alive.", "is barely alive.",
"is trying to stay awake.", "is trying to stay awake.",
"has gone fishing.", "has gone fishing.",
"is somewhere over the rainbow.", "is somewhere over the rainbow.",
"has fallen and can't get up.", "has fallen and can't get up.",
"is running. You better go chase it.", "is running. You better go chase it.",
"has just spontaneously combusted.", "has just spontaneously combusted.",
"is talking to itself... don't interrupt. That's rude.", "is talking to itself... don't interrupt. That's rude.",
"is bartending at an AA meeting.", "is bartending at an AA meeting.",
"is hibernating.", "is hibernating.",
"is saving energy: apathetic mode activated.", "is saving energy: apathetic mode activated.",
"is busy. Go away!" "is busy. Go away!"
) )
@JvmStatic @JvmStatic

View file

@ -52,10 +52,10 @@ class RockPaperScissors : AbstractModule() {
with(help) { with(help) {
add("To play Rock Paper Scissors:") add("To play Rock Paper Scissors:")
add( add(
helpFormat( helpFormat(
"%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}"
+ " | ${Hands.SCISSORS.name.lowercase()}" + " | ${Hands.SCISSORS.name.lowercase()}"
) )
) )
} }
} }
@ -96,7 +96,7 @@ class RockPaperScissors : AbstractModule() {
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
val hand = Hands.valueOf(cmd.uppercase()) val hand = Hands.valueOf(cmd.uppercase())
val botHand = Hands.values()[(0..Hands.values().size).random()] val botHand = Hands.entries[(0..Hands.entries.size).random()]
when { when {
hand == botHand -> { hand == botHand -> {
event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.") event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.")

View file

@ -132,8 +132,8 @@ class StockQuote : AbstractModule() {
fun getQuote(symbol: String, apiKey: String?): List<Message> { fun getQuote(symbol: String, apiKey: String?): List<Message> {
if (apiKey.isNullOrBlank()) { if (apiKey.isNullOrBlank()) {
throw ModuleException( throw ModuleException(
"${StockQuote::class.java.name} is disabled.", "${StockQuote::class.java.name} is disabled.",
"${STOCK_CMD.capitalise()} is disabled. The API key is missing." "${STOCK_CMD.capitalise()} is disabled. The API key is missing."
) )
} }
val messages = mutableListOf<Message>() val messages = mutableListOf<Message>()
@ -144,8 +144,8 @@ class StockQuote : AbstractModule() {
with(messages) { with(messages) {
// Search for symbol/keywords // Search for symbol/keywords
response = URL( response = URL(
"${API_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)
val symbols = json.getJSONArray("bestMatches") val symbols = json.getJSONArray("bestMatches")
@ -156,9 +156,9 @@ class StockQuote : AbstractModule() {
// Get quote for symbol // Get quote for symbol
response = URL( response = URL(
"${API_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
json = getJsonResponse(response, debugMessage) json = getJsonResponse(response, debugMessage)
val quote = json.getJSONObject("Global Quote") val quote = json.getJSONObject("Global Quote")
@ -167,50 +167,50 @@ class StockQuote : AbstractModule() {
} else { } else {
add( add(
PublicMessage( PublicMessage(
"Symbol: " + quote.getString("01. symbol").unescapeXml() "Symbol: " + quote.getString("01. symbol").unescapeXml()
+ " [" + symbolInfo.getString("2. name").unescapeXml() + ']' + " [" + symbolInfo.getString("2. name").unescapeXml() + ']'
) )
) )
val pad = 10 val pad = 10
add( add(
PublicMessage( PublicMessage(
"Price:".padEnd(pad).prependIndent() "Price:".padEnd(pad).prependIndent()
+ quote.getString("05. price").unescapeXml() + quote.getString("05. price").unescapeXml()
) )
) )
add( add(
PublicMessage( PublicMessage(
"Previous:".padEnd(pad).prependIndent() "Previous:".padEnd(pad).prependIndent()
+ quote.getString("08. previous close").unescapeXml() + quote.getString("08. previous close").unescapeXml()
) )
) )
val data = arrayOf( val data = arrayOf(
"Open" to "02. open", "Open" to "02. open",
"High" to "03. high", "High" to "03. high",
"Low" to "04. low", "Low" to "04. low",
"Volume" to "06. volume", "Volume" to "06. volume",
"Latest" to "07. latest trading day" "Latest" to "07. latest trading day"
) )
data.forEach { data.forEach {
add( add(
NoticeMessage( NoticeMessage(
"${it.first}:".padEnd(pad).prependIndent() "${it.first}:".padEnd(pad).prependIndent()
+ quote.getString(it.second).unescapeXml() + quote.getString(it.second).unescapeXml()
) )
) )
} }
add( add(
NoticeMessage( NoticeMessage(
"Change:".padEnd(pad).prependIndent() "Change:".padEnd(pad).prependIndent()
+ quote.getString("09. change").unescapeXml() + quote.getString("09. change").unescapeXml()
+ " [" + quote.getString("10. change percent").unescapeXml() + ']' + " [" + quote.getString("10. change percent").unescapeXml() + ']'
) )
) )
} }
} }

View file

@ -104,7 +104,7 @@ class Weather2 : AbstractModule() {
* Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found. * Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found.
*/ */
fun getCountry(countryCode: String): Country { fun getCountry(countryCode: String): Country {
for (c in Country.values()) { for (c in Country.entries) {
if (c.value.equals(countryCode, ignoreCase = true)) { if (c.value.equals(countryCode, ignoreCase = true)) {
return c return c
} }
@ -120,8 +120,8 @@ class Weather2 : AbstractModule() {
fun getWeather(query: String, apiKey: String?): List<Message> { fun getWeather(query: String, apiKey: String?): List<Message> {
if (apiKey.isNullOrBlank()) { if (apiKey.isNullOrBlank()) {
throw ModuleException( throw ModuleException(
"${Weather2::class.java.name} is disabled.", "${Weather2::class.java.name} is disabled.",
"${WEATHER_CMD.capitalise()} is disabled. The API key is missing." "${WEATHER_CMD.capitalise()} is disabled. The API key is missing."
) )
} }
val owm = OWM(apiKey) val owm = OWM(apiKey)
@ -145,10 +145,10 @@ class Weather2 : AbstractModule() {
} }
if (cwd.hasCityName()) { if (cwd.hasCityName()) {
messages.add( messages.add(
PublicMessage( PublicMessage(
"City: ${cwd.cityName}, " + "City: ${cwd.cityName}, " +
country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]"
) )
) )
cwd.mainData?.let { cwd.mainData?.let {
with(it) { with(it) {
@ -181,8 +181,8 @@ class Weather2 : AbstractModule() {
for (w in it) { for (w in it) {
w?.let { w?.let {
condition.append(' ') condition.append(' ')
.append(w.getDescription().capitalise()) .append(w.getDescription().capitalise())
.append('.') .append('.')
} }
} }
messages.add(NoticeMessage(condition.toString())) messages.add(NoticeMessage(condition.toString()))
@ -192,15 +192,15 @@ class Weather2 : AbstractModule() {
cwd.cityId?.let { cwd.cityId?.let {
if (it > 0) { if (it > 0) {
messages.add( messages.add(
NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN)
) )
} else { } else {
messages.add( messages.add(
NoticeMessage( NoticeMessage(
"https://openweathermap.org/find?q=" "https://openweathermap.org/find?q="
+ "$city,${code.uppercase()}".encodeUrl(), + "$city,${code.uppercase()}".encodeUrl(),
Colors.GREEN Colors.GREEN
) )
) )
} }
} }
@ -209,9 +209,9 @@ class Weather2 : AbstractModule() {
} catch (e: APIException) { } catch (e: APIException) {
if (e.code == 404) { if (e.code == 404) {
throw ModuleException( throw ModuleException(
"getWeather($query): API ${e.code}", "getWeather($query): API ${e.code}",
"The requested city was not found.", "The requested city was not found.",
e e
) )
} else { } else {
throw ModuleException("getWeather($query): API ${e.code}", e.message, e) throw ModuleException("getWeather($query): API ${e.code}", e.message, e)

View file

@ -60,15 +60,15 @@ class WolframAlpha : AbstractModule() {
try { try {
val query = args.trim().split("units=", limit = 2, ignoreCase = true) val query = args.trim().split("units=", limit = 2, ignoreCase = true)
event.sendMessage( event.sendMessage(
queryWolfram( queryWolfram(
query[0].trim(), query[0].trim(),
units = if (query.size == 2) { units = if (query.size == 2) {
getUnits(query[1].trim()) getUnits(query[1].trim())
} else { } else {
getUnits(properties[UNITS_PROP]) getUnits(properties[UNITS_PROP])
}, },
appId = properties[APPID_KEY_PROP] appId = properties[APPID_KEY_PROP]
) )
) )
} catch (e: ModuleException) { } catch (e: ModuleException) {
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
@ -111,15 +111,15 @@ class WolframAlpha : AbstractModule() {
return urlReader.body return urlReader.body
} else { } else {
throw ModuleException( throw ModuleException(
"wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ",
urlReader.body.ifEmpty { urlReader.body.ifEmpty {
"Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})"
} }
) )
} }
} catch (ioe: IOException) { } catch (ioe: IOException) {
throw ModuleException( throw ModuleException(
"wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe
) )
} }
} else { } else {

View file

@ -322,7 +322,7 @@ class WorldTime : AbstractModule() {
put("ZULU", "Zulu") put("ZULU", "Zulu")
put("ZW", "Africa/Harare") put("ZW", "Africa/Harare")
ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) }
.forEach { tz -> put(tz, tz) } .forEach { tz -> put(tz, tz) }
} }
// The Time command // The Time command
@ -336,7 +336,7 @@ class WorldTime : AbstractModule() {
// Date/Time Format // Date/Time Format
private var dtf = private var dtf =
DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '")
/** /**
* Returns the current Internet (beat) Time. * Returns the current Internet (beat) Time.

View file

@ -34,4 +34,4 @@ package net.thauvin.erik.mobibot.msg
* The `ErrorMessage` class. * The `ErrorMessage` class.
*/ */
class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
Message(msg, color, isError = true) Message(msg, color, isError = true)

View file

@ -36,11 +36,11 @@ import net.thauvin.erik.semver.Constants
* The `Message` class. * The `Message` class.
*/ */
open class Message @JvmOverloads constructor( open class Message @JvmOverloads constructor(
var msg: String, var msg: String,
var color: String = DEFAULT_COLOR, var color: String = DEFAULT_COLOR,
var isNotice: Boolean = false, var isNotice: Boolean = false,
isError: Boolean = false, isError: Boolean = false,
var isPrivate: Boolean = false var isPrivate: Boolean = false
) { ) {
companion object { companion object {
var DEFAULT_COLOR = Constants.EMPTY var DEFAULT_COLOR = Constants.EMPTY

View file

@ -34,5 +34,5 @@ package net.thauvin.erik.mobibot.msg
* The `NoticeMessage` class. * The `NoticeMessage` class.
*/ */
class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
Message(msg, color, isNotice = true) Message(msg, color, isNotice = true)

View file

@ -33,6 +33,5 @@ package net.thauvin.erik.mobibot.msg
/** /**
* The `PrivateMessage` class. * The `PrivateMessage` class.
*/ */
@Suppress("unused")
class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
Message(msg, color, isPrivate = true) Message(msg, color, isPrivate = true)

View file

@ -36,7 +36,7 @@ import net.thauvin.erik.mobibot.Constants
import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.util.Timer import java.util.*
/** /**
* Social Manager. * Social Manager.

View file

@ -76,8 +76,8 @@ abstract class SocialModule : AbstractModule() {
post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) post(message = formatEntry(LinksManager.entries.links[index]), isDm = false)
} catch (e: ModuleException) { } catch (e: ModuleException) {
if (logger.isWarnEnabled) logger.warn( if (logger.isWarnEnabled) logger.warn(
"Failed to post entry ${index.toLinkLabel()} on $name.", "Failed to post entry ${index.toLinkLabel()} on $name.",
e e
) )
} }
} }

View file

@ -31,7 +31,7 @@
package net.thauvin.erik.mobibot.social package net.thauvin.erik.mobibot.social
import java.util.TimerTask import java.util.*
class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() { class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() {
override fun run() { override fun run() {

View file

@ -41,13 +41,9 @@ import net.thauvin.erik.mobibot.commands.Die
import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.Ignore
import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.Comment
import net.thauvin.erik.mobibot.commands.links.View import net.thauvin.erik.mobibot.commands.links.View
import net.thauvin.erik.mobibot.modules.Dice import net.thauvin.erik.mobibot.modules.*
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.War
import org.testng.annotations.Test import org.testng.annotations.Test
import java.util.Properties import java.util.*
class AddonsTest { class AddonsTest {
private val p = Properties().apply { private val p = Properties().apply {
@ -80,11 +76,11 @@ class AddonsTest {
assertThat(addons.names.ops, "names.ops").containsExactly("cycle") assertThat(addons.names.ops, "names.ops").containsExactly("cycle")
assertThat(addons.names.commands, "names.command").containsExactly( assertThat(addons.names.commands, "names.command").containsExactly(
"joke", "joke",
"rock", "rock",
"paper", "paper",
"scissors", "scissors",
"ignore" "ignore"
) )
} }
} }

View file

@ -46,9 +46,9 @@ object ExceptionSanitizer {
with(this) { with(this) {
if (!cause?.message.isNullOrBlank()) { if (!cause?.message.isNullOrBlank()) {
return ModuleException( return ModuleException(
debugMessage, debugMessage,
cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate),
this this
) )
} else if (!message.isNullOrBlank()) { } else if (!message.isNullOrBlank()) {
return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this)

View file

@ -68,6 +68,6 @@ class FeedReaderTest {
assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java)
assertFailure { readFeed("https://www.examplesfoo.com/") } assertFailure { readFeed("https://www.examplesfoo.com/") }
.isInstanceOf(UnknownHostException::class.java) .isInstanceOf(UnknownHostException::class.java)
} }
} }

View file

@ -36,7 +36,7 @@ import java.net.InetAddress
import java.net.UnknownHostException import java.net.UnknownHostException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
import java.util.Properties import java.util.*
/** /**
* Access to `local.properties`. * Access to `local.properties`.

View file

@ -68,7 +68,7 @@ class PinboardTest : LocalProperties() {
private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean {
val response = val response =
URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body
matches.forEach { matches.forEach {
if (!response.contains(it)) { if (!response.contains(it)) {

View file

@ -66,15 +66,14 @@ import java.io.File
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
import java.time.LocalDateTime import java.time.LocalDateTime
import java.util.Calendar import java.util.*
import java.util.Properties
/** /**
* The `Utils Test` class. * The `Utils Test` class.
*/ */
class UtilsTest { class UtilsTest {
private val ascii = private val ascii =
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
private val cal = Calendar.getInstance() private val cal = Calendar.getInstance()
private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0)
private val test = "This is a test." private val test = "This is a test."
@ -90,7 +89,7 @@ class UtilsTest {
val sep = '/' val sep = '/'
val url = "https://erik.thauvin.net" val url = "https://erik.thauvin.net"
assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)")
.isEqualTo(dir + File.separatorChar) .isEqualTo(dir + File.separatorChar)
assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep")
assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep")
} }
@ -116,24 +115,24 @@ class UtilsTest {
fun textCapitaliseWords() { fun textCapitaliseWords() {
assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.")
assertThat("Already Capitalized".capitalizeWords(), "already capitalized") assertThat("Already Capitalized".capitalizeWords(), "already capitalized")
.isEqualTo("Already Capitalized") .isEqualTo("Already Capitalized")
assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ")
} }
@Test @Test
fun testColorize() { fun testColorize() {
assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo(
Colors.REVERSE + ascii + Colors.REVERSE Colors.REVERSE + ascii + Colors.REVERSE
) )
assertThat(ascii.colorize(Colors.RED), "red.colorize()") assertThat(ascii.colorize(Colors.RED), "red.colorize()")
.isEqualTo(Colors.RED + ascii + Colors.NORMAL) .isEqualTo(Colors.RED + ascii + Colors.NORMAL)
assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)")
.isEqualTo(Colors.BOLD + ascii + Colors.BOLD) .isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("")
assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("")
assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii)
assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()")
.isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL)
} }
@Test @Test
@ -165,19 +164,19 @@ class UtilsTest {
fun testHelpCmdSyntax() { fun testHelpCmdSyntax() {
val bot = "mobibot" val bot = "mobibot"
assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)")
.isEqualTo("$bot: $test $bot $test") .isEqualTo("$bot: $test $bot $test")
assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)")
.isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot")
} }
@Test @Test
fun testHelpFormat() { fun testHelpFormat() {
assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)")
.isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}")
assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)")
.isEqualTo(test.prependIndent()) .isEqualTo(test.prependIndent())
assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)")
.isEqualTo(test.colorize(Colors.BOLD).prependIndent()) .isEqualTo(test.colorize(Colors.BOLD).prependIndent())
} }
@ -219,15 +218,15 @@ class UtilsTest {
val search = arrayOf("one", "two", "three") val search = arrayOf("one", "two", "three")
val replace = arrayOf("1", "2", "3") val replace = arrayOf("1", "2", "3")
assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3")
.isEqualTo(replace.joinToString(",")) .isEqualTo(replace.joinToString(","))
assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test)
assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)")
.isEqualTo(test.replace("t", "").replace("e", "E")) .isEqualTo(test.replace("t", "").replace("e", "E"))
assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)")
.isEqualTo(test) .isEqualTo(test)
} }
@Test @Test
@ -259,7 +258,7 @@ class UtilsTest {
@Test @Test
fun testUnescapeXml() { fun testUnescapeXml() {
assertThat("&lt;a name=&quot;test &amp; &apos;&#39;&quot;&gt;".unescapeXml()).isEqualTo( assertThat("&lt;a name=&quot;test &amp; &apos;&#39;&quot;&gt;".unescapeXml()).isEqualTo(
"<a name=\"test & ''\">" "<a name=\"test & ''\">"
) )
} }

View file

@ -40,14 +40,14 @@ class InfoTest {
@Test(groups = ["commands"]) @Test(groups = ["commands"])
fun testToUptime() { fun testToUptime() {
assertThat( assertThat(
547800300076L.toUptime(), 547800300076L.toUptime(),
"upTime(full)" "upTime(full)"
).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes")
assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes")
assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes")
assertThat( assertThat(
1320300000L.toUptime(), 1320300000L.toUptime(),
"upTime(weeks days hours minutes)" "upTime(weeks days hours minutes)"
).isEqualTo("2 weeks 1 day 6 hours 45 minutes") ).isEqualTo("2 weeks 1 day 6 hours 45 minutes")
assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes")
assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute")

View file

@ -48,13 +48,13 @@ class RecapTest {
assertThat(Recap.recaps, "Recap.recaps").all { assertThat(Recap.recaps, "Recap.recaps").all {
size().isEqualTo(Recap.MAX_RECAPS) size().isEqualTo(Recap.MAX_RECAPS)
prop(MutableList<String>::first) prop(MutableList<String>::first)
.matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex())
prop(MutableList<String>::last) prop(MutableList<String>::last)
.matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex())
} }
Recap.storeRecap("sender", "test action", true) Recap.storeRecap("sender", "test action", true)
assertThat(Recap.recaps.last()) assertThat(Recap.recaps.last())
.matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex())
} }
} }

View file

@ -47,8 +47,8 @@ class LinksManagerTest {
fun fetchTitle() { fun fetchTitle() {
assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog")
assertThat( assertThat(
linksManager.fetchTitle("https://www.google.com/foo"), linksManager.fetchTitle("https://www.google.com/foo"),
"fetchTitle(Foo)" "fetchTitle(Foo)"
).isEqualTo(Constants.NO_TITLE) ).isEqualTo(Constants.NO_TITLE)
} }

View file

@ -45,14 +45,14 @@ class ViewTest {
for (i in 1..10) { for (i in 1..10) {
LinksManager.entries.links.add( LinksManager.entries.links.add(
EntryLink( EntryLink(
"https://www.example.com/$i", "https://www.example.com/$i",
"Example $i", "Example $i",
"nick$i", "nick$i",
"login$i", "login$i",
"#channel", "#channel",
emptyList() emptyList()
) )
) )
} }

View file

@ -33,14 +33,7 @@ package net.thauvin.erik.mobibot.commands.seen
import assertk.all import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.isEmpty import assertk.assertions.*
import assertk.assertions.isEqualTo
import assertk.assertions.isGreaterThan
import assertk.assertions.isNotEqualTo
import assertk.assertions.isNotNull
import assertk.assertions.key
import assertk.assertions.prop
import assertk.assertions.size
import org.testng.annotations.AfterClass import org.testng.annotations.AfterClass
import org.testng.annotations.BeforeClass import org.testng.annotations.BeforeClass
import org.testng.annotations.Test import org.testng.annotations.Test

View file

@ -33,13 +33,7 @@ package net.thauvin.erik.mobibot.commands.tell
import assertk.all import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.index import assertk.assertions.*
import assertk.assertions.isEqualTo
import assertk.assertions.isFalse
import assertk.assertions.isGreaterThan
import assertk.assertions.isTrue
import assertk.assertions.prop
import assertk.assertions.size
import org.testng.annotations.AfterClass import org.testng.annotations.AfterClass
import org.testng.annotations.BeforeClass import org.testng.annotations.BeforeClass
import org.testng.annotations.Test import org.testng.annotations.Test

View file

@ -46,14 +46,14 @@ class EntriesUtilsTest {
private val links = buildList { private val links = buildList {
for (i in 0..5) { for (i in 0..5) {
add( add(
EntryLink( EntryLink(
"https://www.mobitopia.org/$i", "https://www.mobitopia.org/$i",
"Mobitopia$i", "Mobitopia$i",
"Skynx$i", "Skynx$i",
"JimH$i", "JimH$i",
"#mobitopia$i", "#mobitopia$i",
listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") listOf("tag1", "tag2", "tag3", "TAG4", "Tag5")
) )
) )
} }
} }
@ -67,7 +67,7 @@ class EntriesUtilsTest {
fun printLinkTest() { fun printLinkTest() {
for (i in links.indices) { for (i in links.indices) {
assertThat( assertThat(
printLink(i - 1, links[i]), "link $i" printLink(i - 1, links[i]), "link $i"
).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )")
} }
@ -79,7 +79,7 @@ class EntriesUtilsTest {
fun printTagsTest() { fun printTagsTest() {
for (i in links.indices) { for (i in links.indices) {
assertThat( assertThat(
printTags(i - 1, links[i]), "tag $i" printTags(i - 1, links[i]), "tag $i"
).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5")
} }
} }

View file

@ -32,18 +32,12 @@ package net.thauvin.erik.mobibot.entries
import assertk.all import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.index import assertk.assertions.*
import assertk.assertions.isEmpty
import assertk.assertions.isEqualTo
import assertk.assertions.isFalse
import assertk.assertions.isTrue
import assertk.assertions.prop
import assertk.assertions.size
import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategory
import com.rometools.rome.feed.synd.SyndCategoryImpl import com.rometools.rome.feed.synd.SyndCategoryImpl
import org.testng.annotations.Test import org.testng.annotations.Test
import java.security.SecureRandom import java.security.SecureRandom
import java.util.Date import java.util.*
/** /**
* The `EntryUtilsTest` class. * The `EntryUtilsTest` class.
@ -54,8 +48,8 @@ import java.util.Date
*/ */
class EntryLinkTest { class EntryLinkTest {
private val entryLink = EntryLink( private val entryLink = EntryLink(
"https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia",
listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") listOf("tag1", "tag2", "tag3", "TAG4", "Tag5")
) )
@Test(groups = ["entries"]) @Test(groups = ["entries"])
@ -123,12 +117,12 @@ class EntryLinkTest {
entryLink.setTags("+mobitopia") entryLink.setTags("+mobitopia")
entryLink.setTags("-mobitopia") entryLink.setTags("-mobitopia")
assertThat( assertThat(
entryLink.formatTags(","), entryLink.formatTags(","),
"formatTags(',')" "formatTags(',')"
).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia")
entryLink.setTags("-tag4 tag5") entryLink.setTags("-tag4 tag5")
assertThat( assertThat(
entryLink.formatTags(" ", ","), "formatTag(' ',',')" entryLink.formatTags(" ", ","), "formatTag(' ',',')"
).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5")
val size = entryLink.tags.size val size = entryLink.tags.size
entryLink.setTags("") entryLink.setTags("")

View file

@ -33,18 +33,12 @@ package net.thauvin.erik.mobibot.entries
import assertk.all import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.endsWith import assertk.assertions.*
import assertk.assertions.exists
import assertk.assertions.index
import assertk.assertions.isEqualTo
import assertk.assertions.isTrue
import assertk.assertions.prop
import assertk.assertions.size
import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.today
import org.testng.annotations.BeforeSuite import org.testng.annotations.BeforeSuite
import org.testng.annotations.Test import org.testng.annotations.Test
import java.nio.file.Paths import java.nio.file.Paths
import java.util.Date import java.util.*
import kotlin.io.path.deleteIfExists import kotlin.io.path.deleteIfExists
import kotlin.io.path.fileSize import kotlin.io.path.fileSize
import kotlin.io.path.name import kotlin.io.path.name

View file

@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.modules
import assertk.assertFailure import assertk.assertFailure
import assertk.assertThat import assertk.assertThat
import assertk.assertions.isEqualTo import assertk.assertions.isEqualTo
import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf import assertk.assertions.isInstanceOf
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.bold

View file

@ -34,7 +34,6 @@ import assertk.assertFailure
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.isFailure
import assertk.assertions.isInstanceOf import assertk.assertions.isInstanceOf
import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.LocalProperties
import org.testng.annotations.Test import org.testng.annotations.Test
@ -43,21 +42,21 @@ class ChatGptTest : LocalProperties() {
@Test(groups = ["modules"]) @Test(groups = ["modules"])
fun testApiKey() { fun testApiKey() {
assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) }
.isInstanceOf(ModuleException::class.java) .isInstanceOf(ModuleException::class.java)
.hasNoCause() .hasNoCause()
} }
@Test(groups = ["modules", "no-ci"]) @Test(groups = ["modules", "no-ci"])
fun testChat() { fun testChat() {
val apiKey = getProperty(ChatGpt.API_KEY_PROP) 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")
assertThat( assertThat(
ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ChatGpt.chat("how do I encode a URL in java?", apiKey, 60)
).contains("URLEncoder") ).contains("URLEncoder")
assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) }
.isInstanceOf(ModuleException::class.java) .isInstanceOf(ModuleException::class.java)
} }
} }

View file

@ -58,16 +58,16 @@ class CurrencyConverterTest {
@Test(groups = ["modules"]) @Test(groups = ["modules"])
fun testConvertCurrency() { fun testConvertCurrency() {
assertThat( assertThat(
convertCurrency("100 USD to EUR").msg, convertCurrency("100 USD to EUR").msg,
"convertCurrency(100 USD to EUR)" "convertCurrency(100 USD to EUR)"
).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex()) ).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex())
assertThat( assertThat(
convertCurrency("1 USD to BTC").msg, convertCurrency("1 USD to BTC").msg,
"convertCurrency(1 USD to BTC)" "convertCurrency(1 USD to BTC)"
).matches("1 United States Dollar = 0\\.\\d+ Bitcoin".toRegex()) ).matches("1 United States Dollar = 0\\.\\d+ Bitcoin".toRegex())
assertThat( assertThat(
convertCurrency("100,000.00 GBP to BTC").msg, convertCurrency("100,000.00 GBP to BTC").msg,
"convertCurrency(100,000.00 GBP to BTC)" "convertCurrency(100,000.00 GBP to BTC)"
).matches("100,000.00 British Pound Sterling = \\d{1,2}\\.\\d+ Bitcoin".toRegex()) ).matches("100,000.00 British Pound Sterling = \\d{1,2}\\.\\d+ Bitcoin".toRegex())
assertThat(convertCurrency("100 USD to USD"), "convertCurrency(100 USD to USD)").all { assertThat(convertCurrency("100 USD to USD"), "convertCurrency(100 USD to USD)").all {
prop(Message::msg).contains("You're kidding, right?") prop(Message::msg).contains("You're kidding, right?")

View file

@ -42,12 +42,12 @@ class DiceTest {
fun testRoll() { fun testRoll() {
assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002")
assertThat(Dice.roll(2, 1), "roll(2d1)") assertThat(Dice.roll(2, 1), "roll(2d1)")
.isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002")
assertThat(Dice.roll(5, 1), "roll(5d1)") assertThat(Dice.roll(5, 1), "roll(5d1)")
.isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002")
assertThat(Dice.roll(2, 6), "roll(2d6)") assertThat(Dice.roll(2, 6), "roll(2d6)")
.matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex())
assertThat(Dice.roll(3, 7), "roll(3d7)") assertThat(Dice.roll(3, 7), "roll(3d7)")
.matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex())
} }
} }

View file

@ -33,15 +33,7 @@ package net.thauvin.erik.mobibot.modules
import assertk.all import assertk.all
import assertk.assertFailure import assertk.assertFailure
import assertk.assertThat import assertk.assertThat
import assertk.assertions.contains import assertk.assertions.*
import assertk.assertions.hasMessage
import assertk.assertions.hasNoCause
import assertk.assertions.index
import assertk.assertions.isEqualTo
import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf
import assertk.assertions.isNotEmpty
import assertk.assertions.prop
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.LocalProperties
import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
@ -56,19 +48,19 @@ class GoogleSearchTest : LocalProperties() {
@Test(groups = ["modules"]) @Test(groups = ["modules"])
fun testAPIKeys() { fun testAPIKeys() {
assertThat( assertThat(
searchGoogle("", "apikey", "cssKey").first(), searchGoogle("", "apikey", "cssKey").first(),
"searchGoogle(empty)" "searchGoogle(empty)"
).isInstanceOf(ErrorMessage::class.java) ).isInstanceOf(ErrorMessage::class.java)
assertFailure { searchGoogle("test", "", "apiKey") } assertFailure { searchGoogle("test", "", "apiKey") }
.isInstanceOf(ModuleException::class.java).hasNoCause() .isInstanceOf(ModuleException::class.java).hasNoCause()
assertFailure { searchGoogle("test", "apiKey", "") } assertFailure { searchGoogle("test", "apiKey", "") }
.isInstanceOf(ModuleException::class.java).hasNoCause() .isInstanceOf(ModuleException::class.java).hasNoCause()
assertFailure { searchGoogle("test", "apiKey", "cssKey") } assertFailure { searchGoogle("test", "apiKey", "cssKey") }
.isInstanceOf(ModuleException::class.java) .isInstanceOf(ModuleException::class.java)
.hasMessage("API key not valid. Please pass a valid API key.") .hasMessage("API key not valid. Please pass a valid API key.")
} }
@Test(groups = ["no-ci", "modules"]) @Test(groups = ["no-ci", "modules"])

View file

@ -32,12 +32,7 @@ package net.thauvin.erik.mobibot.modules
import assertk.all import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.doesNotContain import assertk.assertions.*
import assertk.assertions.each
import assertk.assertions.isGreaterThan
import assertk.assertions.isInstanceOf
import assertk.assertions.prop
import assertk.assertions.size
import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke
import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.Message
import net.thauvin.erik.mobibot.msg.PublicMessage import net.thauvin.erik.mobibot.msg.PublicMessage

View file

@ -42,13 +42,13 @@ class MastodonTest : LocalProperties() {
fun testToot() { fun testToot() {
val msg = "Testing Mastodon API from ${getHostName()}" val msg = "Testing Mastodon API from ${getHostName()}"
assertThat( assertThat(
toot( toot(
getProperty(Mastodon.ACCESS_TOKEN_PROP), getProperty(Mastodon.ACCESS_TOKEN_PROP),
getProperty(Mastodon.INSTANCE_PROP), getProperty(Mastodon.INSTANCE_PROP),
getProperty(Mastodon.HANDLE_PROP), getProperty(Mastodon.HANDLE_PROP),
msg, msg,
true true
) )
).contains(msg) ).contains(msg)
} }
} }

View file

@ -32,13 +32,7 @@ package net.thauvin.erik.mobibot.modules
import assertk.all import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.contains import assertk.assertions.*
import assertk.assertions.doesNotContain
import assertk.assertions.endsWith
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isNotNull
import assertk.assertions.isNull
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
import org.testng.annotations.DataProvider import org.testng.annotations.DataProvider
import org.testng.annotations.Test import org.testng.annotations.Test
@ -57,9 +51,9 @@ class ModuleExceptionTest {
@DataProvider(name = "dp") @DataProvider(name = "dp")
fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> { fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> {
return arrayOf( return arrayOf(
arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))),
arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))), arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))),
arrayOf(ModuleException(debugMessage, message)) arrayOf(ModuleException(debugMessage, message))
) )
} }
@ -78,7 +72,7 @@ class ModuleExceptionTest {
val apiKey = "1234567890" val apiKey = "1234567890"
var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me"))
assertThat( assertThat(
e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))"
).isNotNull().all { ).isNotNull().all {
contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") contains("xxxxxxxxxx", "userID=xx", "java.io.IOException")
doesNotContain(apiKey, "me") doesNotContain(apiKey, "me")
@ -92,7 +86,7 @@ class ModuleExceptionTest {
e = ModuleException(debugMessage, apiKey) e = ModuleException(debugMessage, apiKey)
assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull()
.doesNotContain(apiKey) .doesNotContain(apiKey)
val msg: String? = null val msg: String? = null
e = ModuleException(debugMessage, msg, IOException(msg)) e = ModuleException(debugMessage, msg, IOException(msg))
@ -100,8 +94,8 @@ class ModuleExceptionTest {
e = ModuleException(debugMessage, msg, IOException("foo is $apiKey")) e = ModuleException(debugMessage, msg, IOException("foo is $apiKey"))
assertThat( assertThat(
e.sanitize(" ", apiKey, "foo").message, e.sanitize(" ", apiKey, "foo").message,
"ModuleException(debugMessage, msg, IOException(foo is $apiKey))" "ModuleException(debugMessage, msg, IOException(foo is $apiKey))"
).isNotNull().all { ).isNotNull().all {
doesNotContain(apiKey) doesNotContain(apiKey)
endsWith("xxx is xxxxxxxxxx") endsWith("xxx is xxxxxxxxxx")

View file

@ -33,14 +33,7 @@ package net.thauvin.erik.mobibot.modules
import assertk.all import assertk.all
import assertk.assertFailure import assertk.assertFailure
import assertk.assertThat import assertk.assertThat
import assertk.assertions.hasNoCause import assertk.assertions.*
import assertk.assertions.index
import assertk.assertions.isEqualTo
import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf
import assertk.assertions.isNotEmpty
import assertk.assertions.matches
import assertk.assertions.prop
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.LocalProperties
import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote
@ -67,7 +60,7 @@ class StockQuoteTest : LocalProperties() {
assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex())
assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex())
assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg)
.matches(buildMatch("Previous").toRegex()) .matches(buildMatch("Previous").toRegex())
assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex())
symbol = "blahfoo" symbol = "blahfoo"

View file

@ -33,16 +33,7 @@ package net.thauvin.erik.mobibot.modules
import assertk.all import assertk.all
import assertk.assertFailure import assertk.assertFailure
import assertk.assertThat import assertk.assertThat
import assertk.assertions.contains import assertk.assertions.*
import assertk.assertions.endsWith
import assertk.assertions.hasNoCause
import assertk.assertions.index
import assertk.assertions.isEqualTo
import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf
import assertk.assertions.isNotNull
import assertk.assertions.isTrue
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
@ -69,7 +60,7 @@ class Weather2Test : LocalProperties() {
assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES)
assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE)
val country = OWM.Country.values() val country = OWM.Country.entries.toTypedArray()
repeat(3) { repeat(3) {
val rand = country[(country.indices).random()] val rand = country[(country.indices).random()]
assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) assertThat(getCountry(rand.value), rand.name).isEqualTo(rand)

View file

@ -45,11 +45,11 @@ class WolframAlphaTest : LocalProperties() {
@Test(groups = ["modules"]) @Test(groups = ["modules"])
fun testAppId() { fun testAppId() {
assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") }
.isInstanceOf(ModuleException::class.java) .isInstanceOf(ModuleException::class.java)
.hasMessage("Error 1: Invalid appid") .hasMessage("Error 1: Invalid appid")
assertFailure { queryWolfram("1 gallon to liter", appId = "") } assertFailure { queryWolfram("1 gallon to liter", appId = "") }
.isInstanceOf(ModuleException::class.java) .isInstanceOf(ModuleException::class.java)
} }
@Test(groups = ["modules", "no-ci"]) @Test(groups = ["modules", "no-ci"])
@ -62,8 +62,8 @@ class WolframAlphaTest : LocalProperties() {
query = "SFO to LAX" query = "SFO to LAX"
assertThat( assertThat(
queryWolfram(query, WolframAlpha.METRIC, apiKey), queryWolfram(query, WolframAlpha.METRIC, apiKey),
"queryWolfram($query)" "queryWolfram($query)"
).contains("kilometers") ).contains("kilometers")
} catch (e: ModuleException) { } catch (e: ModuleException) {
// Avoid displaying api key in CI logs // Avoid displaying api key in CI logs

View file

@ -30,10 +30,8 @@
*/ */
package net.thauvin.erik.mobibot.modules package net.thauvin.erik.mobibot.modules
import assertk.assertFailure
import assertk.assertThat import assertk.assertThat
import assertk.assertions.endsWith import assertk.assertions.endsWith
import assertk.assertions.isSuccess
import assertk.assertions.matches import assertk.assertions.matches
import assertk.assertions.startsWith import assertk.assertions.startsWith
import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.Utils.bold
@ -51,9 +49,9 @@ class WordTimeTest {
@Test(groups = ["modules"]) @Test(groups = ["modules"])
fun testTime() { fun testTime() {
assertThat(time(), "time()").matches( assertThat(time(), "time()").matches(
("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " +
"on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " + "on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " +
"in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex() "in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex()
) )
assertThat(time(""), "time()").endsWith("Los Angeles".bold()) assertThat(time(""), "time()").endsWith("Los Angeles".bold())
assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold())

View file

@ -1,9 +1,9 @@
#Generated by the Semver Plugin for Gradle #Generated by the Semver Plugin for Gradle
#Sun Jul 02 02:19:45 PDT 2023 #Thu Jul 06 10:21:40 PDT 2023
version.buildmeta=20230702021945 version.buildmeta=20230706102140
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+20230702021945 version.semver=0.8.0-rc+20230706102140