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">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" />
</state>
</component>

2
.idea/kotlinc.xml generated
View file

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

View file

@ -1,7 +1,7 @@
# mobibot
[![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)
[![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)

View file

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

View file

@ -46,6 +46,15 @@
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID>
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</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(module: AbstractModule): Boolean</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:Mobibot.kt$Mobibot : ListenerAdapter</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: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>
</SmellBaseline>

View file

@ -72,6 +72,12 @@ public final class War extends AbstractModule {
help.add(Utils.helpFormat("%c " + WAR_CMD));
}
@NotNull
@Override
public String getName() {
return "War";
}
/**
* {@inheritDoc}
*/
@ -99,10 +105,4 @@ public final class War extends AbstractModule {
} 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.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.Properties
import java.util.*
/**
* Modules and Commands addons.

View file

@ -63,7 +63,7 @@ object Constants {
* 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.

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.sendMessage
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
import net.thauvin.erik.mobibot.commands.ChannelFeed
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.*
import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap
import net.thauvin.erik.mobibot.commands.Say
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.links.*
import net.thauvin.erik.mobibot.commands.seen.Seen
import net.thauvin.erik.mobibot.commands.tell.Tell
import net.thauvin.erik.mobibot.modules.Calc
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.mobibot.modules.*
import net.thauvin.erik.semver.Version
import org.pircbotx.Configuration
import org.pircbotx.PircBotX
import org.pircbotx.hooks.ListenerAdapter
import org.pircbotx.hooks.events.ActionEvent
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.events.*
import org.pircbotx.hooks.types.GenericMessageEvent
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.PrintStream
import java.io.*
import java.nio.file.Files
import java.nio.file.Paths
import java.util.Properties
import java.util.*
import java.util.regex.Pattern
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("For more information on a specific command, type:")
event.sendMessage(
helpFormat(
helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent)
)
helpFormat(
helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent)
)
)
event.sendMessage("The commands are:")
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>()) {
if (user.nick == nick) {
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)
} else {
@ -252,7 +209,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
with(event.getBot<PircBotX>()) {
if (user.nick == nick) {
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)
} else {
@ -275,22 +232,22 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
// Set up the command line options
val parser = ArgParser(Constants.CLI_CMD)
val debug by parser.option(
ArgType.Boolean,
Constants.DEBUG_ARG,
Constants.DEBUG_ARG.substring(0, 1),
"Print debug & logging data directly to the console"
ArgType.Boolean,
Constants.DEBUG_ARG,
Constants.DEBUG_ARG.substring(0, 1),
"Print debug & logging data directly to the console"
).default(false)
val property by parser.option(
ArgType.String,
Constants.PROPS_ARG,
Constants.PROPS_ARG.substring(0, 1),
"Use alternate properties file"
ArgType.String,
Constants.PROPS_ARG,
Constants.PROPS_ARG.substring(0, 1),
"Use alternate properties file"
).default("./${ReleaseInfo.PROJECT}.properties")
val version by parser.option(
ArgType.Boolean,
Constants.VERSION_ARG,
Constants.VERSION_ARG.substring(0, 1),
"Print version info"
ArgType.Boolean,
Constants.VERSION_ARG,
Constants.VERSION_ARG.substring(0, 1),
"Print version info"
).default(false)
// Parse the command line
@ -299,8 +256,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
if (version) {
// Output the version
println(
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" +
" (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})"
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" +
" (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})"
)
println(ReleaseInfo.WEBSITE)
} else {
@ -308,7 +265,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
val p = Properties()
try {
Files.newInputStream(
Paths.get(property)
Paths.get(property)
).use { fis ->
p.load(fis)
}
@ -327,11 +284,11 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
if (!debug) {
try {
val stdout = PrintStream(
BufferedOutputStream(
FileOutputStream(
logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true
)
), true
BufferedOutputStream(
FileOutputStream(
logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true
)
), true
)
System.setOut(stdout)
} catch (ignore: IOException) {
@ -340,9 +297,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
}
try {
val stderr = PrintStream(
BufferedOutputStream(
FileOutputStream("$logsDir$nickname.err", true)
), true
BufferedOutputStream(
FileOutputStream("$logsDir$nickname.err", true)
), true
)
System.setErr(stderr)
} catch (ignore: IOException) {
@ -367,8 +324,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
login = p.getProperty("login", nickname)
realName = p.getProperty("realname", nickname)
addServer(
ircServer,
p.getIntProperty("port", Constants.DEFAULT_PORT)
ircServer,
p.getIntProperty("port", Constants.DEFAULT_PORT)
)
addAutoJoinChannel(channel)
addListener(this@Mobibot)

View file

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

View file

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

View file

@ -48,8 +48,8 @@ import kotlin.time.toDuration
class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() {
private val allVersions = listOf(
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})",
"Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})"
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})",
"Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})"
)
override val name = "info"
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)
val info = StringBuilder()
info.append("Uptime: ")
.append(ManagementFactory.getRuntimeMXBean().uptime.toUptime())
.append(" [Entries: ")
.append(LinksManager.entries.links.size)
.append(ManagementFactory.getRuntimeMXBean().uptime.toUptime())
.append(" [Entries: ")
.append(LinksManager.entries.links.size)
if (seen.isEnabled()) {
info.append(", Seen: ").append(seen.count())
}

View file

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

View file

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

View file

@ -40,10 +40,10 @@ import org.pircbotx.hooks.types.GenericMessageEvent
class Versions : AbstractCommand() {
private val allVersions = listOf(
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})",
"${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" +
", JVM ${System.getProperty("java.runtime.version")}",
"Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}"
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})",
"${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" +
", JVM ${System.getProperty("java.runtime.version")}",
"Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}"
)
override val name = "versions"
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() {
override val name = COMMAND
override val help = listOf(
"To add 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",
"To edit a comment, use its label: ",
helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"),
"To delete a comment, use its label and a minus sign: ",
helpFormat("${Constants.LINK_CMD}1.1:-")
"To add 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",
"To edit a comment, use its label: ",
helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"),
"To delete a comment, use its label and a minus sign: ",
helpFormat("${Constants.LINK_CMD}1.1:-")
)
override val isOpOnly = false
override val isPublic = true
@ -100,12 +100,12 @@ class Comment : AbstractCommand() {
}
private fun changeAuthor(
channel: String,
cmd: String,
entry: EntryLink,
entryIndex: Int,
commentIndex: Int,
event: GenericMessageEvent
channel: String,
cmd: String,
entry: EntryLink,
entryIndex: Int,
commentIndex: Int,
event: GenericMessageEvent
) {
if (event.isChannelOp(channel) && cmd.length > 1) {
val comment = entry.getComment(commentIndex)
@ -118,11 +118,11 @@ class Comment : AbstractCommand() {
}
private fun deleteComment(
channel: String,
entry: EntryLink,
entryIndex: Int,
commentIndex: Int,
event: GenericMessageEvent
channel: String,
entry: EntryLink,
entryIndex: Int,
commentIndex: Int,
event: GenericMessageEvent
) {
if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) {
entry.deleteComment(commentIndex)
@ -134,11 +134,11 @@ class Comment : AbstractCommand() {
}
private fun setComment(
cmd: String,
entry: EntryLink,
entryIndex: Int,
commentIndex: Int,
event: GenericMessageEvent
cmd: String,
entry: EntryLink,
entryIndex: Int,
commentIndex: Int,
event: GenericMessageEvent
) {
entry.setComment(commentIndex, cmd, event.user.nick)
event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex)))

View file

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

View file

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

View file

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

View file

@ -46,8 +46,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent
class View : AbstractCommand() {
override val name = VIEW_CMD
override val help = listOf(
"To list or search the current URL posts:",
helpFormat("%c $name [<start>] [<query>]")
"To list or search the current URL posts:",
helpFormat("%c $name [<start>] [<query>]")
)
override val isOpOnly = false
override val isPublic = true
@ -107,9 +107,9 @@ class View : AbstractCommand() {
if (sent == MAX_ENTRIES && index < entries.links.size) {
event.sendMessage("To view more, try: ")
event.sendMessage(
helpFormat(
helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent)
)
helpFormat(
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 help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>"))
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 isPublic = true
@ -130,12 +130,12 @@ class Seen(private val serialObject: String) : AbstractCommand() {
if (isEnabled()) {
@Suppress("UNCHECKED_CAST")
seenNicks.putAll(
loadSerialData(
serialObject,
TreeMap<String, SeenNick>(),
logger,
"seen nicknames"
) as TreeMap<String, SeenNick>
loadSerialData(
serialObject,
TreeMap<String, SeenNick>(),
logger,
"seen nicknames"
) as TreeMap<String, SeenNick>
)
}
}

View file

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

View file

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

View file

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

View file

@ -43,7 +43,7 @@ object EntriesUtils {
*/
@JvmStatic
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.
@ -52,7 +52,7 @@ object EntriesUtils {
@JvmOverloads
fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String {
val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ")
.append('[').append(entry.nick).append(']')
.append('[').append(entry.nick).append(']')
if (isView && entry.comments.isNotEmpty()) {
buff.append("[+").append(entry.comments.size).append(']')
}
@ -73,7 +73,7 @@ object EntriesUtils {
*/
@JvmStatic
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

View file

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

View file

@ -30,11 +30,7 @@
*/
package net.thauvin.erik.mobibot.entries
import com.rometools.rome.feed.synd.SyndContentImpl
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.feed.synd.*
import com.rometools.rome.io.FeedException
import com.rometools.rome.io.SyndFeedInput
import com.rometools.rome.io.SyndFeedOutput
@ -48,7 +44,7 @@ import java.io.OutputStreamWriter
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
import java.util.Calendar
import java.util.*
import kotlin.io.path.exists
/**
@ -76,7 +72,7 @@ class FeedsManager private constructor() {
if (xml.exists()) {
val input = SyndFeedInput()
InputStreamReader(
Files.newInputStream(xml), StandardCharsets.UTF_8
Files.newInputStream(xml), StandardCharsets.UTF_8
).use { reader ->
val feed = input.build(reader)
pubDate = feed.publishedDate.toIsoLocalDate()
@ -85,12 +81,12 @@ class FeedsManager private constructor() {
for (i in items.indices.reversed()) {
with(items[i]) {
entry = EntryLink(
link,
title,
author.substring(author.lastIndexOf('(') + 1, author.length - 1),
entries.channel,
publishedDate,
categories
link,
title,
author.substring(author.lastIndexOf('(') + 1, author.length - 1),
entries.channel,
publishedDate,
categories
)
var split: List<String>
for (comment in description.value.split("<br/>")) {
@ -123,7 +119,7 @@ class FeedsManager private constructor() {
val items: MutableList<SyndEntry> = mutableListOf()
var item: SyndEntry
OutputStreamWriter(
Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8
Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8
).use { fw ->
with(rss) {
feedType = "rss_2.0"
@ -138,13 +134,13 @@ class FeedsManager private constructor() {
with(entries.links[i]) {
buff.setLength(0)
buff.append("Posted by <b>")
.append(nick)
.append("</b> on <a href=\"irc://")
.append(entries.ircServer).append('/')
.append(channel)
.append("\"><b>")
.append(channel)
.append("</b></a>")
.append(nick)
.append("</b> on <a href=\"irc://")
.append(entries.ircServer).append('/')
.append(channel)
.append("\"><b>")
.append(channel)
.append("</b></a>")
if (comments.size > 0) {
buff.append(" <br/><br/>")
for (j in comments.indices) {
@ -169,11 +165,11 @@ class FeedsManager private constructor() {
output.output(rss, fw)
}
OutputStreamWriter(
Files.newOutputStream(
Paths.get(
entries.logsDir + today() + dotXml
)
), StandardCharsets.UTF_8
Files.newOutputStream(
Paths.get(
entries.logsDir + today() + dotXml
)
), StandardCharsets.UTF_8
).use { fw -> output.output(rss, fw) }
} catch (e: FeedException) {
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) {
if (args.isNotBlank()) {
try {
val answer = chat(args.trim(), properties[API_KEY_PROP],
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt())
val answer = chat(
args.trim(), properties[API_KEY_PROP],
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()
)
if (answer.isNotBlank()) {
event.sendMessage(WordUtils.wrap(answer, 400))
} else {
@ -105,13 +107,13 @@ class ChatGpt : AbstractModule() {
if (!apiKey.isNullOrEmpty()) {
val prompt = JSONWriter.valueToString("Q:$query\nA:")
val request = HttpRequest.newBuilder()
.uri(URI.create(API_URL))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer $apiKey")
.header("User-Agent", Constants.USER_AGENT)
.POST(
HttpRequest.BodyPublishers.ofString(
"""{
.uri(URI.create(API_URL))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer $apiKey")
.header("User-Agent", Constants.USER_AGENT)
.POST(
HttpRequest.BodyPublishers.ofString(
"""{
"model": "text-davinci-003",
"prompt": $prompt,
"temperature": 0,
@ -120,9 +122,9 @@ class ChatGpt : AbstractModule() {
"frequency_penalty": 0,
"presence_penalty": 0
}""".trimIndent()
)
)
)
.build()
.build()
try {
val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() == 200) {
@ -132,16 +134,16 @@ class ChatGpt : AbstractModule() {
return choices.getJSONObject(0).getString("text").trim()
} catch (e: JSONException) {
throw ModuleException(
"$CHATGPT_CMD($query): JSON",
"A JSON error has occurred while conversing with $CHATGPT_NAME.",
e
"$CHATGPT_CMD($query): JSON",
"A JSON error has occurred while conversing with $CHATGPT_NAME.",
e
)
}
} else {
if (response.statusCode() == 429) {
throw ModuleException(
"$CHATGPT_CMD($query): Rate limit reached",
"Rate limit reached. Please try again later."
"$CHATGPT_CMD($query): Rate limit reached",
"Rate limit reached. Please try again later."
)
} else {
throw IOException("HTTP Status Code: " + response.statusCode())
@ -149,9 +151,9 @@ class ChatGpt : AbstractModule() {
}
} catch (e: IOException) {
throw ModuleException(
"$CHATGPT_CMD($query): IO",
"An IO error has occurred while conversing with $CHATGPT_NAME.",
e
"$CHATGPT_CMD($query): IO",
"An IO error has occurred while conversing with $CHATGPT_NAME.",
e
)
}
} else {

View file

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

View file

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

View file

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

View file

@ -55,9 +55,9 @@ class Lookup : AbstractModule() {
event.respondWith(nslookup(args).prependIndent())
} catch (ignore: UnknownHostException) {
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]?)")
.toRegex()
)
("(?:(?: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()
)
) {
try {
val lines = whois(args)

View file

@ -65,7 +65,7 @@ class Mastodon : SocialModule() {
private fun formatTags(entry: EntryLink): String {
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)
override fun post(message: String, isDm: Boolean): String {
return toot(
apiKey = properties[ACCESS_TOKEN_PROP],
instance = properties[INSTANCE_PROP],
handle = handle,
message = message,
isDm = isDm
apiKey = properties[ACCESS_TOKEN_PROP],
instance = properties[INSTANCE_PROP],
handle = handle,
message = message,
isDm = isDm
)
}
@ -99,21 +99,21 @@ class Mastodon : SocialModule() {
@Throws(ModuleException::class)
fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String {
val request = HttpRequest.newBuilder()
.uri(URI.create("https://$instance/api/v1/statuses"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer $apiKey")
.POST(
HttpRequest.BodyPublishers.ofString(
JSONWriter.valueToString(
if (isDm) {
mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct")
} else {
mapOf("status" to message)
}
)
.uri(URI.create("https://$instance/api/v1/statuses"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer $apiKey")
.POST(
HttpRequest.BodyPublishers.ofString(
JSONWriter.valueToString(
if (isDm) {
mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct")
} else {
mapOf("status" to message)
}
)
)
)
)
.build()
.build()
try {
val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() == 200) {

View file

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

View file

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

View file

@ -52,10 +52,10 @@ class RockPaperScissors : AbstractModule() {
with(help) {
add("To play Rock Paper Scissors:")
add(
helpFormat(
"%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}"
+ " | ${Hands.SCISSORS.name.lowercase()}"
)
helpFormat(
"%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}"
+ " | ${Hands.SCISSORS.name.lowercase()}"
)
)
}
}
@ -96,7 +96,7 @@ class RockPaperScissors : AbstractModule() {
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
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 {
hand == botHand -> {
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> {
if (apiKey.isNullOrBlank()) {
throw ModuleException(
"${StockQuote::class.java.name} is disabled.",
"${STOCK_CMD.capitalise()} is disabled. The API key is missing."
"${StockQuote::class.java.name} is disabled.",
"${STOCK_CMD.capitalise()} is disabled. The API key is missing."
)
}
val messages = mutableListOf<Message>()
@ -144,8 +144,8 @@ class StockQuote : AbstractModule() {
with(messages) {
// Search for symbol/keywords
response = URL(
"${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey="
+ apiKey.encodeUrl()
"${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey="
+ apiKey.encodeUrl()
).reader().body
var json = getJsonResponse(response, debugMessage)
val symbols = json.getJSONArray("bestMatches")
@ -156,9 +156,9 @@ class StockQuote : AbstractModule() {
// Get quote for symbol
response = URL(
"${API_URL}GLOBAL_QUOTE&symbol="
+ symbolInfo.getString("1. symbol").encodeUrl() + "&apikey="
+ apiKey.encodeUrl()
"${API_URL}GLOBAL_QUOTE&symbol="
+ symbolInfo.getString("1. symbol").encodeUrl() + "&apikey="
+ apiKey.encodeUrl()
).reader().body
json = getJsonResponse(response, debugMessage)
val quote = json.getJSONObject("Global Quote")
@ -167,50 +167,50 @@ class StockQuote : AbstractModule() {
} else {
add(
PublicMessage(
"Symbol: " + quote.getString("01. symbol").unescapeXml()
+ " [" + symbolInfo.getString("2. name").unescapeXml() + ']'
)
PublicMessage(
"Symbol: " + quote.getString("01. symbol").unescapeXml()
+ " [" + symbolInfo.getString("2. name").unescapeXml() + ']'
)
)
val pad = 10
add(
PublicMessage(
"Price:".padEnd(pad).prependIndent()
+ quote.getString("05. price").unescapeXml()
)
PublicMessage(
"Price:".padEnd(pad).prependIndent()
+ quote.getString("05. price").unescapeXml()
)
)
add(
PublicMessage(
"Previous:".padEnd(pad).prependIndent()
+ quote.getString("08. previous close").unescapeXml()
)
PublicMessage(
"Previous:".padEnd(pad).prependIndent()
+ quote.getString("08. previous close").unescapeXml()
)
)
val data = arrayOf(
"Open" to "02. open",
"High" to "03. high",
"Low" to "04. low",
"Volume" to "06. volume",
"Latest" to "07. latest trading day"
"Open" to "02. open",
"High" to "03. high",
"Low" to "04. low",
"Volume" to "06. volume",
"Latest" to "07. latest trading day"
)
data.forEach {
add(
NoticeMessage(
"${it.first}:".padEnd(pad).prependIndent()
+ quote.getString(it.second).unescapeXml()
)
NoticeMessage(
"${it.first}:".padEnd(pad).prependIndent()
+ quote.getString(it.second).unescapeXml()
)
)
}
add(
NoticeMessage(
"Change:".padEnd(pad).prependIndent()
+ quote.getString("09. change").unescapeXml()
+ " [" + quote.getString("10. change percent").unescapeXml() + ']'
)
NoticeMessage(
"Change:".padEnd(pad).prependIndent()
+ quote.getString("09. change").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.
*/
fun getCountry(countryCode: String): Country {
for (c in Country.values()) {
for (c in Country.entries) {
if (c.value.equals(countryCode, ignoreCase = true)) {
return c
}
@ -120,8 +120,8 @@ class Weather2 : AbstractModule() {
fun getWeather(query: String, apiKey: String?): List<Message> {
if (apiKey.isNullOrBlank()) {
throw ModuleException(
"${Weather2::class.java.name} is disabled.",
"${WEATHER_CMD.capitalise()} is disabled. The API key is missing."
"${Weather2::class.java.name} is disabled.",
"${WEATHER_CMD.capitalise()} is disabled. The API key is missing."
)
}
val owm = OWM(apiKey)
@ -145,10 +145,10 @@ class Weather2 : AbstractModule() {
}
if (cwd.hasCityName()) {
messages.add(
PublicMessage(
"City: ${cwd.cityName}, " +
country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]"
)
PublicMessage(
"City: ${cwd.cityName}, " +
country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]"
)
)
cwd.mainData?.let {
with(it) {
@ -181,8 +181,8 @@ class Weather2 : AbstractModule() {
for (w in it) {
w?.let {
condition.append(' ')
.append(w.getDescription().capitalise())
.append('.')
.append(w.getDescription().capitalise())
.append('.')
}
}
messages.add(NoticeMessage(condition.toString()))
@ -192,15 +192,15 @@ class Weather2 : AbstractModule() {
cwd.cityId?.let {
if (it > 0) {
messages.add(
NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN)
NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN)
)
} else {
messages.add(
NoticeMessage(
"https://openweathermap.org/find?q="
+ "$city,${code.uppercase()}".encodeUrl(),
Colors.GREEN
)
NoticeMessage(
"https://openweathermap.org/find?q="
+ "$city,${code.uppercase()}".encodeUrl(),
Colors.GREEN
)
)
}
}
@ -209,9 +209,9 @@ class Weather2 : AbstractModule() {
} catch (e: APIException) {
if (e.code == 404) {
throw ModuleException(
"getWeather($query): API ${e.code}",
"The requested city was not found.",
e
"getWeather($query): API ${e.code}",
"The requested city was not found.",
e
)
} else {
throw ModuleException("getWeather($query): API ${e.code}", e.message, e)

View file

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

View file

@ -322,7 +322,7 @@ class WorldTime : AbstractModule() {
put("ZULU", "Zulu")
put("ZW", "Africa/Harare")
ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) }
.forEach { tz -> put(tz, tz) }
.forEach { tz -> put(tz, tz) }
}
// The Time command
@ -336,7 +336,7 @@ class WorldTime : AbstractModule() {
// Date/Time Format
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.

View file

@ -34,4 +34,4 @@ package net.thauvin.erik.mobibot.msg
* The `ErrorMessage` class.
*/
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.
*/
open class Message @JvmOverloads constructor(
var msg: String,
var color: String = DEFAULT_COLOR,
var isNotice: Boolean = false,
isError: Boolean = false,
var isPrivate: Boolean = false
var msg: String,
var color: String = DEFAULT_COLOR,
var isNotice: Boolean = false,
isError: Boolean = false,
var isPrivate: Boolean = false
) {
companion object {
var DEFAULT_COLOR = Constants.EMPTY

View file

@ -34,5 +34,5 @@ package net.thauvin.erik.mobibot.msg
* The `NoticeMessage` class.
*/
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.
*/
@Suppress("unused")
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 org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.Timer
import java.util.*
/**
* Social Manager.

View file

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

View file

@ -31,7 +31,7 @@
package net.thauvin.erik.mobibot.social
import java.util.TimerTask
import java.util.*
class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() {
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.links.Comment
import net.thauvin.erik.mobibot.commands.links.View
import net.thauvin.erik.mobibot.modules.Dice
import net.thauvin.erik.mobibot.modules.Joke
import net.thauvin.erik.mobibot.modules.Lookup
import net.thauvin.erik.mobibot.modules.RockPaperScissors
import net.thauvin.erik.mobibot.modules.War
import net.thauvin.erik.mobibot.modules.*
import org.testng.annotations.Test
import java.util.Properties
import java.util.*
class AddonsTest {
private val p = Properties().apply {
@ -80,11 +76,11 @@ class AddonsTest {
assertThat(addons.names.ops, "names.ops").containsExactly("cycle")
assertThat(addons.names.commands, "names.command").containsExactly(
"joke",
"rock",
"paper",
"scissors",
"ignore"
"joke",
"rock",
"paper",
"scissors",
"ignore"
)
}
}

View file

@ -46,9 +46,9 @@ object ExceptionSanitizer {
with(this) {
if (!cause?.message.isNullOrBlank()) {
return ModuleException(
debugMessage,
cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate),
this
debugMessage,
cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate),
this
)
} else if (!message.isNullOrBlank()) {
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.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.nio.file.Files
import java.nio.file.Paths
import java.util.Properties
import java.util.*
/**
* Access to `local.properties`.

View file

@ -68,7 +68,7 @@ class PinboardTest : LocalProperties() {
private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean {
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 {
if (!response.contains(it)) {

View file

@ -66,15 +66,14 @@ import java.io.File
import java.io.IOException
import java.net.URL
import java.time.LocalDateTime
import java.util.Calendar
import java.util.Properties
import java.util.*
/**
* The `Utils Test` class.
*/
class UtilsTest {
private val ascii =
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
private val cal = Calendar.getInstance()
private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0)
private val test = "This is a test."
@ -90,7 +89,7 @@ class UtilsTest {
val sep = '/'
val url = "https://erik.thauvin.net"
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$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep")
}
@ -116,24 +115,24 @@ class UtilsTest {
fun textCapitaliseWords() {
assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.")
assertThat("Already Capitalized".capitalizeWords(), "already capitalized")
.isEqualTo("Already Capitalized")
.isEqualTo("Already Capitalized")
assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ")
}
@Test
fun testColorize() {
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()")
.isEqualTo(Colors.RED + ascii + Colors.NORMAL)
.isEqualTo(Colors.RED + ascii + Colors.NORMAL)
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("".colorize(Colors.RED), "colorize()").isEqualTo("")
assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii)
assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()")
.isEqualTo(Colors.NORMAL + " " + Colors.NORMAL)
.isEqualTo(Colors.NORMAL + " " + Colors.NORMAL)
}
@Test
@ -165,19 +164,19 @@ class UtilsTest {
fun testHelpCmdSyntax() {
val bot = "mobibot"
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)")
.isEqualTo("/msg $bot $bot $test /msg $bot $test $bot")
.isEqualTo("/msg $bot $bot $test /msg $bot $test $bot")
}
@Test
fun testHelpFormat() {
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)")
.isEqualTo(test.prependIndent())
.isEqualTo(test.prependIndent())
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 replace = arrayOf("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(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)")
.isEqualTo(test)
.isEqualTo(test)
}
@Test
@ -259,7 +258,7 @@ class UtilsTest {
@Test
fun testUnescapeXml() {
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"])
fun testToUptime() {
assertThat(
547800300076L.toUptime(),
"upTime(full)"
547800300076L.toUptime(),
"upTime(full)"
).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(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes")
assertThat(
1320300000L.toUptime(),
"upTime(weeks days hours minutes)"
1320300000L.toUptime(),
"upTime(weeks days hours minutes)"
).isEqualTo("2 weeks 1 day 6 hours 45 minutes")
assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes")
assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute")

View file

@ -48,13 +48,13 @@ class RecapTest {
assertThat(Recap.recaps, "Recap.recaps").all {
size().isEqualTo(Recap.MAX_RECAPS)
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)
.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)
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() {
assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog")
assertThat(
linksManager.fetchTitle("https://www.google.com/foo"),
"fetchTitle(Foo)"
linksManager.fetchTitle("https://www.google.com/foo"),
"fetchTitle(Foo)"
).isEqualTo(Constants.NO_TITLE)
}

View file

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

View file

@ -33,14 +33,7 @@ package net.thauvin.erik.mobibot.commands.seen
import assertk.all
import assertk.assertThat
import assertk.assertions.isEmpty
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 assertk.assertions.*
import org.testng.annotations.AfterClass
import org.testng.annotations.BeforeClass
import org.testng.annotations.Test

View file

@ -33,13 +33,7 @@ package net.thauvin.erik.mobibot.commands.tell
import assertk.all
import assertk.assertThat
import assertk.assertions.index
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 assertk.assertions.*
import org.testng.annotations.AfterClass
import org.testng.annotations.BeforeClass
import org.testng.annotations.Test

View file

@ -46,14 +46,14 @@ class EntriesUtilsTest {
private val links = buildList {
for (i in 0..5) {
add(
EntryLink(
"https://www.mobitopia.org/$i",
"Mobitopia$i",
"Skynx$i",
"JimH$i",
"#mobitopia$i",
listOf("tag1", "tag2", "tag3", "TAG4", "Tag5")
)
EntryLink(
"https://www.mobitopia.org/$i",
"Mobitopia$i",
"Skynx$i",
"JimH$i",
"#mobitopia$i",
listOf("tag1", "tag2", "tag3", "TAG4", "Tag5")
)
)
}
}
@ -67,7 +67,7 @@ class EntriesUtilsTest {
fun printLinkTest() {
for (i in links.indices) {
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 )")
}
@ -79,7 +79,7 @@ class EntriesUtilsTest {
fun printTagsTest() {
for (i in links.indices) {
assertThat(
printTags(i - 1, links[i]), "tag $i"
printTags(i - 1, links[i]), "tag $i"
).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.assertThat
import assertk.assertions.index
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 assertk.assertions.*
import com.rometools.rome.feed.synd.SyndCategory
import com.rometools.rome.feed.synd.SyndCategoryImpl
import org.testng.annotations.Test
import java.security.SecureRandom
import java.util.Date
import java.util.*
/**
* The `EntryUtilsTest` class.
@ -54,8 +48,8 @@ import java.util.Date
*/
class EntryLinkTest {
private val entryLink = EntryLink(
"https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia",
listOf("tag1", "tag2", "tag3", "TAG4", "Tag5")
"https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia",
listOf("tag1", "tag2", "tag3", "TAG4", "Tag5")
)
@Test(groups = ["entries"])
@ -123,12 +117,12 @@ class EntryLinkTest {
entryLink.setTags("+mobitopia")
entryLink.setTags("-mobitopia")
assertThat(
entryLink.formatTags(","),
"formatTags(',')"
entryLink.formatTags(","),
"formatTags(',')"
).isEqualTo("tag1,tag2,tag3,tag4,mobitopia")
entryLink.setTags("-tag4 tag5")
assertThat(
entryLink.formatTags(" ", ","), "formatTag(' ',',')"
entryLink.formatTags(" ", ","), "formatTag(' ',',')"
).isEqualTo(",tag1 tag2 tag3 mobitopia tag5")
val size = entryLink.tags.size
entryLink.setTags("")

View file

@ -33,18 +33,12 @@ package net.thauvin.erik.mobibot.entries
import assertk.all
import assertk.assertThat
import assertk.assertions.endsWith
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 assertk.assertions.*
import net.thauvin.erik.mobibot.Utils.today
import org.testng.annotations.BeforeSuite
import org.testng.annotations.Test
import java.nio.file.Paths
import java.util.Date
import java.util.*
import kotlin.io.path.deleteIfExists
import kotlin.io.path.fileSize
import kotlin.io.path.name

View file

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

View file

@ -34,7 +34,6 @@ import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.hasNoCause
import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf
import net.thauvin.erik.mobibot.LocalProperties
import org.testng.annotations.Test
@ -43,21 +42,21 @@ class ChatGptTest : LocalProperties() {
@Test(groups = ["modules"])
fun testApiKey() {
assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) }
.isInstanceOf(ModuleException::class.java)
.hasNoCause()
.isInstanceOf(ModuleException::class.java)
.hasNoCause()
}
@Test(groups = ["modules", "no-ci"])
fun testChat() {
val apiKey = getProperty(ChatGpt.API_KEY_PROP)
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")
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")
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"])
fun testConvertCurrency() {
assertThat(
convertCurrency("100 USD to EUR").msg,
"convertCurrency(100 USD to EUR)"
convertCurrency("100 USD to EUR").msg,
"convertCurrency(100 USD to EUR)"
).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex())
assertThat(
convertCurrency("1 USD to BTC").msg,
"convertCurrency(1 USD to BTC)"
convertCurrency("1 USD to BTC").msg,
"convertCurrency(1 USD to BTC)"
).matches("1 United States Dollar = 0\\.\\d+ Bitcoin".toRegex())
assertThat(
convertCurrency("100,000.00 GBP to BTC").msg,
"convertCurrency(100,000.00 GBP to BTC)"
convertCurrency("100,000.00 GBP to BTC").msg,
"convertCurrency(100,000.00 GBP to BTC)"
).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 {
prop(Message::msg).contains("You're kidding, right?")

View file

@ -42,12 +42,12 @@ class DiceTest {
fun testRoll() {
assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002")
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)")
.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)")
.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)")
.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.assertFailure
import assertk.assertThat
import assertk.assertions.contains
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 assertk.assertions.*
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
import net.thauvin.erik.mobibot.LocalProperties
import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
@ -56,19 +48,19 @@ class GoogleSearchTest : LocalProperties() {
@Test(groups = ["modules"])
fun testAPIKeys() {
assertThat(
searchGoogle("", "apikey", "cssKey").first(),
"searchGoogle(empty)"
searchGoogle("", "apikey", "cssKey").first(),
"searchGoogle(empty)"
).isInstanceOf(ErrorMessage::class.java)
assertFailure { searchGoogle("test", "", "apiKey") }
.isInstanceOf(ModuleException::class.java).hasNoCause()
.isInstanceOf(ModuleException::class.java).hasNoCause()
assertFailure { searchGoogle("test", "apiKey", "") }
.isInstanceOf(ModuleException::class.java).hasNoCause()
.isInstanceOf(ModuleException::class.java).hasNoCause()
assertFailure { searchGoogle("test", "apiKey", "cssKey") }
.isInstanceOf(ModuleException::class.java)
.hasMessage("API key not valid. Please pass a valid API key.")
.isInstanceOf(ModuleException::class.java)
.hasMessage("API key not valid. Please pass a valid API key.")
}
@Test(groups = ["no-ci", "modules"])

View file

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

View file

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

View file

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

View file

@ -33,14 +33,7 @@ package net.thauvin.erik.mobibot.modules
import assertk.all
import assertk.assertFailure
import assertk.assertThat
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.matches
import assertk.assertions.prop
import assertk.assertions.*
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
import net.thauvin.erik.mobibot.LocalProperties
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(1).prop(Message::msg).matches(buildMatch("Price").toRegex())
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())
symbol = "blahfoo"

View file

@ -33,16 +33,7 @@ package net.thauvin.erik.mobibot.modules
import assertk.all
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.contains
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 assertk.assertions.*
import net.aksingh.owmjapis.api.APIException
import net.aksingh.owmjapis.core.OWM
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("fr"), "country should France").isEqualTo(OWM.Country.FRANCE)
val country = OWM.Country.values()
val country = OWM.Country.entries.toTypedArray()
repeat(3) {
val rand = country[(country.indices).random()]
assertThat(getCountry(rand.value), rand.name).isEqualTo(rand)

View file

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

View file

@ -30,10 +30,8 @@
*/
package net.thauvin.erik.mobibot.modules
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.endsWith
import assertk.assertions.isSuccess
import assertk.assertions.matches
import assertk.assertions.startsWith
import net.thauvin.erik.mobibot.Utils.bold
@ -51,9 +49,9 @@ class WordTimeTest {
@Test(groups = ["modules"])
fun testTime() {
assertThat(time(), "time()").matches(
("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " +
"on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " +
"in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex()
("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " +
"on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " +
"in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex()
)
assertThat(time(""), "time()").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
#Sun Jul 02 02:19:45 PDT 2023
version.buildmeta=20230702021945
#Thu Jul 06 10:21:40 PDT 2023
version.buildmeta=20230706102140
version.major=0
version.minor=8
version.patch=0
version.prerelease=rc
version.project=mobibot
version.semver=0.8.0-rc+20230702021945
version.semver=0.8.0-rc+20230706102140