Ignored bin directory
This commit is contained in:
parent
f8de100dde
commit
208f3a82d9
100 changed files with 5 additions and 10574 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -54,8 +54,9 @@ atlassian-ide-plugin.xml
|
|||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
local.properties
|
||||
|
||||
bin
|
||||
deploy
|
||||
local.properties
|
||||
logs
|
||||
mobibot.properties
|
||||
out
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ log4j2.xml
|
||||
~
|
||||
~ Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ Redistributions of source code must retain the above copyright notice, this
|
||||
~ list of conditions and the following disclaimer.
|
||||
~
|
||||
~ Redistributions in binary form must reproduce the above copyright notice,
|
||||
~ this list of conditions and the following disclaimer in the documentation
|
||||
~ and/or other materials provided with the distribution.
|
||||
~
|
||||
~ Neither the name of this project nor the names of its contributors may be
|
||||
~ used to endorse or promote products derived from this software without
|
||||
~ specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<Configuration status="warn">
|
||||
<Appenders>
|
||||
<Console name="stderr" target="SYSTEM_ERR">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
<Console name="input" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{UNIX_MILLIS} %msg%n"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</Filters>
|
||||
</Console>
|
||||
<Console name="output" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{UNIX_MILLIS} >>>%msg%n"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</Filters>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="warn" additivity="false">
|
||||
<AppenderRef ref="stderr"/>
|
||||
</Root>
|
||||
<logger level="debug" name="org.pircbotx.InputParser" additivity="false">
|
||||
<appender-ref ref="input"/>
|
||||
<appender-ref ref="stderr" level="warn"/>
|
||||
</logger>
|
||||
<logger level="debug" name="org.pircbotx.output.OutputRaw" additivity="false">
|
||||
<appender-ref ref="output"/>
|
||||
<appender-ref ref="stderr" level="warn"/>
|
||||
</logger>
|
||||
<logger level="trace" name="net.thauvin.erik.mobibot" additivity="false">
|
||||
<appender-ref ref="stderr"/>
|
||||
</logger>
|
||||
</Loggers>
|
||||
</Configuration>
|
|
@ -1,190 +0,0 @@
|
|||
/*
|
||||
* Addons.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.notContains
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksManager
|
||||
import net.thauvin.erik.mobibot.modules.AbstractModule
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Modules and Commands addons.
|
||||
*/
|
||||
class Addons(private val props: Properties) {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Addons::class.java)
|
||||
private val disabledModules = props.getProperty("disabled-modules", "").split(LinksManager.TAG_MATCH)
|
||||
private val disableCommands = props.getProperty("disabled-commands", "").split(LinksManager.TAG_MATCH)
|
||||
|
||||
val commands: MutableList<AbstractCommand> = mutableListOf()
|
||||
val modules: MutableList<AbstractModule> = mutableListOf()
|
||||
val names = Names
|
||||
|
||||
/**
|
||||
* Add a module with properties.
|
||||
*/
|
||||
fun add(module: AbstractModule): Boolean {
|
||||
var enabled = false
|
||||
with(module) {
|
||||
if (disabledModules.notContains(name, true)) {
|
||||
if (hasProperties()) {
|
||||
propertyKeys.forEach {
|
||||
setProperty(it, props.getProperty(it, ""))
|
||||
}
|
||||
}
|
||||
|
||||
if (isEnabled) {
|
||||
modules.add(this)
|
||||
names.modules.add(name)
|
||||
names.commands.addAll(commands)
|
||||
enabled = true
|
||||
} else {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Module $name is disabled.")
|
||||
}
|
||||
names.disabledModules.add(name)
|
||||
}
|
||||
} else {
|
||||
names.disabledModules.add(name)
|
||||
}
|
||||
}
|
||||
return enabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a command with properties.
|
||||
*/
|
||||
fun add(command: AbstractCommand): Boolean {
|
||||
var enabled = false
|
||||
with(command) {
|
||||
if (disableCommands.notContains(name, true)) {
|
||||
if (properties.isNotEmpty()) {
|
||||
properties.keys.forEach {
|
||||
setProperty(it, props.getProperty(it, ""))
|
||||
}
|
||||
}
|
||||
if (isEnabled()) {
|
||||
commands.add(this)
|
||||
if (isVisible) {
|
||||
if (isOpOnly) {
|
||||
names.ops.add(name)
|
||||
} else {
|
||||
names.commands.add(name)
|
||||
}
|
||||
}
|
||||
enabled = true
|
||||
} else {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Command $name is disabled.")
|
||||
}
|
||||
names.disabledCommands.add(name)
|
||||
}
|
||||
} else {
|
||||
names.disabledCommands.add(name)
|
||||
}
|
||||
}
|
||||
return enabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command or module.
|
||||
*/
|
||||
fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean {
|
||||
val cmds = if (event is PrivateMessageEvent) commands else commands.filter { it.isPublic }
|
||||
for (command in cmds) {
|
||||
if (command.name.startsWith(cmd)) {
|
||||
command.commandResponse(channel, args, event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
val mods = if (event is PrivateMessageEvent) modules.filter { it.isPrivateMsgEnabled } else modules
|
||||
for (module in mods) {
|
||||
if (module.commands.contains(cmd)) {
|
||||
module.commandResponse(channel, cmd, args, event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a command.
|
||||
*/
|
||||
fun match(channel: String, event: GenericMessageEvent): Boolean {
|
||||
for (command in commands) {
|
||||
if (command.matches(event.message)) {
|
||||
command.commandResponse(channel, event.message, event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Commands and Modules help.
|
||||
*/
|
||||
fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
for (command in commands) {
|
||||
if (command.isVisible && command.name.startsWith(topic)) {
|
||||
return command.helpResponse(channel, topic, event)
|
||||
}
|
||||
}
|
||||
for (module in modules) {
|
||||
if (module.commands.contains(topic)) {
|
||||
return module.helpResponse(event)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds commands and modules names.
|
||||
*/
|
||||
object Names {
|
||||
val modules: MutableList<String> = mutableListOf()
|
||||
val disabledModules: MutableList<String> = mutableListOf()
|
||||
val commands: MutableList<String> = mutableListOf()
|
||||
val disabledCommands: MutableList<String> = mutableListOf()
|
||||
val ops: MutableList<String> = mutableListOf()
|
||||
|
||||
fun sort() {
|
||||
modules.sort()
|
||||
disabledModules.sort()
|
||||
commands.sort()
|
||||
disabledCommands.sort()
|
||||
ops.sort()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Constants.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
/**
|
||||
* The `Constants`.
|
||||
*/
|
||||
object Constants {
|
||||
/**
|
||||
* The connect/read timeout in ms.
|
||||
*/
|
||||
const val CONNECT_TIMEOUT = 5000
|
||||
|
||||
/**
|
||||
* Debug command line argument.
|
||||
*/
|
||||
const val DEBUG_ARG = "debug"
|
||||
|
||||
/**
|
||||
* Default IRC Port.
|
||||
*/
|
||||
const val DEFAULT_PORT = 6667
|
||||
|
||||
/**
|
||||
* Default IRC Server.
|
||||
*/
|
||||
const val DEFAULT_SERVER = "irc.libera.chat"
|
||||
|
||||
/**
|
||||
* CLI command for usage.
|
||||
*/
|
||||
const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar"
|
||||
|
||||
/**
|
||||
* 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"
|
||||
|
||||
/**
|
||||
* The help command.
|
||||
*/
|
||||
const val HELP_CMD = "help"
|
||||
|
||||
/**
|
||||
* The link command.
|
||||
*/
|
||||
const val LINK_CMD = "L"
|
||||
|
||||
/**
|
||||
* The empty title string.
|
||||
*/
|
||||
const val NO_TITLE = "No Title"
|
||||
|
||||
/**
|
||||
* Properties command line argument.
|
||||
*/
|
||||
const val PROPS_ARG = "properties"
|
||||
|
||||
/**
|
||||
* The tag command
|
||||
*/
|
||||
const val TAG_CMD = "T"
|
||||
|
||||
/**
|
||||
* The timer delay in minutes.
|
||||
*/
|
||||
const val TIMER_DELAY = 10L
|
||||
|
||||
/**
|
||||
* Properties version line argument.
|
||||
*/
|
||||
const val VERSION_ARG = "version"
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* FeedReader.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import com.rometools.rome.io.FeedException
|
||||
import com.rometools.rome.io.SyndFeedInput
|
||||
import com.rometools.rome.io.XmlReader
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.entries.FeedsManager
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Reads an RSS feed.
|
||||
*/
|
||||
class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable {
|
||||
private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java)
|
||||
|
||||
/**
|
||||
* Fetches the Feed's items.
|
||||
*/
|
||||
override fun run() {
|
||||
try {
|
||||
readFeed(url).forEach {
|
||||
event.sendMessage("", it)
|
||||
}
|
||||
} catch (e: FeedException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e)
|
||||
event.sendMessage("An error has occurred while parsing the feed: ${e.message}")
|
||||
} catch (e: IOException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e)
|
||||
event.sendMessage("An IO error has occurred while fetching the feed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Throws(FeedException::class, IOException::class)
|
||||
fun readFeed(url: String, maxItems: Int = 5): List<Message> {
|
||||
val messages = mutableListOf<Message>()
|
||||
val input = SyndFeedInput()
|
||||
XmlReader(URL(url).openStream()).use { reader ->
|
||||
val feed = input.build(reader)
|
||||
val items = feed.entries
|
||||
if (items.isEmpty()) {
|
||||
messages.add(NoticeMessage("There is currently nothing to view."))
|
||||
} else {
|
||||
items.take(maxItems).forEach {
|
||||
messages.add(NoticeMessage(it.title))
|
||||
messages.add(NoticeMessage(helpFormat(it.link.green(), false)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,421 +0,0 @@
|
|||
/*
|
||||
* Mobibot.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import kotlinx.cli.ArgParser
|
||||
import kotlinx.cli.ArgType
|
||||
import kotlinx.cli.default
|
||||
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.getIntProperty
|
||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
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.*
|
||||
import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap
|
||||
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.*
|
||||
import net.thauvin.erik.semver.Version
|
||||
import org.pircbotx.Configuration
|
||||
import org.pircbotx.PircBotX
|
||||
import org.pircbotx.hooks.ListenerAdapter
|
||||
import org.pircbotx.hooks.events.*
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.*
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt")
|
||||
class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() {
|
||||
// The bot configuration.
|
||||
private val config: Configuration
|
||||
|
||||
// Commands and Modules
|
||||
private val addons: Addons
|
||||
|
||||
// Seen command
|
||||
private val seen: Seen
|
||||
|
||||
// Tell command
|
||||
private val tell: Tell
|
||||
|
||||
/** Logger. */
|
||||
val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java)
|
||||
|
||||
/**
|
||||
* Connects to the server and joins the channel.
|
||||
*/
|
||||
fun connect() {
|
||||
PircBotX(config).startBot()
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the default help.
|
||||
*/
|
||||
private fun helpDefault(event: GenericMessageEvent) {
|
||||
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)
|
||||
)
|
||||
)
|
||||
event.sendMessage("The commands are:")
|
||||
event.sendList(addons.names.commands, 8, isBold = true, isIndent = true)
|
||||
if (event.isChannelOp(channel)) {
|
||||
if (addons.names.disabledCommands.isNotEmpty()) {
|
||||
event.sendMessage("The disabled commands are:")
|
||||
event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true)
|
||||
}
|
||||
event.sendMessage("The op commands are:")
|
||||
event.sendList(addons.names.ops, 8, isBold = true, isIndent = true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the default, commands or modules help.
|
||||
*/
|
||||
private fun helpResponse(event: GenericMessageEvent, topic: String) {
|
||||
if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) {
|
||||
helpDefault(event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAction(event: ActionEvent?) {
|
||||
event?.channel?.let {
|
||||
if (channel == it.name) {
|
||||
event.user?.let { user ->
|
||||
storeRecap(user.nick, event.action, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisconnect(event: DisconnectEvent?) {
|
||||
event?.let {
|
||||
with(event.getBot<PircBotX>()) {
|
||||
LinksManager.socialManager.notification("$nick disconnected from $serverHostname")
|
||||
seen.add(userChannelDao.getChannel(channel).users)
|
||||
}
|
||||
}
|
||||
LinksManager.socialManager.shutdown()
|
||||
}
|
||||
|
||||
override fun onPrivateMessage(event: PrivateMessageEvent?) {
|
||||
event?.user?.let { user ->
|
||||
if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}")
|
||||
val cmds = event.message.trim().split(" ".toRegex(), 2)
|
||||
val cmd = cmds[0].lowercase()
|
||||
val args = cmds.lastOrEmpty().trim()
|
||||
if (cmd.startsWith(Constants.HELP_CMD)) { // help
|
||||
helpResponse(event, args)
|
||||
} else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module
|
||||
helpDefault(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onJoin(event: JoinEvent?) {
|
||||
event?.user?.let { user ->
|
||||
with(event.getBot<PircBotX>()) {
|
||||
if (user.nick == nick) {
|
||||
LinksManager.socialManager.notification(
|
||||
"$nick has joined ${event.channel.name} on $serverHostname"
|
||||
)
|
||||
seen.add(userChannelDao.getChannel(channel).users)
|
||||
} else {
|
||||
tell.send(event)
|
||||
seen.add(user.nick)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMessage(event: MessageEvent?) {
|
||||
event?.user?.let { user ->
|
||||
tell.send(event)
|
||||
if (event.message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command>
|
||||
if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}")
|
||||
val cmds = event.message.substring(event.bot().nick.length + 1).trim().split(" ".toRegex(), 2)
|
||||
val cmd = cmds[0].lowercase()
|
||||
val args = cmds.lastOrEmpty().trim()
|
||||
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
|
||||
helpResponse(event, args)
|
||||
} else {
|
||||
// Execute module or command
|
||||
addons.exec(channel, cmd, args, event)
|
||||
}
|
||||
} else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc.
|
||||
if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}")
|
||||
}
|
||||
storeRecap(user.nick, event.message, false)
|
||||
seen.add(user.nick)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNickChange(event: NickChangeEvent?) {
|
||||
event?.let {
|
||||
tell.send(event)
|
||||
if (!it.oldNick.equals(it.newNick, true)) {
|
||||
seen.add(it.oldNick)
|
||||
}
|
||||
seen.add(it.newNick)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPart(event: PartEvent?) {
|
||||
event?.user?.let { user ->
|
||||
with(event.getBot<PircBotX>()) {
|
||||
if (user.nick == nick) {
|
||||
LinksManager.socialManager.notification(
|
||||
"$nick has left ${event.channel.name} on $serverHostname"
|
||||
)
|
||||
seen.add(userChannelDao.getChannel(channel).users)
|
||||
} else {
|
||||
seen.add(user.nick)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onQuit(event: QuitEvent?) {
|
||||
event?.user?.let { user ->
|
||||
seen.add(user.nick)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Throws(Exception::class)
|
||||
fun main(args: Array<String>) {
|
||||
// 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"
|
||||
).default(false)
|
||||
val property by parser.option(
|
||||
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"
|
||||
).default(false)
|
||||
|
||||
// Parse the command line
|
||||
parser.parse(args)
|
||||
|
||||
if (version) {
|
||||
// Output the version
|
||||
println(
|
||||
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" +
|
||||
" (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})"
|
||||
)
|
||||
println(ReleaseInfo.WEBSITE)
|
||||
} else {
|
||||
// Load the properties
|
||||
val p = Properties()
|
||||
try {
|
||||
Files.newInputStream(
|
||||
Paths.get(property)
|
||||
).use { fis ->
|
||||
p.load(fis)
|
||||
}
|
||||
} catch (ignore: FileNotFoundException) {
|
||||
System.err.println("Unable to find properties file.")
|
||||
exitProcess(1)
|
||||
} catch (ignore: IOException) {
|
||||
System.err.println("Unable to open properties file.")
|
||||
exitProcess(1)
|
||||
}
|
||||
val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase())
|
||||
val channel = p.getProperty("channel")
|
||||
val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar)
|
||||
|
||||
// Redirect stdout and stderr
|
||||
if (!debug) {
|
||||
try {
|
||||
val stdout = PrintStream(
|
||||
BufferedOutputStream(
|
||||
FileOutputStream(
|
||||
logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true
|
||||
)
|
||||
), true
|
||||
)
|
||||
System.setOut(stdout)
|
||||
} catch (ignore: IOException) {
|
||||
System.err.println("Unable to open output (stdout) log file.")
|
||||
exitProcess(1)
|
||||
}
|
||||
try {
|
||||
val stderr = PrintStream(
|
||||
BufferedOutputStream(
|
||||
FileOutputStream("$logsDir$nickname.err", true)
|
||||
), true
|
||||
)
|
||||
System.setErr(stderr)
|
||||
} catch (ignore: IOException) {
|
||||
System.err.println("Unable to open error (stderr) log file.")
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Start the bot
|
||||
Mobibot(nickname, channel, logsDir, p).connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the bot.
|
||||
*/
|
||||
init {
|
||||
val ircServer = p.getProperty("server", Constants.DEFAULT_SERVER)
|
||||
config = Configuration.Builder().apply {
|
||||
name = nickname
|
||||
login = p.getProperty("login", nickname)
|
||||
realName = p.getProperty("realname", nickname)
|
||||
addServer(
|
||||
ircServer,
|
||||
p.getIntProperty("port", Constants.DEFAULT_PORT)
|
||||
)
|
||||
addAutoJoinChannel(channel)
|
||||
addListener(this@Mobibot)
|
||||
version = "${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}"
|
||||
isAutoNickChange = true
|
||||
val identPwd = p.getProperty("ident")
|
||||
if (!identPwd.isNullOrBlank()) {
|
||||
nickservPassword = identPwd
|
||||
}
|
||||
val identNick = p.getProperty("ident-nick")
|
||||
if (!identNick.isNullOrBlank()) {
|
||||
nickservNick = identNick
|
||||
}
|
||||
val identMsg = p.getProperty("ident-msg")
|
||||
if (!identMsg.isNullOrBlank()) {
|
||||
nickservCustomMessage = identMsg
|
||||
}
|
||||
isAutoReconnect = true
|
||||
|
||||
//socketConnectTimeout = Constants.CONNECT_TIMEOUT
|
||||
//socketTimeout = Constants.CONNECT_TIMEOUT
|
||||
//messageDelay = StaticDelay(500)
|
||||
}.buildConfiguration()
|
||||
|
||||
// Load the current entries
|
||||
with(LinksManager) {
|
||||
entries.channel = channel
|
||||
entries.ircServer = ircServer
|
||||
entries.logsDir = logsDirPath
|
||||
entries.backlogs = p.getProperty("backlogs", "")
|
||||
entries.load()
|
||||
|
||||
// Set up pinboard
|
||||
pinboard.setApiToken(p.getProperty("pinboard-api-token", ""))
|
||||
}
|
||||
|
||||
addons = Addons(p)
|
||||
|
||||
// Load the commands
|
||||
addons.add(ChannelFeed(channel.removePrefix("#")))
|
||||
addons.add(Comment())
|
||||
addons.add(Cycle())
|
||||
addons.add(Die())
|
||||
addons.add(Ignore())
|
||||
addons.add(LinksManager())
|
||||
addons.add(Me())
|
||||
addons.add(Modules(addons.names.modules, addons.names.disabledModules))
|
||||
addons.add(Msg())
|
||||
addons.add(Nick())
|
||||
addons.add(Posting())
|
||||
addons.add(Recap())
|
||||
addons.add(Say())
|
||||
|
||||
// Seen command
|
||||
seen = Seen("${logsDirPath}${nickname}-seen.ser")
|
||||
addons.add(seen)
|
||||
|
||||
addons.add(Tags())
|
||||
|
||||
// Tell command
|
||||
tell = Tell("${logsDirPath}${nickname}.ser")
|
||||
addons.add(tell)
|
||||
|
||||
addons.add(Users())
|
||||
addons.add(Versions())
|
||||
addons.add(View())
|
||||
|
||||
// Load social modules
|
||||
LinksManager.socialManager.add(addons, Mastodon())
|
||||
|
||||
// Load the modules
|
||||
addons.add(Calc())
|
||||
addons.add(ChatGpt())
|
||||
addons.add(CryptoPrices())
|
||||
addons.add(CurrencyConverter())
|
||||
addons.add(Dice())
|
||||
addons.add(GoogleSearch())
|
||||
addons.add(Info(tell, seen))
|
||||
addons.add(Joke())
|
||||
addons.add(Lookup())
|
||||
addons.add(Ping())
|
||||
addons.add(RockPaperScissors())
|
||||
addons.add(StockQuote())
|
||||
addons.add(War())
|
||||
addons.add(Weather2())
|
||||
addons.add(WolframAlpha())
|
||||
addons.add(WorldTime())
|
||||
|
||||
// Sort the addons
|
||||
addons.names.sort()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* Pinboard.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import net.thauvin.erik.pinboard.PinboardPoster
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Handles posts to pinboard.in.
|
||||
*/
|
||||
class Pinboard {
|
||||
private val poster = PinboardPoster()
|
||||
|
||||
/**
|
||||
* Adds a pin.
|
||||
*/
|
||||
fun addPin(ircServer: String, entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
with(entry) {
|
||||
poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pinboard API token.
|
||||
*/
|
||||
fun setApiToken(apiToken: String) {
|
||||
poster.apiToken = apiToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a pin.
|
||||
*/
|
||||
fun deletePin(entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
poster.deletePin(entry.link)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a pin.
|
||||
*/
|
||||
fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
with(entry) {
|
||||
if (oldUrl != link) {
|
||||
poster.deletePin(oldUrl)
|
||||
}
|
||||
poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date to a UTC timestamp.
|
||||
*/
|
||||
private fun Date.toTimestamp(): String {
|
||||
return ZonedDateTime.ofInstant(
|
||||
toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault()
|
||||
).format(DateTimeFormatter.ISO_INSTANT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the tags for pinboard.
|
||||
*/
|
||||
private fun EntryLink.formatTags(): String {
|
||||
return nick + formatTags(",", ",")
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pinboard.in extended attribution line.
|
||||
*/
|
||||
private fun EntryLink.postedBy(ircServer: String): String {
|
||||
return "Posted by $nick on $channel ( $ircServer )"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,439 +0,0 @@
|
|||
/*
|
||||
* Utils.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
||||
import net.thauvin.erik.urlencoder.UrlEncoderUtil
|
||||
import org.jsoup.Jsoup
|
||||
import org.pircbotx.Colors
|
||||
import org.pircbotx.PircBotX
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import java.io.*
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.fileSize
|
||||
|
||||
/**
|
||||
* Miscellaneous utilities.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
object Utils {
|
||||
private val searchFlags = arrayOf("%c", "%n")
|
||||
|
||||
/**
|
||||
* Prepends a prefix if not present.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.prefixIfMissing(prefix: Char): String {
|
||||
return if (first() != prefix) {
|
||||
"$prefix${this}"
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a suffix to the end of the String if not present.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.appendIfMissing(suffix: Char): String {
|
||||
return if (last() != suffix) {
|
||||
"$this${suffix}"
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given int bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Int.bold(): String = toString().bold()
|
||||
|
||||
/**
|
||||
* Makes the given long bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Long.bold(): String = toString().bold()
|
||||
|
||||
/**
|
||||
* Makes the given string bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.bold(): String = colorize(Colors.BOLD)
|
||||
|
||||
/**
|
||||
* Returns the [PircBotX] instance.
|
||||
*/
|
||||
fun GenericMessageEvent.bot(): PircBotX {
|
||||
return getBot() as PircBotX
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalize a string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.capitalise(): String = lowercase().replaceFirstChar { it.uppercase() }
|
||||
|
||||
/**
|
||||
* Capitalize words
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() }
|
||||
|
||||
/**
|
||||
* Colorize a string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.colorize(color: String): String {
|
||||
return when {
|
||||
isNullOrEmpty() -> {
|
||||
""
|
||||
}
|
||||
|
||||
color == DEFAULT_COLOR -> {
|
||||
this
|
||||
}
|
||||
|
||||
Colors.BOLD == color || Colors.REVERSE == color -> {
|
||||
color + this + color
|
||||
}
|
||||
|
||||
else -> {
|
||||
color + this + Colors.NORMAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string cyan.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.cyan(): String = colorize(Colors.CYAN)
|
||||
|
||||
/**
|
||||
* URL encodes the given string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.encodeUrl(): String = UrlEncoderUtil.encode(this)
|
||||
|
||||
/**
|
||||
* Returns a property as an int.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Properties.getIntProperty(key: String, defaultValue: Int): Int {
|
||||
return getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string green.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.green(): String = colorize(Colors.DARK_GREEN)
|
||||
|
||||
/**
|
||||
* Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's
|
||||
* nick.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun helpCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String {
|
||||
val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick)
|
||||
return text.replaceEach(searchFlags, replace)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted help string.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String {
|
||||
val s = if (isBold) help.bold() else help
|
||||
return if (isIndent) s.prependIndent() else s
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the specified user is an operator on the [channel].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun GenericMessageEvent.isChannelOp(channel: String): Boolean {
|
||||
return this.bot().userChannelDao.getChannel(channel).isOp(this.user)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if a HTTP status code indicates a successful response.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Int.isHttpSuccess() = this in 200..399
|
||||
|
||||
/**
|
||||
* Returns the last item of a list of strings or empty if none.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun List<String>.lastOrEmpty(): String {
|
||||
return if (this.size >= 2) {
|
||||
this.last()
|
||||
} else
|
||||
""
|
||||
}
|
||||
|
||||
/**
|
||||
* Load serial data from file.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any {
|
||||
val serialFile = Paths.get(file)
|
||||
if (serialFile.exists() && serialFile.fileSize() > 0) {
|
||||
try {
|
||||
ObjectInputStream(
|
||||
BufferedInputStream(Files.newInputStream(serialFile))
|
||||
).use { input ->
|
||||
if (logger.isDebugEnabled) logger.debug("Loading the ${description}.")
|
||||
return input.readObject()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
logger.error("An IO error occurred loading the ${description}.", e)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
logger.error("An error occurred loading the ${description}.", e)
|
||||
}
|
||||
}
|
||||
return default
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the list does not contain the given string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun List<String>.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) }
|
||||
|
||||
/**
|
||||
* Obfuscates the given string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.obfuscate(): String {
|
||||
return if (isNotBlank()) {
|
||||
"x".repeat(length)
|
||||
} else this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plural form of a word, if count > 1.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.plural(count: Long): String {
|
||||
return if (count > 1) "${this}s" else this
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string red.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.red(): String = colorize(Colors.RED)
|
||||
|
||||
/**
|
||||
* Replaces all occurrences of Strings within another String.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.replaceEach(search: Array<out String>, replace: Array<out String>): String {
|
||||
var result = this
|
||||
if (search.size == replace.size) {
|
||||
search.forEachIndexed { i, s ->
|
||||
result = result.replace(s, replace[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string reverse color.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.reverseColor(): String = colorize(Colors.REVERSE)
|
||||
|
||||
/**
|
||||
* Save data
|
||||
*/
|
||||
@JvmStatic
|
||||
fun saveSerialData(file: String, data: Any, logger: Logger, description: String) {
|
||||
try {
|
||||
BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos ->
|
||||
ObjectOutputStream(bos).use { output ->
|
||||
if (logger.isDebugEnabled) logger.debug("Saving the ${description}.")
|
||||
output.writeObject(data)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
logger.error("Unable to save the ${description}.", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a formatted commands/modules, etc. list.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun GenericMessageEvent.sendList(
|
||||
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
|
||||
),
|
||||
)
|
||||
i += maxPerLine
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a [message].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun GenericMessageEvent.sendMessage(channel: String, message: Message) {
|
||||
if (message.isNotice) {
|
||||
bot().sendIRC().notice(user.nick, message.msg.colorize(message.color))
|
||||
} else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) {
|
||||
respondPrivateMessage(message.msg.colorize(message.color))
|
||||
} else {
|
||||
bot().sendIRC().message(channel, message.msg.colorize(message.color))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a response as a private message or notice.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun GenericMessageEvent.sendMessage(message: String) {
|
||||
if (this is PrivateMessageEvent) {
|
||||
respondPrivateMessage(message)
|
||||
} else {
|
||||
bot().sendIRC().notice(user.nick, message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns today's date.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun today(): String = LocalDateTime.now().toIsoLocalDate()
|
||||
|
||||
/**
|
||||
* Converts a string to an int.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.toIntOrDefault(defaultValue: Int): Int {
|
||||
return try {
|
||||
toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date as an ISO local date string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Date.toIsoLocalDate(): String {
|
||||
return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toIsoLocalDate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date as an ISO local date string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun LocalDateTime.toIsoLocalDate(): String = format(DateTimeFormatter.ISO_LOCAL_DATE)
|
||||
|
||||
/**
|
||||
* Returns the specified date formatted as `yyyy-MM-dd HH:mm`.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Date.toUtcDateTime(): String {
|
||||
return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toUtcDateTime()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date formatted as `yyyy-MM-dd HH:mm`.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||
|
||||
/**
|
||||
* Makes the given string bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.underline(): String = colorize(Colors.UNDERLINE)
|
||||
|
||||
|
||||
/**
|
||||
* Converts XML/XHTML entities to plain text.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.unescapeXml(): String = Jsoup.parse(this).text()
|
||||
|
||||
/**
|
||||
* Reads contents of a URL.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
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"
|
||||
)
|
||||
return if (connection.responseCode.isHttpSuccess()) {
|
||||
UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() })
|
||||
} else {
|
||||
UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the [URL.reader] response code and body text.
|
||||
*/
|
||||
data class UrlReaderResponse(val responseCode: Int, val body: String)
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* AbstractCommand.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
abstract class AbstractCommand {
|
||||
abstract val name: String
|
||||
abstract val help: List<String>
|
||||
abstract val isOpOnly: Boolean
|
||||
abstract val isPublic: Boolean
|
||||
abstract val isVisible: Boolean
|
||||
|
||||
val properties: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent)
|
||||
|
||||
open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
if (!isOpOnly || isOpOnly == event.isChannelOp(channel)) {
|
||||
for (h in help) {
|
||||
event.sendMessage(helpCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic))
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open fun initProperties(vararg keys: String) {
|
||||
keys.forEach {
|
||||
properties[it] = ""
|
||||
}
|
||||
}
|
||||
|
||||
open fun isEnabled(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
open fun matches(message: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
open fun setProperty(key: String, value: String) {
|
||||
properties[key] = value
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* ChannelFeed.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.FeedReader
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class ChannelFeed(channel: String) : AbstractCommand() {
|
||||
override val name = channel
|
||||
override val help = listOf("To list the last 5 posts from the channel's weblog feed:", helpFormat("%c $channel"))
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val FEED_PROP = "feed"
|
||||
}
|
||||
|
||||
init {
|
||||
initProperties(FEED_PROP)
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isEnabled()) {
|
||||
properties[FEED_PROP]?.let { FeedReader(it, event).run() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEnabled(): Boolean {
|
||||
return !properties[FEED_PROP].isNullOrBlank()
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Cycle.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Cycle : AbstractCommand() {
|
||||
private val wait = 10
|
||||
override val name = "cycle"
|
||||
override val help = listOf("To have the bot leave the channel and come back:", helpFormat("%c $name"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
runBlocking {
|
||||
launch {
|
||||
sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!")
|
||||
userChannelDao.getChannel(channel).send().part()
|
||||
delay(wait * 1000L)
|
||||
sendIRC().joinChannel(channel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Die.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Die : AbstractCommand() {
|
||||
override val name = "die"
|
||||
override val help = emptyList<String>()
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = false
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
if (event.isChannelOp(channel) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) {
|
||||
sendIRC().message(channel, "${event.user?.nick} has just signed my death sentence.")
|
||||
stopBotReconnect()
|
||||
sendIRC().quitServer("The Bot is Out There!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DIE_PROP = "die"
|
||||
}
|
||||
|
||||
init {
|
||||
initProperties(DIE_PROP)
|
||||
}
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* Ignore.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksManager
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Ignore : AbstractCommand() {
|
||||
private val me = "me"
|
||||
|
||||
init {
|
||||
initProperties(IGNORE_PROP)
|
||||
}
|
||||
|
||||
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")
|
||||
)
|
||||
private val helpOp = help.plus(
|
||||
arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]"))
|
||||
)
|
||||
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val IGNORE_CMD = "ignore"
|
||||
const val IGNORE_PROP = IGNORE_CMD
|
||||
private val ignored = mutableSetOf<String>()
|
||||
|
||||
@JvmStatic
|
||||
fun isNotIgnored(nick: String): Boolean {
|
||||
return !ignored.contains(nick.lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val isMe = args.trim().equals(me, true)
|
||||
if (isMe || !event.isChannelOp(channel)) {
|
||||
val nick = event.user.nick.lowercase()
|
||||
ignoreNick(nick, isMe, event)
|
||||
} else {
|
||||
ignoreOp(args, event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
return if (event.isChannelOp(channel)) {
|
||||
for (h in helpOp) {
|
||||
event.sendMessage(helpCmdSyntax(h, event.bot().nick, true))
|
||||
}
|
||||
true
|
||||
} else {
|
||||
super.helpResponse(channel, topic, event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ignoreNick(sender: String, isMe: Boolean, event: GenericMessageEvent) {
|
||||
if (isMe) {
|
||||
if (ignored.remove(sender)) {
|
||||
event.sendMessage("You are no longer ignored.")
|
||||
} else {
|
||||
ignored.add(sender)
|
||||
event.sendMessage("You are now ignored.")
|
||||
}
|
||||
} else {
|
||||
if (ignored.contains(sender)) {
|
||||
event.sendMessage("You are currently ignored.")
|
||||
} else {
|
||||
event.sendMessage("You are not currently ignored.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ignoreOp(args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotEmpty()) {
|
||||
val nicks = args.lowercase().split(" ")
|
||||
for (nick in nicks) {
|
||||
val ignore = if (me == nick) {
|
||||
nick.lowercase()
|
||||
} else {
|
||||
nick
|
||||
}
|
||||
if (!ignored.remove(ignore)) {
|
||||
ignored.add(ignore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ignored.isNotEmpty()) {
|
||||
event.sendMessage("The following nicks are ignored:")
|
||||
event.sendList(ignored.sorted(), 8, isIndent = true)
|
||||
} else {
|
||||
event.sendMessage("No one is currently ${"ignored".bold()}.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun setProperty(key: String, value: String) {
|
||||
super.setProperty(key, value)
|
||||
if (IGNORE_PROP == key) {
|
||||
ignored.addAll(value.split(LinksManager.TAG_MATCH))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Info.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.plural
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksManager
|
||||
import net.thauvin.erik.mobibot.commands.seen.Seen
|
||||
import net.thauvin.erik.mobibot.commands.tell.Tell
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.lang.management.ManagementFactory
|
||||
import kotlin.time.DurationUnit
|
||||
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()})"
|
||||
)
|
||||
override val name = "info"
|
||||
override val help = listOf("To view information about the bot:", helpFormat("%c $name"))
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Converts milliseconds to year month week day hour and minutes.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Long.toUptime(): String {
|
||||
this.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, seconds, _ ->
|
||||
val years = wholeDays / 365
|
||||
var days = wholeDays % 365
|
||||
val months = days / 30
|
||||
days %= 30
|
||||
val weeks = days / 7
|
||||
days %= 7
|
||||
|
||||
with(StringBuffer()) {
|
||||
if (years > 0) {
|
||||
append(years).append(" year".plural(years)).append(' ')
|
||||
}
|
||||
if (months > 0) {
|
||||
append(months).append(" month".plural(months)).append(' ')
|
||||
}
|
||||
if (weeks > 0) {
|
||||
append(weeks).append(" week".plural(weeks)).append(' ')
|
||||
}
|
||||
if (days > 0) {
|
||||
append(days).append(" day".plural(days)).append(' ')
|
||||
}
|
||||
if (hours > 0) {
|
||||
append(hours).append(" hour".plural(hours.toLong())).append(' ')
|
||||
}
|
||||
|
||||
if (minutes > 0) {
|
||||
append(minutes).append(" minute".plural(minutes.toLong()))
|
||||
} else {
|
||||
append(seconds).append(" second".plural(seconds.toLong()))
|
||||
}
|
||||
|
||||
return toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
event.sendList(allVersions, 1)
|
||||
val info = StringBuilder()
|
||||
info.append("Uptime: ")
|
||||
.append(ManagementFactory.getRuntimeMXBean().uptime.toUptime())
|
||||
.append(" [Entries: ")
|
||||
.append(LinksManager.entries.links.size)
|
||||
if (seen.isEnabled()) {
|
||||
info.append(", Seen: ").append(seen.count())
|
||||
}
|
||||
if (event.isChannelOp(channel)) {
|
||||
if (tell.isEnabled()) {
|
||||
info.append(", Messages: ").append(tell.size())
|
||||
}
|
||||
if (LinksManager.socialManager.entriesCount() > 0) {
|
||||
info.append(", Social: ").append(LinksManager.socialManager.entriesCount())
|
||||
}
|
||||
}
|
||||
info.append(", Recap: ").append(Recap.recaps.size).append(']')
|
||||
event.sendMessage(info.toString())
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Me.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Me : AbstractCommand() {
|
||||
override val name = "me"
|
||||
override val help = listOf("To have the bot perform an action:", helpFormat("%c $name <action>"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
event.bot().sendIRC().action(channel, args)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Modules.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Modules(private val modules: List<String>, private val disabledModules: List<String>) : AbstractCommand() {
|
||||
override val name = "modules"
|
||||
override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
if (modules.isEmpty()) {
|
||||
event.respondPrivateMessage("There are no enabled modules.")
|
||||
} else {
|
||||
event.respondPrivateMessage("The enabled modules are: ")
|
||||
event.sendList(modules, 7, isIndent = true)
|
||||
}
|
||||
if (disabledModules.isNotEmpty()) {
|
||||
event.respondPrivateMessage("The disabled modules are: ")
|
||||
event.sendList(disabledModules, 7, isIndent = true)
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Msg.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
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>")
|
||||
)
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
val msg = args.split(" ", limit = 2)
|
||||
if (args.length > 2) {
|
||||
event.bot().sendIRC().message(msg[0], msg[1])
|
||||
event.respondPrivateMessage("A message was sent to ${msg[0]}")
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Nick.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Nick : AbstractCommand() {
|
||||
override val name = "nick"
|
||||
override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <new_nick>"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
event.bot().sendIRC().changeNick(args)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Recap.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.time.Clock
|
||||
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")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val MAX_RECAPS = 10
|
||||
|
||||
@JvmField
|
||||
val recaps = mutableListOf<String>()
|
||||
|
||||
/**
|
||||
* Stores the last 10 public messages and actions.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun storeRecap(sender: String, message: String, isAction: Boolean) {
|
||||
recaps.add(
|
||||
LocalDateTime.now(Clock.systemUTC()).toUtcDateTime()
|
||||
+ " - $sender" + (if (isAction) " " else ": ") + message
|
||||
)
|
||||
if (recaps.size > MAX_RECAPS) {
|
||||
recaps.removeFirst()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (recaps.isNotEmpty()) {
|
||||
for (r in recaps) {
|
||||
event.sendMessage(r)
|
||||
}
|
||||
} else {
|
||||
event.sendMessage("Sorry, nothing to recap.")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Say.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Say : AbstractCommand() {
|
||||
override val name = "say"
|
||||
override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name <text>"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
event.bot().sendIRC().message(channel, args)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Users.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Users : AbstractCommand() {
|
||||
override val name = "users"
|
||||
override val help = listOf("To list the users present on the channel:", helpFormat("%c $name"))
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val ch = event.bot().userChannelDao.getChannel(channel)
|
||||
event.sendList(ch.users.map { if (it.channelsOpIn.contains(ch)) "@${it.nick}" else it.nick }, 8)
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Versions.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||
import org.pircbotx.PircBotX
|
||||
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}"
|
||||
)
|
||||
override val name = "versions"
|
||||
override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
event.sendList(allVersions, 1)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Comment.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
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:-")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val COMMAND = "comment"
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.substring(1).split("[.:]".toRegex(), 3)
|
||||
val entryIndex = cmds[0].toInt() - 1
|
||||
|
||||
if (entryIndex < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) {
|
||||
val entry: EntryLink = LinksManager.entries.links[entryIndex]
|
||||
val commentIndex = cmds[1].toInt() - 1
|
||||
if (commentIndex < entry.comments.size) {
|
||||
when (val cmd = cmds[2].trim()) {
|
||||
"" -> showComment(entry, entryIndex, commentIndex, event) // L1.1:
|
||||
"-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:-
|
||||
else -> {
|
||||
if (cmd.startsWith('?')) { // L1.1:?<author>
|
||||
changeAuthor(channel, cmd, entry, entryIndex, commentIndex, event)
|
||||
} else { // L1.1:<comment>
|
||||
setComment(cmd, entry, entryIndex, commentIndex, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
if (super.helpResponse(channel, topic, event)) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
event.sendMessage("To change a comment's author:")
|
||||
event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?<nick>"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches("^${Constants.LINK_CMD}\\d+\\.\\d+:.*".toRegex())
|
||||
}
|
||||
|
||||
private fun changeAuthor(
|
||||
channel: String,
|
||||
cmd: String,
|
||||
entry: EntryLink,
|
||||
entryIndex: Int,
|
||||
commentIndex: Int,
|
||||
event: GenericMessageEvent
|
||||
) {
|
||||
if (event.isChannelOp(channel) && cmd.length > 1) {
|
||||
val comment = entry.getComment(commentIndex)
|
||||
comment.nick = cmd.substring(1)
|
||||
event.sendMessage(printComment(entryIndex, commentIndex, comment))
|
||||
LinksManager.entries.save()
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to change the author of this comment for you.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteComment(
|
||||
channel: String,
|
||||
entry: EntryLink,
|
||||
entryIndex: Int,
|
||||
commentIndex: Int,
|
||||
event: GenericMessageEvent
|
||||
) {
|
||||
if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) {
|
||||
entry.deleteComment(commentIndex)
|
||||
event.sendMessage("Comment ${entryIndex.toLinkLabel()}.${commentIndex + 1} removed.")
|
||||
LinksManager.entries.save()
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to delete this comment for you.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun setComment(
|
||||
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)))
|
||||
LinksManager.entries.save()
|
||||
}
|
||||
|
||||
private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) {
|
||||
event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex)))
|
||||
}
|
||||
}
|
|
@ -1,207 +0,0 @@
|
|||
/*
|
||||
* LinksManager.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Pinboard
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.today
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored
|
||||
import net.thauvin.erik.mobibot.entries.Entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import net.thauvin.erik.mobibot.social.SocialManager
|
||||
import org.jsoup.Jsoup
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.io.IOException
|
||||
|
||||
class LinksManager : AbstractCommand() {
|
||||
private val defaultTags: MutableList<String> = mutableListOf()
|
||||
private val keywords: MutableList<String> = mutableListOf()
|
||||
|
||||
override val name = Constants.LINK_CMD
|
||||
override val help = emptyList<String>()
|
||||
override val isOpOnly = false
|
||||
override val isPublic = false
|
||||
override val isVisible = false
|
||||
|
||||
init {
|
||||
initProperties(TAGS_PROP, KEYWORDS_PROP)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*".toRegex()
|
||||
const val KEYWORDS_PROP = "tags-keywords"
|
||||
const val TAGS_PROP = "tags"
|
||||
val TAG_MATCH = ", *| +".toRegex()
|
||||
|
||||
/**
|
||||
* Entries array
|
||||
*/
|
||||
@JvmField
|
||||
val entries = Entries()
|
||||
|
||||
/**
|
||||
* Pinboard handler.
|
||||
*/
|
||||
@JvmField
|
||||
val pinboard = Pinboard()
|
||||
|
||||
/**
|
||||
* Social Manager handler.
|
||||
*/
|
||||
@JvmField
|
||||
val socialManager = SocialManager()
|
||||
|
||||
/**
|
||||
* Let the user know if the entries are too old to be modified.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isUpToDate(event: GenericMessageEvent): Boolean {
|
||||
if (entries.lastPubDate != today()) {
|
||||
event.sendMessage("The links are too old to be updated.")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.split(" ".toRegex(), 2)
|
||||
val sender = event.user.nick
|
||||
val botNick = event.bot().nick
|
||||
val login = event.user.login
|
||||
|
||||
if (isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(botNick))) {
|
||||
val link = cmds[0].trim()
|
||||
if (!isDupEntry(link, event)) {
|
||||
var title = ""
|
||||
val tags = ArrayList<String>(defaultTags)
|
||||
if (cmds.size == 2) {
|
||||
val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2)
|
||||
title = data[0].trim()
|
||||
if (data.size > 1) {
|
||||
tags.addAll(data[1].split(TAG_MATCH))
|
||||
}
|
||||
}
|
||||
|
||||
if (title.isBlank()) {
|
||||
title = fetchTitle(link)
|
||||
}
|
||||
|
||||
if (title != Constants.NO_TITLE) {
|
||||
matchTagKeywords(title, tags)
|
||||
}
|
||||
|
||||
// Links are old, clear them
|
||||
if (entries.lastPubDate != today()) {
|
||||
entries.links.clear()
|
||||
}
|
||||
|
||||
val entry = EntryLink(link, title, sender, login, channel, tags)
|
||||
entries.links.add(entry)
|
||||
val index = entries.links.lastIndexOf(entry)
|
||||
event.sendMessage(printLink(index, entry))
|
||||
|
||||
pinboard.addPin(event.bot().serverHostname, entry)
|
||||
|
||||
// Queue link for posting to social media.
|
||||
socialManager.queueEntry(index)
|
||||
|
||||
entries.save()
|
||||
|
||||
if (Constants.NO_TITLE == entry.title) {
|
||||
event.sendMessage("Please specify a title, by typing:")
|
||||
event.sendMessage(helpFormat("${index.toLinkLabel()}:|This is the title"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches(LINK_MATCH)
|
||||
}
|
||||
|
||||
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()
|
||||
val title = html.title()
|
||||
if (title.isNotBlank()) {
|
||||
return title
|
||||
}
|
||||
} catch (ignore: IOException) {
|
||||
// Do nothing
|
||||
}
|
||||
return Constants.NO_TITLE
|
||||
}
|
||||
|
||||
private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean {
|
||||
synchronized(entries) {
|
||||
return try {
|
||||
val match = entries.links.single { it.link == link }
|
||||
event.sendMessage(
|
||||
"Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match)
|
||||
)
|
||||
true
|
||||
} catch (ignore: NoSuchElementException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun matchTagKeywords(title: String, tags: MutableList<String>) {
|
||||
for (match in keywords) {
|
||||
val m = Regex.escape(match)
|
||||
if (title.matches("(?i).*\\b$m\\b.*".toRegex())) {
|
||||
tags.add(match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setProperty(key: String, value: String) {
|
||||
super.setProperty(key, value)
|
||||
if (KEYWORDS_PROP == key) {
|
||||
keywords.addAll(value.split(TAG_MATCH))
|
||||
} else if (TAGS_PROP == key) {
|
||||
defaultTags.addAll(value.split(TAG_MATCH))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
/*
|
||||
* Posting.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksManager.Companion.entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
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}")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.substring(1).split(":", limit = 2)
|
||||
val entryIndex = cmds[0].toInt() - 1
|
||||
|
||||
if (entryIndex < entries.links.size) {
|
||||
val cmd = cmds[1].trim()
|
||||
if (cmd.isBlank()) {
|
||||
showEntry(entryIndex, event) // L1:
|
||||
} else if (LinksManager.isUpToDate(event)) {
|
||||
if (cmd == "-") {
|
||||
removeEntry(channel, entryIndex, event) // L1:-
|
||||
} else {
|
||||
when (cmd[0]) {
|
||||
'|' -> changeTitle(cmd, entryIndex, event) // L1:|<title>
|
||||
'=' -> changeUrl(channel, cmd, entryIndex, event) // L1:=<url>
|
||||
'?' -> changeAuthor(channel, cmd, entryIndex, event) // L1:?<author>
|
||||
else -> addComment(cmd, entryIndex, event) // L1:<comment>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches("${Constants.LINK_CMD}\\d+:.*".toRegex())
|
||||
}
|
||||
|
||||
private fun addComment(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
val commentIndex = entry.addComment(cmd, event.user.nick)
|
||||
val comment = entry.getComment(commentIndex)
|
||||
event.sendMessage(EntriesUtils.printComment(entryIndex, commentIndex, comment))
|
||||
entries.save()
|
||||
}
|
||||
|
||||
private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
if (cmd.length > 1) {
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
entry.title = cmd.substring(1).trim()
|
||||
LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.printLink(entryIndex, entry))
|
||||
entries.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
if (entry.login == event.user.login || event.isChannelOp(channel)) {
|
||||
val link = cmd.substring(1)
|
||||
if (link.matches(LinksManager.LINK_MATCH)) {
|
||||
val oldLink = entry.link
|
||||
entry.link = link
|
||||
LinksManager.pinboard.updatePin(event.bot().serverHostname, oldLink, entry)
|
||||
event.sendMessage(EntriesUtils.printLink(entryIndex, entry))
|
||||
entries.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) {
|
||||
if (event.isChannelOp(channel)) {
|
||||
if (cmd.length > 1) {
|
||||
val entry: EntryLink = entries.links[index]
|
||||
entry.nick = cmd.substring(1)
|
||||
LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.printLink(index, entry))
|
||||
entries.save()
|
||||
}
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to change the author of this link for you.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[index]
|
||||
if (entry.login == event.user.login || event.isChannelOp(channel)) {
|
||||
LinksManager.pinboard.deletePin(entry)
|
||||
LinksManager.socialManager.removeEntry(index)
|
||||
entries.links.removeAt(index)
|
||||
event.sendMessage("Entry ${index.toLinkLabel()} removed.")
|
||||
entries.save()
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to remove this entry for you.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun showEntry(index: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[index]
|
||||
event.sendMessage(EntriesUtils.printLink(index, entry))
|
||||
if (entry.tags.isNotEmpty()) {
|
||||
event.sendMessage(EntriesUtils.printTags(index, entry))
|
||||
}
|
||||
if (entry.comments.isNotEmpty()) {
|
||||
val comments = entry.comments
|
||||
for (i in comments.indices) {
|
||||
event.sendMessage(EntriesUtils.printComment(index, i, comments[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Tags.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
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> [...]")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val COMMAND = "tags"
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2)
|
||||
val index = cmds[0].toInt() - 1
|
||||
|
||||
if (index < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) {
|
||||
val cmd = cmds[1].trim()
|
||||
val entry: EntryLink = LinksManager.entries.links[index]
|
||||
if (cmd.isNotEmpty()) {
|
||||
if (entry.login == event.user.login || event.isChannelOp(channel)) {
|
||||
entry.setTags(cmd)
|
||||
LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.printTags(index, entry))
|
||||
LinksManager.entries.save()
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to change the tags for you.")
|
||||
}
|
||||
} else {
|
||||
if (entry.tags.isNotEmpty()) {
|
||||
event.sendMessage(EntriesUtils.printTags(index, entry))
|
||||
} else {
|
||||
event.sendMessage("The entry has no tags. Why don't add some?")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches("^${Constants.LINK_CMD}\\d+${Constants.TAG_CMD}:.*".toRegex())
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* View.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.lastOrEmpty
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksManager.Companion.entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
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>]")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val MAX_ENTRIES = 6
|
||||
const val VIEW_CMD = "view"
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (entries.links.isNotEmpty()) {
|
||||
val p = parseArgs(args)
|
||||
showPosts(p.first, p.second, event)
|
||||
} else {
|
||||
event.sendMessage("There is currently nothing to view. Why don't you post something?")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun parseArgs(args: String): Pair<Int, String> {
|
||||
var query = args.lowercase().trim()
|
||||
var start = 0
|
||||
if (query.isEmpty() && entries.links.size > MAX_ENTRIES) {
|
||||
start = entries.links.size - MAX_ENTRIES
|
||||
}
|
||||
if (query.matches("^\\d+(| .*)".toRegex())) { // view [<start>] [<query>]
|
||||
val split = query.split(" ", limit = 2)
|
||||
try {
|
||||
start = split[0].toInt() - 1
|
||||
query = split.lastOrEmpty().trim()
|
||||
if (start > entries.links.size) {
|
||||
start = 0
|
||||
}
|
||||
} catch (ignore: NumberFormatException) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
return Pair(start, query)
|
||||
}
|
||||
|
||||
private fun showPosts(start: Int, query: String, event: GenericMessageEvent) {
|
||||
var index = start
|
||||
var entry: EntryLink
|
||||
var sent = 0
|
||||
while (index < entries.links.size && sent < MAX_ENTRIES) {
|
||||
entry = entries.links[index]
|
||||
if (query.isNotBlank()) {
|
||||
if (entry.matches(query)) {
|
||||
event.sendMessage(EntriesUtils.printLink(index, entry, true))
|
||||
sent++
|
||||
}
|
||||
} else {
|
||||
event.sendMessage(EntriesUtils.printLink(index, entry, true))
|
||||
sent++
|
||||
}
|
||||
index++
|
||||
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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (sent == 0) {
|
||||
event.sendMessage("No matches. Please try again.")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* NickComparator.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.seen
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
class NickComparator : Comparator<String>, Serializable {
|
||||
override fun compare(a: String, b: String): Int {
|
||||
return a.lowercase().compareTo(b.lowercase())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Suppress("ConstPropertyName")
|
||||
private const val serialVersionUID = 1L
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* Seen.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.seen
|
||||
|
||||
import com.google.common.collect.ImmutableSortedSet
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.loadSerialData
|
||||
import net.thauvin.erik.mobibot.Utils.saveSerialData
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime
|
||||
import org.pircbotx.User
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
|
||||
|
||||
class Seen(private val serialObject: String) : AbstractCommand() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Seen::class.java)
|
||||
private val allKeyword = "all"
|
||||
val seenNicks = TreeMap<String, SeenNick>(NickComparator())
|
||||
|
||||
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"))
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isEnabled()) {
|
||||
if (args.isNotBlank() && !args.contains(' ')) {
|
||||
val ch = event.bot().userChannelDao.getChannel(channel)
|
||||
if (args == allKeyword && ch.isOp(event.user) && seenNicks.isNotEmpty()) {
|
||||
event.sendMessage("The ${"seen".bold()} nicks are:")
|
||||
event.sendList(seenNicks.keys.toList(), 7, separator = ", ", isIndent = true)
|
||||
return
|
||||
}
|
||||
ch.users.forEach {
|
||||
if (args.equals(it.nick, true)) {
|
||||
event.sendMessage("${it.nick} is on ${channel}.")
|
||||
return
|
||||
}
|
||||
}
|
||||
if (seenNicks.containsKey(args)) {
|
||||
val seenNick = seenNicks.getValue(args)
|
||||
val lastSeen = System.currentTimeMillis() - seenNick.lastSeen
|
||||
event.sendMessage("${seenNick.nick} was last seen on $channel ${lastSeen.toUptime()} ago.")
|
||||
return
|
||||
}
|
||||
event.sendMessage("I haven't seen $args on $channel lately.")
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun add(nick: String) {
|
||||
if (isEnabled()) {
|
||||
seenNicks[nick] = SeenNick(nick, System.currentTimeMillis())
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
fun add(users: ImmutableSortedSet<User>) {
|
||||
if (isEnabled()) {
|
||||
users.forEach {
|
||||
seenNicks[it.nick] = SeenNick(it.nick, System.currentTimeMillis())
|
||||
}
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
seenNicks.clear()
|
||||
}
|
||||
|
||||
fun count(): Int = seenNicks.size
|
||||
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
return if (event.isChannelOp(channel)) {
|
||||
for (h in helpOp) {
|
||||
event.sendMessage(Utils.helpCmdSyntax(h, event.bot().nick, true))
|
||||
}
|
||||
true
|
||||
} else {
|
||||
super.helpResponse(channel, topic, event)
|
||||
}
|
||||
}
|
||||
|
||||
fun load() {
|
||||
if (isEnabled()) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
seenNicks.putAll(
|
||||
loadSerialData(
|
||||
serialObject,
|
||||
TreeMap<String, SeenNick>(),
|
||||
logger,
|
||||
"seen nicknames"
|
||||
) as TreeMap<String, SeenNick>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun save() {
|
||||
saveSerialData(serialObject, seenNicks, logger, "seen nicknames")
|
||||
}
|
||||
|
||||
init {
|
||||
load()
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* SeenNick.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.seen
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class SeenNick(val nick: String, val lastSeen: Long) : Serializable {
|
||||
companion object {
|
||||
@Suppress("ConstPropertyName")
|
||||
private const val serialVersionUID = 1L
|
||||
}
|
||||
}
|
|
@ -1,306 +0,0 @@
|
|||
/*
|
||||
* Tell.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.commands.tell
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.plural
|
||||
import net.thauvin.erik.mobibot.Utils.reverseColor
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.toIntOrDefault
|
||||
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.links.View
|
||||
import org.pircbotx.PircBotX
|
||||
import org.pircbotx.hooks.events.MessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericUserEvent
|
||||
|
||||
/**
|
||||
* The `Tell` command.
|
||||
*/
|
||||
class Tell(private val serialObject: String) : AbstractCommand() {
|
||||
// Messages queue
|
||||
private val messages: MutableList<TellMessage> = mutableListOf()
|
||||
|
||||
// Maximum number of days to keep messages
|
||||
private var maxDays = 7
|
||||
|
||||
// Message maximum queue size
|
||||
private var maxSize = 50
|
||||
|
||||
/**
|
||||
* The tell command.
|
||||
*/
|
||||
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()) + '.'
|
||||
)
|
||||
override val isOpOnly: Boolean = false
|
||||
override val isPublic: Boolean = isEnabled()
|
||||
override val isVisible: Boolean = isEnabled()
|
||||
|
||||
/**
|
||||
* Cleans the messages queue.
|
||||
*/
|
||||
private fun clean(): Boolean {
|
||||
return TellManager.clean(messages, maxDays.toLong())
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isEnabled()) {
|
||||
when {
|
||||
args.isBlank() -> {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
|
||||
args.startsWith(View.VIEW_CMD) -> {
|
||||
if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) {
|
||||
viewAll(event)
|
||||
} else {
|
||||
viewMessages(event)
|
||||
}
|
||||
}
|
||||
|
||||
args.startsWith("$TELL_DEL_KEYWORD ") -> {
|
||||
deleteMessage(channel, args, event)
|
||||
}
|
||||
|
||||
else -> {
|
||||
newMessage(channel, args, event)
|
||||
}
|
||||
}
|
||||
if (clean()) {
|
||||
save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete message.
|
||||
private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val split = args.split(" ")
|
||||
if (split.size == 2) {
|
||||
val id = split[1]
|
||||
if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) {
|
||||
if (messages.removeIf { it.sender.equals(event.user.nick, true) && it.isReceived }) {
|
||||
save()
|
||||
event.sendMessage("Delivered messages have been deleted.")
|
||||
} else {
|
||||
event.sendMessage("No delivered messages were found.")
|
||||
}
|
||||
} else {
|
||||
if (messages.removeIf {
|
||||
it.id == id &&
|
||||
(it.sender.equals(event.user.nick, true) || event.isChannelOp(channel))
|
||||
}) {
|
||||
save()
|
||||
event.sendMessage("The message was deleted from the queue.")
|
||||
} else {
|
||||
event.sendMessage("The specified message [ID $id] could not be found.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEnabled(): Boolean {
|
||||
return maxSize > 0 && maxDays > 0
|
||||
}
|
||||
|
||||
override fun setProperty(key: String, value: String) {
|
||||
super.setProperty(key, value)
|
||||
if (MAX_DAYS_PROP == key) {
|
||||
maxDays = value.toIntOrDefault(maxDays)
|
||||
} else if (MAX_SIZE_PROP == key) {
|
||||
maxSize = value.toIntOrDefault(maxSize)
|
||||
}
|
||||
}
|
||||
|
||||
// New message.
|
||||
private fun newMessage(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val split = args.split(" ".toRegex(), 2)
|
||||
if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) {
|
||||
if (messages.size < maxSize) {
|
||||
val message = TellMessage(event.user.nick, split[0], split[1].trim())
|
||||
messages.add(message)
|
||||
save()
|
||||
event.sendMessage("Message [ID ${message.id}] was queued for ${message.recipient.bold()}")
|
||||
} else {
|
||||
event.sendMessage("Sorry, the messages queue is currently full.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the messages queue.
|
||||
*/
|
||||
private fun save() {
|
||||
TellManager.save(serialObject, messages)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends messages.
|
||||
*/
|
||||
fun send(event: GenericUserEvent) {
|
||||
val nickname = event.user.nick
|
||||
if (isEnabled() && nickname != event.getBot<PircBotX>().nick) {
|
||||
messages.filter { it.isMatch(nickname) }.forEach { message ->
|
||||
if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) {
|
||||
if (message.sender == nickname) {
|
||||
if (event !is MessageEvent) {
|
||||
event.user.send().message(
|
||||
"${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}"
|
||||
)
|
||||
message.isReceived = true
|
||||
message.isNotified = true
|
||||
save()
|
||||
}
|
||||
} else {
|
||||
event.user.send().message(
|
||||
"${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
|
||||
) {
|
||||
event.user.send().message(
|
||||
"Your message ${"[ID ${message.id}]".reverseColor()} was sent to "
|
||||
+ "${message.recipient.bold()} on ${message.receptionDate}"
|
||||
)
|
||||
message.isNotified = true
|
||||
save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the messages queue size.
|
||||
*
|
||||
* @return The size.
|
||||
*/
|
||||
fun size(): Int = messages.size
|
||||
|
||||
// View all messages.
|
||||
private fun viewAll(event: GenericMessageEvent) {
|
||||
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]")
|
||||
)
|
||||
}
|
||||
} else {
|
||||
event.sendMessage("There are no messages in the queue.")
|
||||
}
|
||||
}
|
||||
|
||||
// View messages.
|
||||
private fun viewMessages(event: GenericMessageEvent) {
|
||||
var hasMessage = false
|
||||
for (message in messages.filter { it.isMatch(event.user.nick) }) {
|
||||
if (!hasMessage) {
|
||||
hasMessage = true
|
||||
event.sendMessage("Here are your messages: ")
|
||||
}
|
||||
if (message.isReceived) {
|
||||
event.sendMessage(
|
||||
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]"
|
||||
)
|
||||
}
|
||||
event.sendMessage(helpFormat(message.message))
|
||||
}
|
||||
if (!hasMessage) {
|
||||
event.sendMessage("You have no messages in the queue.")
|
||||
} 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)
|
||||
)
|
||||
)
|
||||
event.sendMessage(help.last())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Max days property.
|
||||
*/
|
||||
const val MAX_DAYS_PROP = "tell-max-days"
|
||||
|
||||
/**
|
||||
* Max size property.
|
||||
*/
|
||||
const val MAX_SIZE_PROP = "tell-max-size"
|
||||
|
||||
// Arrow
|
||||
private const val ARROW = " --> "
|
||||
|
||||
// All keyword
|
||||
private const val TELL_ALL_KEYWORD = "all"
|
||||
|
||||
//T he delete command.
|
||||
private const val TELL_DEL_KEYWORD = "del"
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
init {
|
||||
initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP)
|
||||
|
||||
// Load the message queue
|
||||
messages.addAll(TellManager.load(serialObject))
|
||||
if (clean()) {
|
||||
save()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* TellManager.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.commands.tell
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.loadSerialData
|
||||
import net.thauvin.erik.mobibot.Utils.saveSerialData
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.time.Clock
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* The Tell Messages Manager.
|
||||
*/
|
||||
object TellManager {
|
||||
private val logger: Logger = LoggerFactory.getLogger(TellManager::class.java)
|
||||
|
||||
/**
|
||||
* Cleans the messages queue.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Long): Boolean {
|
||||
if (logger.isDebugEnabled) logger.debug("Cleaning the messages.")
|
||||
val today = LocalDateTime.now(Clock.systemUTC())
|
||||
return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays).isBefore(today) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the messages.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun load(file: String): List<TellMessage> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return loadSerialData(file, emptyList<TellMessage>(), logger, "message queue") as List<TellMessage>
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the messages.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun save(file: String, messages: List<TellMessage?>?) {
|
||||
if (messages != null) {
|
||||
saveSerialData(file, messages, logger, "messages")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* TellMessage.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.commands.tell
|
||||
|
||||
import java.io.Serializable
|
||||
import java.time.Clock
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
/**
|
||||
* Tell Message.
|
||||
*/
|
||||
class TellMessage(
|
||||
/**
|
||||
* Returns the message's sender.
|
||||
*/
|
||||
val sender: String,
|
||||
|
||||
/**
|
||||
* Returns the message's recipient.
|
||||
*/
|
||||
val recipient: String,
|
||||
|
||||
/**
|
||||
* Returns the message text.
|
||||
*/
|
||||
val message: String
|
||||
) : Serializable {
|
||||
/**
|
||||
* Returns the queued date/time.
|
||||
*/
|
||||
var queued: LocalDateTime = LocalDateTime.now(Clock.systemUTC())
|
||||
|
||||
/**
|
||||
* Returns the message id.
|
||||
*/
|
||||
var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
|
||||
|
||||
/**
|
||||
* Returns `true` if a notification was sent.
|
||||
*/
|
||||
var isNotified = false
|
||||
|
||||
/**
|
||||
* Returns `true` if the message was received.
|
||||
*/
|
||||
var isReceived = false
|
||||
set(value) {
|
||||
if (value) {
|
||||
receptionDate = LocalDateTime.now(Clock.systemUTC())
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message creating date.
|
||||
*/
|
||||
var receptionDate: LocalDateTime = LocalDateTime.MIN
|
||||
|
||||
/**
|
||||
* Matches the message sender or recipient.
|
||||
*/
|
||||
fun isMatch(nick: String?): Boolean {
|
||||
return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " +
|
||||
"queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Suppress("ConstPropertyName")
|
||||
private const val serialVersionUID = 2L
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Entries.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.entries
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.today
|
||||
|
||||
class Entries(
|
||||
var channel: String = "",
|
||||
var ircServer: String = "",
|
||||
var logsDir: String = "",
|
||||
var backlogs: String = ""
|
||||
) {
|
||||
val links = mutableListOf<EntryLink>()
|
||||
|
||||
var lastPubDate = today()
|
||||
|
||||
fun load() {
|
||||
lastPubDate = FeedsManager.loadFeed(this)
|
||||
}
|
||||
|
||||
fun save() {
|
||||
lastPubDate = today()
|
||||
FeedsManager.saveFeed(this)
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* EntriesUtils.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.entries
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
|
||||
/**
|
||||
* Entries utilities.
|
||||
*/
|
||||
object EntriesUtils {
|
||||
/**
|
||||
* Prints an entry's comment for display on the channel.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String =
|
||||
("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}")
|
||||
|
||||
/**
|
||||
* Prints an entry's link for display on the channel.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String {
|
||||
val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ")
|
||||
.append('[').append(entry.nick).append(']')
|
||||
if (isView && entry.comments.isNotEmpty()) {
|
||||
buff.append("[+").append(entry.comments.size).append(']')
|
||||
}
|
||||
buff.append(' ')
|
||||
with(entry) {
|
||||
if (Constants.NO_TITLE == title) {
|
||||
buff.append(title)
|
||||
} else {
|
||||
buff.append(title.bold())
|
||||
}
|
||||
buff.append(" ( ").append(link.green()).append(" )")
|
||||
}
|
||||
return buff.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an entry's tags/categories for display on the channel. e.g. L1T: tag1, tag2
|
||||
*/
|
||||
@JvmStatic
|
||||
fun printTags(entryIndex: Int, entry: EntryLink): String =
|
||||
entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ")
|
||||
|
||||
/**
|
||||
* Builds link label based on its index. e.g: L1
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Int.toLinkLabel(): String = Constants.LINK_CMD + (this + 1)
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* EntryComment.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.entries
|
||||
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Entry comments data class.
|
||||
*/
|
||||
data class EntryComment(var comment: String, var nick: String) : Serializable {
|
||||
/**
|
||||
* Creation date.
|
||||
*/
|
||||
val date: LocalDateTime = LocalDateTime.now()
|
||||
|
||||
override fun toString(): String = "EntryComment{comment='$comment', date=$date, nick='$nick'}"
|
||||
|
||||
companion object {
|
||||
// Serial version UID
|
||||
@Suppress("ConstPropertyName")
|
||||
private const val serialVersionUID: Long = 1L
|
||||
}
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
/*
|
||||
* EntryLink.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.entries
|
||||
|
||||
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.*
|
||||
|
||||
/**
|
||||
* The class used to store link entries.
|
||||
*/
|
||||
class EntryLink(
|
||||
// Link's comments
|
||||
val comments: MutableList<EntryComment> = mutableListOf(),
|
||||
|
||||
// Tags/categories
|
||||
val tags: MutableList<SyndCategory> = mutableListOf(),
|
||||
|
||||
// Channel
|
||||
var channel: String,
|
||||
|
||||
// Creation date
|
||||
var date: Date = Calendar.getInstance().time,
|
||||
|
||||
// Link's URL
|
||||
var link: String,
|
||||
|
||||
// Author's login
|
||||
var login: String = "",
|
||||
|
||||
// Author's nickname
|
||||
var nick: 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?>
|
||||
) : this(link = link, title = title, nick = nick, login = login, channel = channel) {
|
||||
setTags(tags)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry.
|
||||
*/
|
||||
constructor(
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new comment
|
||||
*/
|
||||
fun addComment(comment: EntryComment): Int {
|
||||
comments.add(comment)
|
||||
return comments.lastIndex
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new comment.
|
||||
*/
|
||||
fun addComment(comment: String, nick: String): Int {
|
||||
return addComment(EntryComment(comment, nick))
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a specific comment.
|
||||
*/
|
||||
fun deleteComment(index: Int): Boolean {
|
||||
if (index < comments.size) {
|
||||
comments.removeAt(index)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a comment.
|
||||
*/
|
||||
fun deleteComment(entryComment: EntryComment): Boolean {
|
||||
return comments.remove(entryComment)
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the tags.
|
||||
*/
|
||||
fun formatTags(sep: String, prefix: String = ""): String {
|
||||
return tags.joinToString(separator = sep, prefix = prefix) { it.name }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a comment.
|
||||
*/
|
||||
fun getComment(index: Int): EntryComment = comments[index]
|
||||
|
||||
/**
|
||||
* Returns true if a string is contained in the link, title, or nick.
|
||||
*/
|
||||
fun matches(match: String?): Boolean {
|
||||
return if (match.isNullOrEmpty()) {
|
||||
false
|
||||
} else {
|
||||
link.contains(match, true) || title.contains(match, true) || nick.contains(match, true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comment.
|
||||
*/
|
||||
fun setComment(index: Int, comment: String?, nick: String?) {
|
||||
if (index < comments.size && !comment.isNullOrBlank() && !nick.isNullOrBlank()) {
|
||||
comments[index] = EntryComment(comment, nick)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags.
|
||||
*/
|
||||
fun setTags(tags: String) {
|
||||
setTags(tags.split(LinksManager.TAG_MATCH))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags.
|
||||
*/
|
||||
private fun setTags(tags: List<String?>) {
|
||||
if (tags.isNotEmpty()) {
|
||||
var category: SyndCategoryImpl
|
||||
for (tag in tags) {
|
||||
if (!tag.isNullOrBlank()) {
|
||||
val t = tag.lowercase()
|
||||
val mod = t[0]
|
||||
if (mod == '-') {
|
||||
// Don't remove the channel tag
|
||||
if (channel.substring(1) != t.substring(1)) {
|
||||
category = SyndCategoryImpl()
|
||||
category.name = t.substring(1)
|
||||
this.tags.remove(category)
|
||||
}
|
||||
} else {
|
||||
category = SyndCategoryImpl()
|
||||
if (mod == '+') {
|
||||
category.name = t.substring(1)
|
||||
} else {
|
||||
category.name = t
|
||||
}
|
||||
if (!this.tags.contains(category)) {
|
||||
this.tags.add(category)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object.
|
||||
*/
|
||||
override fun toString(): String {
|
||||
return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," +
|
||||
"nick='$nick', tags=$tags, title='$title'}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Serial version UID
|
||||
@Suppress("ConstPropertyName")
|
||||
private const val serialVersionUID: Long = 1L
|
||||
}
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
* FeedsManager.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.entries
|
||||
|
||||
import com.rometools.rome.feed.synd.*
|
||||
import com.rometools.rome.io.FeedException
|
||||
import com.rometools.rome.io.SyndFeedInput
|
||||
import com.rometools.rome.io.SyndFeedOutput
|
||||
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||
import net.thauvin.erik.mobibot.Utils.today
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStreamWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import kotlin.io.path.exists
|
||||
|
||||
/**
|
||||
* Manages the RSS feeds.
|
||||
*/
|
||||
class FeedsManager private constructor() {
|
||||
companion object {
|
||||
private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java)
|
||||
|
||||
// The file containing the current entries.
|
||||
private const val CURRENT_XML = "current.xml"
|
||||
|
||||
// The .xml extension.
|
||||
private const val DOT_XML = ".xml"
|
||||
|
||||
/**
|
||||
* Loads the current feed.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(IOException::class, FeedException::class)
|
||||
fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String {
|
||||
entries.links.clear()
|
||||
val xml = Paths.get("${entries.logsDir}${currentFile}")
|
||||
var pubDate = today()
|
||||
if (xml.exists()) {
|
||||
val input = SyndFeedInput()
|
||||
InputStreamReader(
|
||||
Files.newInputStream(xml), StandardCharsets.UTF_8
|
||||
).use { reader ->
|
||||
val feed = input.build(reader)
|
||||
pubDate = feed.publishedDate.toIsoLocalDate()
|
||||
val items = feed.entries
|
||||
var entry: EntryLink
|
||||
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
|
||||
)
|
||||
var split: List<String>
|
||||
for (comment in description.value.split("<br/>")) {
|
||||
split = comment.split(": ".toRegex(), 2)
|
||||
if (split.size == 2) {
|
||||
entry.addComment(comment = split[1].trim(), nick = split[0].trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
entries.links.add(entry)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Create an empty feed.
|
||||
saveFeed(entries)
|
||||
}
|
||||
return pubDate
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the feeds.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) {
|
||||
if (logger.isDebugEnabled) logger.debug("Saving the feeds...")
|
||||
if (entries.logsDir.isNotBlank()) {
|
||||
try {
|
||||
val output = SyndFeedOutput()
|
||||
val rss: SyndFeed = SyndFeedImpl()
|
||||
val items: MutableList<SyndEntry> = mutableListOf()
|
||||
var item: SyndEntry
|
||||
OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8
|
||||
).use { fw ->
|
||||
with(rss) {
|
||||
feedType = "rss_2.0"
|
||||
title = "${entries.channel} IRC Links"
|
||||
description = "Links from ${entries.ircServer} on ${entries.channel}"
|
||||
if (entries.backlogs.isNotBlank()) link = entries.backlogs
|
||||
publishedDate = Calendar.getInstance().time
|
||||
language = "en"
|
||||
}
|
||||
val buff: StringBuilder = StringBuilder()
|
||||
for (i in entries.links.indices.reversed()) {
|
||||
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>")
|
||||
if (comments.isNotEmpty()) {
|
||||
buff.append(" <br/><br/>")
|
||||
for (j in comments.indices) {
|
||||
if (j > 0) {
|
||||
buff.append(" <br/>")
|
||||
}
|
||||
buff.append(comments[j].nick).append(": ").append(comments[j].comment)
|
||||
}
|
||||
}
|
||||
item = SyndEntryImpl()
|
||||
item.link = link
|
||||
item.description = SyndContentImpl().apply { value = buff.toString() }
|
||||
item.title = title
|
||||
item.publishedDate = date
|
||||
item.author = "${channel.removePrefix("#")}@${entries.ircServer} ($nick)"
|
||||
item.categories = tags
|
||||
items.add(item)
|
||||
}
|
||||
}
|
||||
rss.entries = items
|
||||
if (logger.isDebugEnabled) logger.debug("Writing the entries feed.")
|
||||
output.output(rss, fw)
|
||||
}
|
||||
OutputStreamWriter(
|
||||
Files.newOutputStream(
|
||||
Paths.get(
|
||||
entries.logsDir + today() + DOT_XML
|
||||
)
|
||||
), StandardCharsets.UTF_8
|
||||
).use { fw -> output.output(rss, fw) }
|
||||
} catch (e: FeedException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e)
|
||||
} catch (e: IOException) {
|
||||
if (logger.isWarnEnabled)
|
||||
logger.warn("An IO error occurred while generating the entries feed.", e)
|
||||
}
|
||||
} else {
|
||||
if (logger.isWarnEnabled) {
|
||||
logger.warn("Unable to generate the entries feed. A required property is missing.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* AbstractModule.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
/**
|
||||
* The `Module` abstract class.
|
||||
*/
|
||||
abstract class AbstractModule {
|
||||
/**
|
||||
* The module name.
|
||||
*/
|
||||
abstract val name: String
|
||||
|
||||
/**
|
||||
* The module's commands, if any.
|
||||
*/
|
||||
@JvmField
|
||||
val commands: MutableList<String> = mutableListOf()
|
||||
|
||||
@JvmField
|
||||
val help: MutableList<String> = mutableListOf()
|
||||
val properties: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
/**
|
||||
* Responds to a command.
|
||||
*/
|
||||
abstract fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)
|
||||
|
||||
/**
|
||||
* Returns the module's property keys.
|
||||
*/
|
||||
val propertyKeys: Set<String>
|
||||
get() = properties.keys
|
||||
|
||||
/**
|
||||
* Returns `true` if the module has properties.
|
||||
*/
|
||||
fun hasProperties(): Boolean {
|
||||
return properties.isNotEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the module's help.
|
||||
*/
|
||||
open fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||
for (h in help) {
|
||||
event.sendMessage(helpCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the properties.
|
||||
*/
|
||||
fun initProperties(vararg keys: String) {
|
||||
for (key in keys) {
|
||||
properties[key] = ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the module is enabled.
|
||||
*/
|
||||
val isEnabled: Boolean
|
||||
get() = if (hasProperties()) {
|
||||
isValidProperties
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the module responds to private messages.
|
||||
*/
|
||||
open val isPrivateMsgEnabled: Boolean = false
|
||||
|
||||
/**
|
||||
* Ensures that all properties have values.
|
||||
*/
|
||||
open val isValidProperties: Boolean
|
||||
get() {
|
||||
for (s in properties.keys) {
|
||||
if (properties[s].isNullOrBlank()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a property key and value.
|
||||
*/
|
||||
fun setProperty(key: String, value: String) {
|
||||
if (key.isNotBlank()) {
|
||||
properties[key] = value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Calc.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.objecthunter.exp4j.ExpressionBuilder
|
||||
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.text.DecimalFormat
|
||||
|
||||
/**
|
||||
* The Calc module.
|
||||
*/
|
||||
class Calc : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Calc::class.java)
|
||||
|
||||
override val name = "Calc"
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
event.respond(calculate(args))
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
||||
event.respond("No idea. This is the kind of math I don't get.")
|
||||
} catch (e: UnknownFunctionOrVariableException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e)
|
||||
event.respond("No idea. I must've some form of Dyscalculia.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Calc command
|
||||
private const val CALC_CMD = "calc"
|
||||
|
||||
/**
|
||||
* Performs a calculation. e.g.: 1 + 1 * 2
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun calculate(query: String): String {
|
||||
val decimalFormat = DecimalFormat("#.##")
|
||||
val calc = ExpressionBuilder(query).build()
|
||||
return query.replace(" ", "") + " = " + decimalFormat.format(calc.evaluate()).bold()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(CALC_CMD)
|
||||
help.add("To solve a mathematical calculation:")
|
||||
help.add(helpFormat("%c $CALC_CMD <calculation>"))
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* ChatGpt.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import org.apache.commons.text.WordUtils
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.json.JSONWriter
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import java.net.http.HttpClient
|
||||
import java.net.http.HttpRequest
|
||||
import java.net.http.HttpResponse
|
||||
|
||||
class ChatGpt : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java)
|
||||
|
||||
override val name = CHATGPT_NAME
|
||||
|
||||
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()
|
||||
)
|
||||
if (answer.isNotBlank()) {
|
||||
event.sendMessage(WordUtils.wrap(answer, 400))
|
||||
} else {
|
||||
event.respond("$name is stumped.")
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
} catch (e: NumberFormatException) {
|
||||
if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e)
|
||||
event.respond("The $name module is misconfigured.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The service name.
|
||||
*/
|
||||
const val CHATGPT_NAME = "ChatGPT"
|
||||
|
||||
/**
|
||||
* The API Key property.
|
||||
*/
|
||||
const val API_KEY_PROP = "chatgpt-api-key"
|
||||
|
||||
/**
|
||||
* The max tokens property.
|
||||
*/
|
||||
const val MAX_TOKENS_PROP = "chatgpt-max-tokens"
|
||||
|
||||
// ChatGPT API URL
|
||||
private const val API_URL = "https://api.openai.com/v1/completions"
|
||||
|
||||
// ChatGPT command
|
||||
private const val CHATGPT_CMD = "chatgpt"
|
||||
|
||||
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun chat(query: String, apiKey: String?, maxTokens: Int): String {
|
||||
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(
|
||||
"""{
|
||||
"model": "text-davinci-003",
|
||||
"prompt": $prompt,
|
||||
"temperature": 0,
|
||||
"max_tokens": $maxTokens,
|
||||
"top_p": 1,
|
||||
"frequency_penalty": 0,
|
||||
"presence_penalty": 0
|
||||
}""".trimIndent()
|
||||
)
|
||||
)
|
||||
.build()
|
||||
try {
|
||||
val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
|
||||
if (response.statusCode() == 200) {
|
||||
try {
|
||||
val jsonResponse = JSONObject(response.body())
|
||||
val choices = jsonResponse.getJSONArray("choices")
|
||||
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
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (response.statusCode() == 429) {
|
||||
throw ModuleException(
|
||||
"$CHATGPT_CMD($query): Rate limit reached",
|
||||
"Rate limit reached. Please try again later."
|
||||
)
|
||||
} else {
|
||||
throw IOException("HTTP Status Code: " + response.statusCode())
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException(
|
||||
"$CHATGPT_CMD($query): IO",
|
||||
"An IO error has occurred while conversing with $CHATGPT_NAME.",
|
||||
e
|
||||
)
|
||||
}
|
||||
} else {
|
||||
throw ModuleException("$CHATGPT_CMD($query)", "No $CHATGPT_NAME API key specified.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(CHATGPT_CMD)
|
||||
with(help) {
|
||||
add("To get answers from $name:")
|
||||
add(Utils.helpFormat("%c $CHATGPT_CMD <query>"))
|
||||
add("For example:")
|
||||
add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms"))
|
||||
add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?"))
|
||||
}
|
||||
initProperties(API_KEY_PROP, MAX_TOKENS_PROP)
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
* CryptoPrices.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.crypto.CryptoException
|
||||
import net.thauvin.erik.crypto.CryptoPrice
|
||||
import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import org.json.JSONObject
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* The Cryptocurrency Prices module.
|
||||
*/
|
||||
class CryptoPrices : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java)
|
||||
|
||||
override val name = "CryptoPrices"
|
||||
|
||||
/**
|
||||
* Returns the cryptocurrency market price from
|
||||
* [Coinbase](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices#get-spot-price).
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (CURRENCIES.isEmpty()) {
|
||||
try {
|
||||
loadCurrencies()
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
}
|
||||
}
|
||||
|
||||
val debugMessage = "crypto($cmd $args)"
|
||||
if (args == CODES_KEYWORD) {
|
||||
event.sendMessage("The supported currencies are:")
|
||||
event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true)
|
||||
} else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) {
|
||||
try {
|
||||
val price = currentPrice(args.split(' '))
|
||||
val amount = try {
|
||||
price.toCurrency()
|
||||
} catch (ignore: IllegalArgumentException) {
|
||||
price.amount
|
||||
}
|
||||
event.respond("${price.base} current price is $amount [${CURRENCIES[price.currency]}]")
|
||||
} catch (e: CryptoException) {
|
||||
if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
if (logger.isErrorEnabled) logger.error(debugMessage, e)
|
||||
event.respond("An IO error has occurred while retrieving the cryptocurrency market price.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Crypto command
|
||||
private const val CRYPTO_CMD = "crypto"
|
||||
|
||||
// Fiat Currencies
|
||||
private val CURRENCIES: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
// Currency codes keyword
|
||||
private const val CODES_KEYWORD = "codes"
|
||||
|
||||
/**
|
||||
* Get current market price.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun currentPrice(args: List<String>): CryptoPrice {
|
||||
return if (args.size == 2)
|
||||
spotPrice(args[0], args[1])
|
||||
else
|
||||
spotPrice(args[0])
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing purposes.
|
||||
*/
|
||||
fun getCurrencyName(code: String): String? {
|
||||
return CURRENCIES[code]
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Fiat currencies..
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun loadCurrencies() {
|
||||
try {
|
||||
val json = JSONObject(CryptoPrice.apiCall(listOf("currencies")))
|
||||
val data = json.getJSONArray("data")
|
||||
for (i in 0 until data.length()) {
|
||||
val d = data.getJSONObject(i)
|
||||
CURRENCIES[d.getString("id")] = d.getString("name")
|
||||
}
|
||||
} catch (e: CryptoException) {
|
||||
throw ModuleException(
|
||||
"loadCurrencies(): CE",
|
||||
"An error has occurred while retrieving the currencies table.",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(CRYPTO_CMD)
|
||||
with(help) {
|
||||
add("To retrieve a cryptocurrency's market price:")
|
||||
add(helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]"))
|
||||
add("For example:")
|
||||
add(helpFormat("%c $CRYPTO_CMD BTC"))
|
||||
add(helpFormat("%c $CRYPTO_CMD ETH EUR"))
|
||||
add(helpFormat("%c $CRYPTO_CMD ETH2 GPB"))
|
||||
add("To list the supported currencies:")
|
||||
add(helpFormat("%c $CRYPTO_CMD $CODES_KEYWORD"))
|
||||
}
|
||||
loadCurrencies()
|
||||
}
|
||||
}
|
|
@ -1,222 +0,0 @@
|
|||
/*
|
||||
* CurrencyConverter.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.reader
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.json.JSONObject
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* The CurrencyConverter module.
|
||||
*/
|
||||
class CurrencyConverter : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java)
|
||||
|
||||
override val name = "CurrencyConverter"
|
||||
|
||||
// Reload currency codes
|
||||
private fun reload(apiKey: String?) {
|
||||
if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) {
|
||||
try {
|
||||
loadSymbols(apiKey)
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified currencies.
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
reload(properties[API_KEY_PROP])
|
||||
|
||||
when {
|
||||
SYMBOLS.isEmpty() -> {
|
||||
event.respond(EMPTY_SYMBOLS_TABLE)
|
||||
}
|
||||
|
||||
args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> {
|
||||
val msg = convertCurrency(properties[API_KEY_PROP], args)
|
||||
event.respond(msg.msg)
|
||||
if (msg.isError) {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
args.contains(CODES_KEYWORD) -> {
|
||||
event.sendMessage("The supported currency codes are:")
|
||||
event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true)
|
||||
}
|
||||
|
||||
else -> {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||
reload(properties[API_KEY_PROP])
|
||||
|
||||
if (SYMBOLS.isEmpty()) {
|
||||
event.sendMessage(EMPTY_SYMBOLS_TABLE)
|
||||
} else {
|
||||
val nick = event.bot().nick
|
||||
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 USD", nick, isPrivateMsgEnabled)
|
||||
)
|
||||
)
|
||||
event.sendMessage("To list the supported currency codes:")
|
||||
event.sendMessage(
|
||||
helpFormat(
|
||||
helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled)
|
||||
)
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The API Key property.
|
||||
*/
|
||||
const val API_KEY_PROP = "exchangerate-api-key"
|
||||
|
||||
// Currency command
|
||||
private const val CURRENCY_CMD = "currency"
|
||||
|
||||
// Currency codes keyword
|
||||
private const val CODES_KEYWORD = "codes"
|
||||
|
||||
// Empty symbols table.
|
||||
private const val EMPTY_SYMBOLS_TABLE = "Sorry, but the currency table is empty."
|
||||
|
||||
// Currency symbols
|
||||
private val SYMBOLS: TreeMap<String, String> = TreeMap()
|
||||
|
||||
// Decimal format
|
||||
private val DECIMAL_FORMAT = DecimalFormat("0.00#")
|
||||
|
||||
/**
|
||||
* Converts from a currency to another.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun convertCurrency(apiKey: String?, query: String): Message {
|
||||
if (apiKey.isNullOrEmpty()) {
|
||||
throw ModuleException("${CURRENCY_CMD}($query)", "No Exchange Rate API key specified.")
|
||||
}
|
||||
|
||||
val cmds = query.split(" ")
|
||||
return if (cmds.size == 4) {
|
||||
if (cmds[3] == cmds[1] || "0" == cmds[0]) {
|
||||
PublicMessage("You're kidding, right?")
|
||||
} else {
|
||||
val to = cmds[1].uppercase()
|
||||
val from = cmds[3].uppercase()
|
||||
if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) {
|
||||
try {
|
||||
val amt = cmds[0].replace(",", "")
|
||||
val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/pair/$to/$from/$amt")
|
||||
val body = url.reader().body
|
||||
val json = JSONObject(body)
|
||||
|
||||
if (json.getString("result") == "success") {
|
||||
val result = DECIMAL_FORMAT.format(json.getDouble("conversion_result"))
|
||||
PublicMessage(
|
||||
"${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}"
|
||||
)
|
||||
} else {
|
||||
ErrorMessage("Sorry, an error occurred while converting the currencies.")
|
||||
}
|
||||
} catch (ignore: IOException) {
|
||||
ErrorMessage("Sorry, an IO error occurred while converting the currencies.")
|
||||
}
|
||||
} else {
|
||||
ErrorMessage("Sounds like monopoly money to me!")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ErrorMessage("Invalid query. Let's try again.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the currency ISO symbols.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun loadSymbols(apiKey: String?) {
|
||||
if (!apiKey.isNullOrEmpty()) {
|
||||
try {
|
||||
val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/codes")
|
||||
val json = JSONObject(url.reader().body)
|
||||
if (json.getString("result") == "success") {
|
||||
val codes = json.getJSONArray("supported_codes")
|
||||
for (i in 0 until codes.length()) {
|
||||
val code = codes.getJSONArray(i)
|
||||
SYMBOLS[code.getString(0)] = code.getString(1)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException(
|
||||
"loadCodes(): IOE",
|
||||
"An IO error has occurred while retrieving the currencies.",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(CURRENCY_CMD)
|
||||
initProperties(API_KEY_PROP)
|
||||
loadSymbols(properties[ChatGpt.API_KEY_PROP])
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Dice.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
/**
|
||||
* The Dice module.
|
||||
*/
|
||||
class Dice : AbstractModule() {
|
||||
override val name = "Dice"
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
val arg = if (args.isBlank()) "2d6" else args.trim()
|
||||
val match = Regex("^([1-9]|[12]\\d|3[0-2])[dD]([1-9]|[12]\\d|3[0-2])$").find(arg)
|
||||
if (match != null) {
|
||||
val (dice, sides) = match.destructured
|
||||
event.respond("you rolled " + roll(dice.toInt(), sides.toInt()))
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Dice command
|
||||
private const val DICE_CMD = "dice"
|
||||
|
||||
@JvmStatic
|
||||
fun roll(dice: Int, sides: Int): String {
|
||||
val result = StringBuilder()
|
||||
var total = 0
|
||||
|
||||
repeat(dice) {
|
||||
val roll = (1..sides).random()
|
||||
total += roll
|
||||
|
||||
if (result.isNotEmpty()) {
|
||||
result.append(" + ")
|
||||
}
|
||||
|
||||
result.append(roll.bold())
|
||||
}
|
||||
|
||||
if (dice != 1) {
|
||||
result.append(" = ${total.bold()}")
|
||||
}
|
||||
|
||||
return result.toString()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(DICE_CMD)
|
||||
help.add("To roll 2 dice with 6 sides:")
|
||||
help.add(helpFormat("%c $DICE_CMD [2d6]"))
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* GoogleSearch.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.colorize
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.reader
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.pircbotx.Colors
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* The GoogleSearch module.
|
||||
*/
|
||||
class GoogleSearch : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java)
|
||||
|
||||
override val name = "GoogleSearch"
|
||||
|
||||
/**
|
||||
* Searches Google.
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val results = searchGoogle(
|
||||
args,
|
||||
properties[API_KEY_PROP],
|
||||
properties[CSE_KEY_PROP],
|
||||
event.user.nick
|
||||
)
|
||||
for (msg in results) {
|
||||
if (msg.isError) {
|
||||
event.respond(msg.msg.colorize(msg.color))
|
||||
} else {
|
||||
event.sendMessage(channel, msg)
|
||||
}
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Google API Key property
|
||||
const val API_KEY_PROP = "google-api-key"
|
||||
|
||||
// Google Custom Search Engine ID property
|
||||
const val CSE_KEY_PROP = "google-cse-cx"
|
||||
|
||||
// Google command
|
||||
private const val GOOGLE_CMD = "google"
|
||||
|
||||
/**
|
||||
* Performs a search on Google.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun searchGoogle(
|
||||
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."
|
||||
)
|
||||
}
|
||||
val results = mutableListOf<Message>()
|
||||
if (query.isNotBlank()) {
|
||||
try {
|
||||
val url = URL(
|
||||
"https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" +
|
||||
""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json"
|
||||
)
|
||||
val json = JSONObject(url.reader().body)
|
||||
if (json.has("items")) {
|
||||
val ja = json.getJSONArray("items")
|
||||
for (i in 0 until ja.length()) {
|
||||
val j = ja.getJSONObject(i)
|
||||
results.add(NoticeMessage(j.getString("title").unescapeXml()))
|
||||
results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN))
|
||||
}
|
||||
} else if (json.has("error")) {
|
||||
val error = json.getJSONObject("error")
|
||||
val message = error.getString("message")
|
||||
throw ModuleException("searchGoogle($query): ${error.getInt("code")} : $message", message)
|
||||
} else {
|
||||
results.add(ErrorMessage("No results found.", Colors.RED))
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
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
|
||||
)
|
||||
}
|
||||
} else {
|
||||
results.add(ErrorMessage("Invalid query. Please try again."))
|
||||
}
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(GOOGLE_CMD)
|
||||
help.add("To search Google:")
|
||||
help.add(helpFormat("%c $GOOGLE_CMD <query>"))
|
||||
initProperties(API_KEY_PROP, CSE_KEY_PROP)
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Joke.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.jokeapi.exceptions.HttpErrorException
|
||||
import net.thauvin.erik.jokeapi.exceptions.JokeException
|
||||
import net.thauvin.erik.jokeapi.joke
|
||||
import net.thauvin.erik.jokeapi.models.Type
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.colorize
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.json.JSONException
|
||||
import org.pircbotx.Colors
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* The Joke module.
|
||||
*/
|
||||
class Joke : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Joke::class.java)
|
||||
|
||||
override val name = "Joke"
|
||||
|
||||
/**
|
||||
* Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/).
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
try {
|
||||
randomJoke().forEach {
|
||||
sendIRC().notice(channel, it.msg.colorize(it.color))
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Joke command
|
||||
private const val JOKE_CMD = "joke"
|
||||
|
||||
/**
|
||||
* Retrieves a random joke.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun randomJoke(): List<Message> {
|
||||
return try {
|
||||
val joke = joke(safe = true, type = Type.SINGLE, splitNewLine = true)
|
||||
joke.joke.map { PublicMessage(it, Colors.CYAN) }
|
||||
} catch (e: JokeException) {
|
||||
throw ModuleException("randomJoke(): ${e.additionalInfo}", e.message, e)
|
||||
} catch (e: HttpErrorException) {
|
||||
throw ModuleException("randomJoke(): HTTP: ${e.statusCode}", e.message, e)
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException("randomJoke(): IOE", "An IO error has occurred retrieving a random joke.", e)
|
||||
} catch (e: JSONException) {
|
||||
throw ModuleException("randomJoke(): JSON", "A parsing error has occurred retrieving a random joke.", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(JOKE_CMD)
|
||||
help.add("To display a random joke:")
|
||||
help.add(helpFormat("%c $JOKE_CMD"))
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
/*
|
||||
* Lookup.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import org.apache.commons.net.whois.WhoisClient
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
|
||||
/**
|
||||
* The Lookup module.
|
||||
*/
|
||||
class Lookup : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java)
|
||||
|
||||
override val name = "Lookup"
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.matches("(\\S.)+(\\S)+".toRegex())) {
|
||||
try {
|
||||
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()
|
||||
)
|
||||
) {
|
||||
try {
|
||||
val lines = whois(args)
|
||||
if (lines.isNotEmpty()) {
|
||||
var line: String
|
||||
var hasData = false
|
||||
for (rawLine in lines) {
|
||||
line = rawLine.trim()
|
||||
if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) {
|
||||
if (!hasData) {
|
||||
event.respondWith(line)
|
||||
hasData = true
|
||||
} else {
|
||||
event.bot().sendIRC().notice(event.user.nick, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
event.respond("Unknown host.")
|
||||
}
|
||||
} catch (ioe: IOException) {
|
||||
if (logger.isWarnEnabled) {
|
||||
logger.warn("Unable to perform whois IP lookup: $args", ioe)
|
||||
}
|
||||
event.respond("Unable to perform whois IP lookup: ${ioe.message}")
|
||||
}
|
||||
} else {
|
||||
event.respond("Unknown host.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The whois default host.
|
||||
*/
|
||||
const val WHOIS_HOST = "whois.arin.net"
|
||||
|
||||
// Lookup command
|
||||
private const val LOOKUP_CMD = "lookup"
|
||||
|
||||
/**
|
||||
* Performs a DNS lookup on the specified query.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(UnknownHostException::class)
|
||||
fun nslookup(query: String): String {
|
||||
val buffer = StringBuilder()
|
||||
val results = InetAddress.getAllByName(query)
|
||||
var hostInfo: String
|
||||
for (result in results) {
|
||||
if (result.hostAddress == query) {
|
||||
hostInfo = result.hostName
|
||||
if (hostInfo == query) {
|
||||
throw UnknownHostException()
|
||||
}
|
||||
} else {
|
||||
hostInfo = result.hostAddress
|
||||
}
|
||||
if (buffer.isNotEmpty()) {
|
||||
buffer.append(", ")
|
||||
}
|
||||
buffer.append(hostInfo)
|
||||
}
|
||||
return buffer.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a whois IP query.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
private fun whois(query: String): List<String> {
|
||||
return whois(query, WHOIS_HOST)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a whois IP query.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
fun whois(query: String, host: String): List<String> {
|
||||
val whoisClient = WhoisClient()
|
||||
val lines: List<String>
|
||||
with(whoisClient) {
|
||||
try {
|
||||
defaultTimeout = Constants.CONNECT_TIMEOUT
|
||||
connect(host)
|
||||
soTimeout = Constants.CONNECT_TIMEOUT
|
||||
setSoLinger(false, 0)
|
||||
lines = if (WHOIS_HOST == host) {
|
||||
query("n - $query").split("\n")
|
||||
} else {
|
||||
query(query).split("\n")
|
||||
}
|
||||
} finally {
|
||||
disconnect()
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(LOOKUP_CMD)
|
||||
help.add("To perform a DNS lookup query:")
|
||||
help.add(helpFormat("%c $LOOKUP_CMD <ip address or hostname>"))
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Mastodon.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.Utils.prefixIfMissing
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import net.thauvin.erik.mobibot.social.SocialModule
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.json.JSONWriter
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import java.net.http.HttpClient
|
||||
import java.net.http.HttpRequest
|
||||
import java.net.http.HttpResponse
|
||||
|
||||
class Mastodon : SocialModule() {
|
||||
override val name = "Mastodon"
|
||||
|
||||
override val handle: String?
|
||||
get() = properties[HANDLE_PROP]
|
||||
|
||||
override val isAutoPost: Boolean
|
||||
get() = isEnabled && properties[AUTO_POST_PROP].toBoolean()
|
||||
|
||||
override val isValidProperties: Boolean
|
||||
get() = !(properties[INSTANCE_PROP].isNullOrBlank() || properties[ACCESS_TOKEN_PROP].isNullOrBlank())
|
||||
|
||||
/**
|
||||
* Formats the entry for posting.
|
||||
*/
|
||||
override fun formatEntry(entry: EntryLink): String {
|
||||
return "${entry.title} (via ${entry.nick} on ${entry.channel})${formatTags(entry)}\n\n${entry.link}"
|
||||
}
|
||||
|
||||
private fun formatTags(entry: EntryLink): String {
|
||||
return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) }
|
||||
.joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts on Mastodon.
|
||||
*/
|
||||
@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
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Property keys
|
||||
const val ACCESS_TOKEN_PROP = "mastodon-access-token"
|
||||
const val AUTO_POST_PROP = "mastodon-auto-post"
|
||||
const val HANDLE_PROP = "mastodon-handle"
|
||||
const val INSTANCE_PROP = "mastodon-instance"
|
||||
|
||||
private const val MASTODON_CMD = "mastodon"
|
||||
private const val TOOT_CMD = "toot"
|
||||
|
||||
/**
|
||||
* Post on Mastodon.
|
||||
*/
|
||||
@JvmStatic
|
||||
@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)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
try {
|
||||
val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
|
||||
if (response.statusCode() == 200) {
|
||||
return try {
|
||||
val jsonResponse = JSONObject(response.body())
|
||||
if (isDm) {
|
||||
jsonResponse.getString("content")
|
||||
} else {
|
||||
"Your message was posted to ${jsonResponse.getString("url")}"
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
throw ModuleException("mastodonPost($message)", "A JSON error has occurred: ${e.message}", e)
|
||||
}
|
||||
} else {
|
||||
throw IOException("Status Code: " + response.statusCode())
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException("mastodonPost($message)", "An IO error has occurred: ${e.message}", e)
|
||||
} catch (e: InterruptedException) {
|
||||
throw ModuleException("mastodonPost($message)", "An error has occurred: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(MASTODON_CMD)
|
||||
commands.add(TOOT_CMD)
|
||||
help.add("To toot on Mastodon:")
|
||||
help.add(Utils.helpFormat("%c $TOOT_CMD <message>"))
|
||||
properties[AUTO_POST_PROP] = "false"
|
||||
initProperties(ACCESS_TOKEN_PROP, HANDLE_PROP, INSTANCE_PROP)
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* ModuleException.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
/**
|
||||
* The `ModuleException` class.
|
||||
*/
|
||||
class ModuleException @JvmOverloads constructor(
|
||||
val debugMessage: String,
|
||||
message: String? = null,
|
||||
cause: Throwable? = null
|
||||
) : Exception(message, cause) {
|
||||
companion object {
|
||||
@Suppress("ConstPropertyName")
|
||||
private const val serialVersionUID = 1L
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Ping.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
/**
|
||||
* The Ping module.
|
||||
*/
|
||||
class Ping : AbstractModule() {
|
||||
override val name = "Ping"
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
event.bot().sendIRC().action(channel, randomPing())
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The ping responses.
|
||||
*/
|
||||
@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!"
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun randomPing(): String {
|
||||
return PINGS[PINGS.indices.random()]
|
||||
}
|
||||
|
||||
/**
|
||||
* The ping command.
|
||||
*/
|
||||
private const val PING_CMD = "ping"
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(PING_CMD)
|
||||
help.add("To ping the bot:")
|
||||
help.add(helpFormat("%c $PING_CMD"))
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* RockPaperScissors.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
|
||||
/**
|
||||
* Simple module example in Kotlin.
|
||||
*/
|
||||
class RockPaperScissors : AbstractModule() {
|
||||
override val name = "RockPaperScissors"
|
||||
|
||||
init {
|
||||
with(commands) {
|
||||
add(Hands.ROCK.name.lowercase())
|
||||
add(Hands.PAPER.name.lowercase())
|
||||
add(Hands.SCISSORS.name.lowercase())
|
||||
}
|
||||
|
||||
with(help) {
|
||||
add("To play Rock Paper Scissors:")
|
||||
add(
|
||||
helpFormat(
|
||||
"%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}"
|
||||
+ " | ${Hands.SCISSORS.name.lowercase()}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class Hands(val action: String) {
|
||||
ROCK("crushes") {
|
||||
override fun beats(hand: Hands): Boolean {
|
||||
return hand == SCISSORS
|
||||
}
|
||||
},
|
||||
PAPER("covers") {
|
||||
override fun beats(hand: Hands): Boolean {
|
||||
return hand == ROCK
|
||||
}
|
||||
},
|
||||
SCISSORS("cuts") {
|
||||
override fun beats(hand: Hands): Boolean {
|
||||
return hand == PAPER
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun beats(hand: Hands): Boolean
|
||||
}
|
||||
|
||||
companion object {
|
||||
// For testing.
|
||||
fun winLoseOrDraw(player: String, bot: String): String {
|
||||
val hand = Hands.valueOf(player.uppercase())
|
||||
val botHand = Hands.valueOf(bot.uppercase())
|
||||
|
||||
return when {
|
||||
hand == botHand -> "draw"
|
||||
hand.beats(botHand) -> "win"
|
||||
else -> "lose"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
val hand = Hands.valueOf(cmd.uppercase())
|
||||
val botHand = Hands.entries[(0..Hands.entries.size).random()]
|
||||
when {
|
||||
hand == botHand -> {
|
||||
event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.")
|
||||
}
|
||||
|
||||
hand.beats(botHand) -> {
|
||||
event.respond("${hand.name.bold()} ${hand.action} ${botHand.name} » You ${"win".bold()}!")
|
||||
}
|
||||
|
||||
else -> {
|
||||
event.respond("${botHand.name.bold()} ${botHand.action} ${hand.name} » You ${"lose".bold()}!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
/*
|
||||
* StockQuote.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.reader
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* The StockQuote module.
|
||||
*/
|
||||
class StockQuote : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java)
|
||||
|
||||
override val name = "StockQuote"
|
||||
|
||||
/**
|
||||
* Returns the specified stock quote from Alpha Vantage.
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val messages = getQuote(args, properties[API_KEY_PROP])
|
||||
for (msg in messages) {
|
||||
event.sendMessage(channel, msg)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The API property key.
|
||||
*/
|
||||
const val API_KEY_PROP = "alphavantage-api-key"
|
||||
|
||||
/**
|
||||
* The Invalid Symbol error string.
|
||||
*/
|
||||
const val INVALID_SYMBOL = "Invalid symbol."
|
||||
|
||||
// API URL
|
||||
private const val API_URL = "https://www.alphavantage.co/query?function="
|
||||
|
||||
// Quote command
|
||||
private const val STOCK_CMD = "stock"
|
||||
|
||||
@Throws(ModuleException::class)
|
||||
private fun getJsonResponse(response: String, debugMessage: String): JSONObject {
|
||||
return if (response.isNotBlank()) {
|
||||
val json = JSONObject(response)
|
||||
try {
|
||||
val info = json.getString("Information")
|
||||
if (info.isNotEmpty()) {
|
||||
throw ModuleException(debugMessage, info.unescapeXml())
|
||||
}
|
||||
} catch (ignore: JSONException) {
|
||||
// Do nothing
|
||||
}
|
||||
try {
|
||||
var error = json.getString("Note")
|
||||
if (error.isNotEmpty()) {
|
||||
throw ModuleException(debugMessage, error.unescapeXml())
|
||||
}
|
||||
error = json.getString("Error Message")
|
||||
if (error.isNotEmpty()) {
|
||||
throw ModuleException(debugMessage, error.unescapeXml())
|
||||
}
|
||||
} catch (ignore: JSONException) {
|
||||
// Do nothing
|
||||
}
|
||||
json
|
||||
} else {
|
||||
throw ModuleException(debugMessage, "Empty Response.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a stock quote.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
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."
|
||||
)
|
||||
}
|
||||
val messages = mutableListOf<Message>()
|
||||
if (symbol.isNotBlank()) {
|
||||
val debugMessage = "getQuote($symbol)"
|
||||
var response: String
|
||||
try {
|
||||
with(messages) {
|
||||
// Search for symbol/keywords
|
||||
response = URL(
|
||||
"${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey="
|
||||
+ apiKey.encodeUrl()
|
||||
).reader().body
|
||||
var json = getJsonResponse(response, debugMessage)
|
||||
val symbols = json.getJSONArray("bestMatches")
|
||||
if (symbols.isEmpty) {
|
||||
messages.add(ErrorMessage(INVALID_SYMBOL))
|
||||
} else {
|
||||
val symbolInfo = symbols.getJSONObject(0)
|
||||
|
||||
// Get quote for symbol
|
||||
response = URL(
|
||||
"${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")
|
||||
if (quote.isEmpty) {
|
||||
add(ErrorMessage(INVALID_SYMBOL))
|
||||
} else {
|
||||
|
||||
add(
|
||||
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()
|
||||
)
|
||||
)
|
||||
add(
|
||||
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"
|
||||
)
|
||||
|
||||
data.forEach {
|
||||
add(
|
||||
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() + ']'
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException("$debugMessage: IOE", "An IO error has occurred retrieving a stock quote.", e)
|
||||
} catch (e: NullPointerException) {
|
||||
throw ModuleException("$debugMessage: NPE", "An error has occurred retrieving a stock quote.", e)
|
||||
}
|
||||
} else {
|
||||
messages.add(ErrorMessage(INVALID_SYMBOL))
|
||||
}
|
||||
return messages
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(STOCK_CMD)
|
||||
help.add("To retrieve a stock quote:")
|
||||
help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>"))
|
||||
initProperties(API_KEY_PROP)
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -1,250 +0,0 @@
|
|||
/*
|
||||
* Weather2.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.aksingh.owmjapis.api.APIException
|
||||
import net.aksingh.owmjapis.core.OWM
|
||||
import net.aksingh.owmjapis.core.OWM.Country
|
||||
import net.aksingh.owmjapis.model.CurrentWeather
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.capitalizeWords
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.pircbotx.Colors
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* The `Weather2` module.
|
||||
*/
|
||||
class Weather2 : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java)
|
||||
|
||||
override val name = "Weather"
|
||||
|
||||
/**
|
||||
* Fetches the weather data from a specific city.
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val messages = getWeather(args, properties[API_KEY_PROP])
|
||||
if (messages[0].isError) {
|
||||
helpResponse(event)
|
||||
} else {
|
||||
for (msg in messages) {
|
||||
event.sendMessage(channel, msg)
|
||||
}
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The OpenWeatherMap API Key property.
|
||||
*/
|
||||
const val API_KEY_PROP = "owm-api-key"
|
||||
|
||||
// Weather command
|
||||
private const val WEATHER_CMD = "weather"
|
||||
|
||||
/**
|
||||
* Converts and rounds temperature from °F to °C.
|
||||
*/
|
||||
fun ftoC(d: Double): Pair<Int, Int> {
|
||||
val c = (d - 32) * 5 / 9
|
||||
return d.roundToInt() to c.roundToInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.entries) {
|
||||
if (c.value.equals(countryCode, ignoreCase = true)) {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return Country.UNITED_STATES
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the weather data.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
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."
|
||||
)
|
||||
}
|
||||
val owm = OWM(apiKey)
|
||||
val messages = mutableListOf<Message>()
|
||||
owm.unit = OWM.Unit.IMPERIAL
|
||||
if (query.isNotBlank()) {
|
||||
val argv = query.split(",")
|
||||
if (argv.size in 1..2) {
|
||||
val city = argv[0].trim()
|
||||
val code: String = if (argv.size > 1 && argv[1].isNotBlank()) {
|
||||
argv[1].trim()
|
||||
} else {
|
||||
"US"
|
||||
}
|
||||
try {
|
||||
val country = getCountry(code)
|
||||
val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) {
|
||||
owm.currentWeatherByZipCode(city.toInt(), country)
|
||||
} else {
|
||||
owm.currentWeatherByCityName(city, country)
|
||||
}
|
||||
if (cwd.hasCityName()) {
|
||||
messages.add(
|
||||
PublicMessage(
|
||||
"City: ${cwd.cityName}, " +
|
||||
country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]"
|
||||
)
|
||||
)
|
||||
cwd.mainData?.let {
|
||||
with(it) {
|
||||
if (hasTemp()) {
|
||||
temp?.let { t ->
|
||||
val (f, c) = ftoC(t)
|
||||
messages.add(PublicMessage("Temperature: ${f}°F, ${c}°C"))
|
||||
}
|
||||
}
|
||||
if (hasHumidity()) {
|
||||
humidity?.let { h ->
|
||||
messages.add(NoticeMessage("Humidity: ${h.roundToInt()}%"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cwd.hasWindData()) {
|
||||
cwd.windData?.let {
|
||||
if (it.hasSpeed()) {
|
||||
it.speed?.let { s ->
|
||||
val w = mphToKmh(s)
|
||||
messages.add(NoticeMessage("Wind: ${w.first} mph, ${w.second} km/h"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cwd.hasWeatherList()) {
|
||||
val condition = StringBuilder("Condition:")
|
||||
cwd.weatherList?.let {
|
||||
for (w in it) {
|
||||
w?.let {
|
||||
condition.append(' ')
|
||||
.append(w.getDescription().capitalise())
|
||||
.append('.')
|
||||
}
|
||||
}
|
||||
messages.add(NoticeMessage(condition.toString()))
|
||||
}
|
||||
}
|
||||
if (cwd.hasCityId()) {
|
||||
cwd.cityId?.let {
|
||||
if (it > 0) {
|
||||
messages.add(
|
||||
NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN)
|
||||
)
|
||||
} else {
|
||||
messages.add(
|
||||
NoticeMessage(
|
||||
"https://openweathermap.org/find?q="
|
||||
+ "$city,${code.uppercase()}".encodeUrl(),
|
||||
Colors.GREEN
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: APIException) {
|
||||
if (e.code == 404) {
|
||||
throw ModuleException(
|
||||
"getWeather($query): API ${e.code}",
|
||||
"The requested city was not found.",
|
||||
e
|
||||
)
|
||||
} else {
|
||||
throw ModuleException("getWeather($query): API ${e.code}", e.message, e)
|
||||
}
|
||||
} catch (e: NullPointerException) {
|
||||
throw ModuleException("getWeather($query): NPE", "Unable to perform weather lookup.", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (messages.isEmpty()) {
|
||||
messages.add(ErrorMessage("Invalid syntax."))
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts and rounds temperature from mph to km/h.
|
||||
*/
|
||||
fun mphToKmh(w: Double): Pair<Int, Int> {
|
||||
val kmh = w * 1.60934
|
||||
return w.roundToInt() to kmh.roundToInt()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(WEATHER_CMD)
|
||||
with(help) {
|
||||
add("To display weather information:")
|
||||
add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]"))
|
||||
add("For example:")
|
||||
add(helpFormat("%c $WEATHER_CMD paris, fr"))
|
||||
add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.")
|
||||
}
|
||||
initProperties(API_KEY_PROP)
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* WolframAlpha.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
import net.thauvin.erik.mobibot.Utils.isHttpSuccess
|
||||
import net.thauvin.erik.mobibot.Utils.reader
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
class WolframAlpha : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(WolframAlpha::class.java)
|
||||
|
||||
override val name = "WolframAlpha"
|
||||
|
||||
private fun getUnits(unit: String?): String {
|
||||
return if (unit?.lowercase() == METRIC) {
|
||||
METRIC
|
||||
} else {
|
||||
IMPERIAL
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val query = args.trim().split("units=", limit = 2, ignoreCase = true)
|
||||
event.sendMessage(
|
||||
queryWolfram(
|
||||
query[0].trim(),
|
||||
units = if (query.size == 2) {
|
||||
getUnits(query[1].trim())
|
||||
} else {
|
||||
getUnits(properties[UNITS_PROP])
|
||||
},
|
||||
appId = properties[APPID_KEY_PROP]
|
||||
)
|
||||
)
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The Wolfram Alpha API Key property.
|
||||
*/
|
||||
const val APPID_KEY_PROP = "wolfram-appid"
|
||||
|
||||
/**
|
||||
* The Wolfram units properties
|
||||
*/
|
||||
const val UNITS_PROP = "wolfram-units"
|
||||
|
||||
const val METRIC = "metric"
|
||||
const val IMPERIAL = "imperial"
|
||||
|
||||
// Wolfram command
|
||||
private const val WOLFRAM_CMD = "wolfram"
|
||||
|
||||
// Wolfram Alpha API URL
|
||||
private const val API_URL = "http://api.wolframalpha.com/v1/spoken?appid="
|
||||
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String {
|
||||
if (!appId.isNullOrEmpty()) {
|
||||
try {
|
||||
val urlReader = URL("${API_URL}${appId}&units=${units}&i=" + query.encodeUrl()).reader()
|
||||
if (urlReader.responseCode.isHttpSuccess()) {
|
||||
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})"
|
||||
}
|
||||
)
|
||||
}
|
||||
} catch (ioe: IOException) {
|
||||
throw ModuleException(
|
||||
"wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe
|
||||
)
|
||||
}
|
||||
} else {
|
||||
throw ModuleException("wolfram($query): No API Key", "No Wolfram Alpha API key specified.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(WOLFRAM_CMD)
|
||||
with(help) {
|
||||
add("To get answers from Wolfram Alpha:")
|
||||
add(Utils.helpFormat("%c $WOLFRAM_CMD <query> [units=(${METRIC}|${IMPERIAL})]"))
|
||||
add("For example:")
|
||||
add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas"))
|
||||
add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric"))
|
||||
}
|
||||
initProperties(APPID_KEY_PROP, UNITS_PROP)
|
||||
}
|
||||
}
|
|
@ -1,390 +0,0 @@
|
|||
/*
|
||||
* WorldTime.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.temporal.ChronoField
|
||||
|
||||
/**
|
||||
* The WorldTime module.
|
||||
*/
|
||||
class WorldTime : AbstractModule() {
|
||||
override val name = "WorldTime"
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Beats (Internet Time) keyword
|
||||
*/
|
||||
const val BEATS_KEYWORD = ".beats"
|
||||
|
||||
/**
|
||||
* Supported countries
|
||||
*/
|
||||
val COUNTRIES_MAP = buildMap<String, String> {
|
||||
put("AG", "America/Antigua")
|
||||
put("AI", "America/Anguilla")
|
||||
put("AE", "Asia/Dubai")
|
||||
put("AD", "Europe/Andorra")
|
||||
put("AKDT", "America/Anchorage")
|
||||
put("AF", "Asia/Kabul")
|
||||
put("AKST", "America/Anchorage")
|
||||
put("AL", "Europe/Tirane")
|
||||
put("AM", "Asia/Yerevan")
|
||||
put("AO", "Africa/Luanda")
|
||||
put("AQ", "Antarctica/South_Pole")
|
||||
put("AR", "America/Argentina/Buenos_Aires")
|
||||
put("AS", "Pacific/Pago_Pago")
|
||||
put("AT", "Europe/Vienna")
|
||||
put("AU", "Australia/Sydney")
|
||||
put("AW", "America/Aruba")
|
||||
put("AX", "Europe/Mariehamn")
|
||||
put("AZ", "Asia/Baku")
|
||||
put("BA", "Europe/Sarajevo")
|
||||
put("BB", "America/Barbados")
|
||||
put("BD", "Asia/Dhaka")
|
||||
put("BE", "Europe/Brussels")
|
||||
put("BEAT", BEATS_KEYWORD)
|
||||
put("BF", "Africa/Ouagadougou")
|
||||
put("BG", "Europe/Sofia")
|
||||
put("BH", "Asia/Bahrain")
|
||||
put("BI", "Africa/Bujumbura")
|
||||
put("BJ", "Africa/Porto-Novo")
|
||||
put("BL", "America/St_Barthelemy")
|
||||
put("BM", "Atlantic/Bermuda")
|
||||
put("BMT", BEATS_KEYWORD)
|
||||
put("BN", "Asia/Brunei")
|
||||
put("BO", "America/La_Paz")
|
||||
put("BQ", "America/Kralendijk")
|
||||
put("BR", "America/Sao_Paulo")
|
||||
put("BS", "America/Nassau")
|
||||
put("BT", "Asia/Thimphu")
|
||||
put("BW", "Africa/Gaborone")
|
||||
put("BY", "Europe/Minsk")
|
||||
put("BZ", "America/Belize")
|
||||
put("CA", "America/Montreal")
|
||||
put("CC", "Indian/Cocos")
|
||||
put("CD", "Africa/Kinshasa")
|
||||
put("CDT", "America/Chicago")
|
||||
put("CET", "CET")
|
||||
put("CF", "Africa/Bangui")
|
||||
put("CG", "Africa/Brazzaville")
|
||||
put("CH", "Europe/Zurich")
|
||||
put("CI", "Africa/Abidjan")
|
||||
put("CK", "Pacific/Rarotonga")
|
||||
put("CL", "America/Santiago")
|
||||
put("CM", "Africa/Douala")
|
||||
put("CN", "Asia/Shanghai")
|
||||
put("CO", "America/Bogota")
|
||||
put("CR", "America/Costa_Rica")
|
||||
put("CST", "America/Chicago")
|
||||
put("CU", "Cuba")
|
||||
put("CV", "Atlantic/Cape_Verde")
|
||||
put("CW", "America/Curacao")
|
||||
put("CX", "Indian/Christmas")
|
||||
put("CY", "Asia/Nicosia")
|
||||
put("CZ", "Europe/Prague")
|
||||
put("DE", "Europe/Berlin")
|
||||
put("DJ", "Africa/Djibouti")
|
||||
put("DK", "Europe/Copenhagen")
|
||||
put("DM", "America/Dominica")
|
||||
put("DO", "America/Santo_Domingo")
|
||||
put("DZ", "Africa/Algiers")
|
||||
put("EC", "Pacific/Galapagos")
|
||||
put("EDT", "America/New_York")
|
||||
put("EE", "Europe/Tallinn")
|
||||
put("EG", "Africa/Cairo")
|
||||
put("EH", "Africa/El_Aaiun")
|
||||
put("ER", "Africa/Asmara")
|
||||
put("ES", "Europe/Madrid")
|
||||
put("EST", "America/New_York")
|
||||
put("ET", "Africa/Addis_Ababa")
|
||||
put("FI", "Europe/Helsinki")
|
||||
put("FJ", "Pacific/Fiji")
|
||||
put("FK", "Atlantic/Stanley")
|
||||
put("FM", "Pacific/Yap")
|
||||
put("FO", "Atlantic/Faroe")
|
||||
put("FR", "Europe/Paris")
|
||||
put("GA", "Africa/Libreville")
|
||||
put("GB", "Europe/London")
|
||||
put("GD", "America/Grenada")
|
||||
put("GE", "Asia/Tbilisi")
|
||||
put("GF", "America/Cayenne")
|
||||
put("GG", "Europe/Guernsey")
|
||||
put("GH", "Africa/Accra")
|
||||
put("GI", "Europe/Gibraltar")
|
||||
put("GL", "America/Thule")
|
||||
put("GM", "Africa/Banjul")
|
||||
put("GMT", "GMT")
|
||||
put("GN", "Africa/Conakry")
|
||||
put("GP", "America/Guadeloupe")
|
||||
put("GQ", "Africa/Malabo")
|
||||
put("GR", "Europe/Athens")
|
||||
put("GS", "Atlantic/South_Georgia")
|
||||
put("GT", "America/Guatemala")
|
||||
put("GU", "Pacific/Guam")
|
||||
put("GW", "Africa/Bissau")
|
||||
put("GY", "America/Guyana")
|
||||
put("HK", "Asia/Hong_Kong")
|
||||
put("HN", "America/Tegucigalpa")
|
||||
put("HR", "Europe/Zagreb")
|
||||
put("HST", "Pacific/Honolulu")
|
||||
put("HT", "America/Port-au-Prince")
|
||||
put("HU", "Europe/Budapest")
|
||||
put("ID", "Asia/Jakarta")
|
||||
put("IE", "Europe/Dublin")
|
||||
put("IL", "Asia/Tel_Aviv")
|
||||
put("IM", "Europe/Isle_of_Man")
|
||||
put("IN", "Asia/Kolkata")
|
||||
put("IO", "Indian/Chagos")
|
||||
put("IQ", "Asia/Baghdad")
|
||||
put("IR", "Asia/Tehran")
|
||||
put("IS", "Atlantic/Reykjavik")
|
||||
put("IT", "Europe/Rome")
|
||||
put("JE", "Europe/Jersey")
|
||||
put("JM", "Jamaica")
|
||||
put("JO", "Asia/Amman")
|
||||
put("JP", "Asia/Tokyo")
|
||||
put("KE", "Africa/Nairobi")
|
||||
put("KG", "Asia/Bishkek")
|
||||
put("KH", "Asia/Phnom_Penh")
|
||||
put("KI", "Pacific/Tarawa")
|
||||
put("KM", "Indian/Comoro")
|
||||
put("KN", "America/St_Kitts")
|
||||
put("KP", "Asia/Pyongyang")
|
||||
put("KR", "Asia/Seoul")
|
||||
put("KW", "Asia/Riyadh")
|
||||
put("KY", "America/Cayman")
|
||||
put("KZ", "Asia/Oral")
|
||||
put("LA", "Asia/Vientiane")
|
||||
put("LB", "Asia/Beirut")
|
||||
put("LC", "America/St_Lucia")
|
||||
put("LI", "Europe/Vaduz")
|
||||
put("LK", "Asia/Colombo")
|
||||
put("LR", "Africa/Monrovia")
|
||||
put("LS", "Africa/Maseru")
|
||||
put("LT", "Europe/Vilnius")
|
||||
put("LU", "Europe/Luxembourg")
|
||||
put("LV", "Europe/Riga")
|
||||
put("LY", "Africa/Tripoli")
|
||||
put("MA", "Africa/Casablanca")
|
||||
put("MC", "Europe/Monaco")
|
||||
put("MD", "Europe/Chisinau")
|
||||
put("MDT", "America/Denver")
|
||||
put("ME", "Europe/Podgorica")
|
||||
put("MF", "America/Marigot")
|
||||
put("MG", "Indian/Antananarivo")
|
||||
put("MH", "Pacific/Majuro")
|
||||
put("MK", "Europe/Skopje")
|
||||
put("ML", "Africa/Timbuktu")
|
||||
put("MM", "Asia/Yangon")
|
||||
put("MN", "Asia/Ulaanbaatar")
|
||||
put("MO", "Asia/Macau")
|
||||
put("MP", "Pacific/Saipan")
|
||||
put("MQ", "America/Martinique")
|
||||
put("MR", "Africa/Nouakchott")
|
||||
put("MS", "America/Montserrat")
|
||||
put("MST", "America/Denver")
|
||||
put("MT", "Europe/Malta")
|
||||
put("MU", "Indian/Mauritius")
|
||||
put("MV", "Indian/Maldives")
|
||||
put("MW", "Africa/Blantyre")
|
||||
put("MX", "America/Mexico_City")
|
||||
put("MY", "Asia/Kuala_Lumpur")
|
||||
put("MZ", "Africa/Maputo")
|
||||
put("NA", "Africa/Windhoek")
|
||||
put("NC", "Pacific/Noumea")
|
||||
put("NE", "Africa/Niamey")
|
||||
put("NF", "Pacific/Norfolk")
|
||||
put("NG", "Africa/Lagos")
|
||||
put("NI", "America/Managua")
|
||||
put("NL", "Europe/Amsterdam")
|
||||
put("NO", "Europe/Oslo")
|
||||
put("NP", "Asia/Kathmandu")
|
||||
put("NR", "Pacific/Nauru")
|
||||
put("NU", "Pacific/Niue")
|
||||
put("NZ", "Pacific/Auckland")
|
||||
put("OM", "Asia/Muscat")
|
||||
put("PA", "America/Panama")
|
||||
put("PDT", "America/Los_Angeles")
|
||||
put("PE", "America/Lima")
|
||||
put("PF", "Pacific/Tahiti")
|
||||
put("PG", "Pacific/Port_Moresby")
|
||||
put("PH", "Asia/Manila")
|
||||
put("PK", "Asia/Karachi")
|
||||
put("PL", "Europe/Warsaw")
|
||||
put("PM", "America/Miquelon")
|
||||
put("PN", "Pacific/Pitcairn")
|
||||
put("PR", "America/Puerto_Rico")
|
||||
put("PS", "Asia/Gaza")
|
||||
put("PST", "America/Los_Angeles")
|
||||
put("PT", "Europe/Lisbon")
|
||||
put("PW", "Pacific/Palau")
|
||||
put("PY", "America/Asuncion")
|
||||
put("QA", "Asia/Qatar")
|
||||
put("RE", "Indian/Reunion")
|
||||
put("RO", "Europe/Bucharest")
|
||||
put("RS", "Europe/Belgrade")
|
||||
put("RU", "Europe/Moscow")
|
||||
put("RW", "Africa/Kigali")
|
||||
put("SA", "Asia/Riyadh")
|
||||
put("SB", "Pacific/Guadalcanal")
|
||||
put("SC", "Indian/Mahe")
|
||||
put("SD", "Africa/Khartoum")
|
||||
put("SE", "Europe/Stockholm")
|
||||
put("SG", "Asia/Singapore")
|
||||
put("SH", "Atlantic/St_Helena")
|
||||
put("SI", "Europe/Ljubljana")
|
||||
put("SJ", "Atlantic/Jan_Mayen")
|
||||
put("SK", "Europe/Bratislava")
|
||||
put("SL", "Africa/Freetown")
|
||||
put("SM", "Europe/San_Marino")
|
||||
put("SN", "Africa/Dakar")
|
||||
put("SO", "Africa/Mogadishu")
|
||||
put("SR", "America/Paramaribo")
|
||||
put("SS", "Africa/Juba")
|
||||
put("ST", "Africa/Sao_Tome")
|
||||
put("SV", "America/El_Salvador")
|
||||
put("SX", "America/Lower_Princes")
|
||||
put("SY", "Asia/Damascus")
|
||||
put("SZ", "Africa/Mbabane")
|
||||
put("TC", "America/Grand_Turk")
|
||||
put("TD", "Africa/Ndjamena")
|
||||
put("TF", "Indian/Kerguelen")
|
||||
put("TG", "Africa/Lome")
|
||||
put("TH", "Asia/Bangkok")
|
||||
put("TJ", "Asia/Dushanbe")
|
||||
put("TK", "Pacific/Fakaofo")
|
||||
put("TL", "Asia/Dili")
|
||||
put("TM", "Asia/Ashgabat")
|
||||
put("TN", "Africa/Tunis")
|
||||
put("TO", "Pacific/Tongatapu")
|
||||
put("TR", "Europe/Istanbul")
|
||||
put("TT", "America/Port_of_Spain")
|
||||
put("TV", "Pacific/Funafuti")
|
||||
put("TW", "Asia/Taipei")
|
||||
put("TZ", "Africa/Dar_es_Salaam")
|
||||
put("UA", "Europe/Kiev")
|
||||
put("UG", "Africa/Kampala")
|
||||
put("UK", "Europe/London")
|
||||
put("UM", "Pacific/Wake")
|
||||
put("US", "America/New_York")
|
||||
put("UTC", "UTC")
|
||||
put("UY", "America/Montevideo")
|
||||
put("UZ", "Asia/Tashkent")
|
||||
put("VA", "Europe/Vatican")
|
||||
put("VC", "America/St_Vincent")
|
||||
put("VE", "America/Caracas")
|
||||
put("VG", "America/Tortola")
|
||||
put("VI", "America/St_Thomas")
|
||||
put("VN", "Asia/Ho_Chi_Minh")
|
||||
put("VU", "Pacific/Efate")
|
||||
put("WF", "Pacific/Wallis")
|
||||
put("WS", "Pacific/Apia")
|
||||
put("YE", "Asia/Aden")
|
||||
put("YT", "Indian/Mayotte")
|
||||
put("ZA", "Africa/Johannesburg")
|
||||
put("ZM", "Africa/Lusaka")
|
||||
put("ZULU", "Zulu")
|
||||
put("ZW", "Africa/Harare")
|
||||
ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) }
|
||||
.forEach { tz -> put(tz, tz) }
|
||||
}
|
||||
|
||||
// The Time command
|
||||
private const val TIME_CMD = "time"
|
||||
|
||||
// The zones arguments
|
||||
private const val ZONES_ARGS = "zones"
|
||||
|
||||
// The default zone
|
||||
private const val DEFAULT_ZONE = "PST"
|
||||
|
||||
// Date/Time Format
|
||||
private var dtf =
|
||||
DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '")
|
||||
|
||||
/**
|
||||
* Returns the current Internet (beat) Time.
|
||||
*/
|
||||
private fun internetTime(): String {
|
||||
val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00"))
|
||||
val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60
|
||||
+ zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt()
|
||||
return "%c%03d".format('@', beats)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time for the given timezone/city.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun time(query: String = DEFAULT_ZONE): String {
|
||||
val tz = COUNTRIES_MAP[(if (query.isNotBlank()) query.trim().uppercase() else DEFAULT_ZONE)]
|
||||
return if (tz != null) {
|
||||
if (BEATS_KEYWORD == tz) {
|
||||
"The current Internet Time is ${internetTime().bold()} $BEATS_KEYWORD"
|
||||
} else {
|
||||
(ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf)
|
||||
+ tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ').bold())
|
||||
}
|
||||
} else {
|
||||
"Unsupported country/zone. Please try again."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.equals(ZONES_ARGS, true)) {
|
||||
event.sendMessage("The supported countries/zones are: ")
|
||||
event.sendList(COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) }, 14, isIndent = true)
|
||||
} else {
|
||||
event.respond(time(args))
|
||||
}
|
||||
}
|
||||
|
||||
override val isPrivateMsgEnabled = true
|
||||
|
||||
init {
|
||||
with(help) {
|
||||
add("To display a country's current date/time:")
|
||||
add(helpFormat("%c $TIME_CMD [<country code or zone>]"))
|
||||
add("For a listing of the supported countries/zones:")
|
||||
add(helpFormat("%c $TIME_CMD $ZONES_ARGS"))
|
||||
}
|
||||
commands.add(TIME_CMD)
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* ErrorMessage.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.msg
|
||||
|
||||
/**
|
||||
* The `ErrorMessage` class.
|
||||
*/
|
||||
class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
||||
Message(msg, color, isError = true)
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Message.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.msg
|
||||
|
||||
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
|
||||
) {
|
||||
companion object {
|
||||
var DEFAULT_COLOR = Constants.EMPTY
|
||||
}
|
||||
|
||||
init {
|
||||
if (isError) {
|
||||
isNotice = true
|
||||
}
|
||||
}
|
||||
|
||||
/** Error flag. */
|
||||
var isError = isError
|
||||
set(value) {
|
||||
if (value) isNotice = true
|
||||
field = value
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, msg='$msg')"
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* NoticeMessage.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.msg
|
||||
|
||||
/**
|
||||
* The `NoticeMessage` class.
|
||||
*/
|
||||
class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
||||
Message(msg, color, isNotice = true)
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* PrivateMessage.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.msg
|
||||
|
||||
/**
|
||||
* The `PrivateMessage` class.
|
||||
*/
|
||||
class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
||||
Message(msg, color, isPrivate = true)
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* PublicMessage.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.msg
|
||||
|
||||
/**
|
||||
* The `PublicMessage` class.
|
||||
*/
|
||||
class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color)
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* SocialManager.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.social
|
||||
|
||||
import net.thauvin.erik.mobibot.Addons
|
||||
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.*
|
||||
|
||||
/**
|
||||
* Social Manager.
|
||||
*/
|
||||
class SocialManager {
|
||||
private val entries: MutableSet<Int> = HashSet()
|
||||
private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java)
|
||||
private val modules = ArrayList<SocialModule>()
|
||||
private val timer = Timer(true)
|
||||
|
||||
/**
|
||||
* Adds social modules.
|
||||
*/
|
||||
fun add(addons: Addons, vararg modules: SocialModule) {
|
||||
modules.forEach {
|
||||
if (addons.add(it)) {
|
||||
this.modules.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of entries.
|
||||
*/
|
||||
fun entriesCount(): Int = entries.size
|
||||
|
||||
/**
|
||||
* Sends a social notification (dm, etc.)
|
||||
*/
|
||||
fun notification(msg: String) {
|
||||
modules.forEach {
|
||||
it.notification(msg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts to social media.
|
||||
*/
|
||||
fun postEntry(index: Int) {
|
||||
if (entries.contains(index)) {
|
||||
modules.forEach {
|
||||
it.postEntry(index)
|
||||
}
|
||||
entries.remove(index)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues an entry for posting to social media.
|
||||
*/
|
||||
fun queueEntry(index: Int) {
|
||||
if (modules.isNotEmpty()) {
|
||||
entries.add(index)
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Scheduling {} for posting on social media.", index.toLinkLabel())
|
||||
}
|
||||
timer.schedule(SocialTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entries from queue.
|
||||
*/
|
||||
fun removeEntry(index: Int) {
|
||||
entries.remove(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts all entries on shutdown.
|
||||
*/
|
||||
fun shutdown() {
|
||||
timer.cancel()
|
||||
entries.forEach {
|
||||
postEntry(it)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* SocialModule.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.social
|
||||
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksManager
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import net.thauvin.erik.mobibot.modules.AbstractModule
|
||||
import net.thauvin.erik.mobibot.modules.ModuleException
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
abstract class SocialModule : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java)
|
||||
|
||||
abstract val handle: String?
|
||||
abstract val isAutoPost: Boolean
|
||||
|
||||
abstract fun formatEntry(entry: EntryLink): String
|
||||
|
||||
/**
|
||||
* Sends a DM.
|
||||
*/
|
||||
fun notification(msg: String) {
|
||||
if (isEnabled && !handle.isNullOrBlank()) {
|
||||
try {
|
||||
post(message = msg, isDm = true)
|
||||
if (logger.isDebugEnabled) logger.debug("Notified $handle on $name: $msg")
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to notify $handle on $name: $msg", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun post(message: String, isDm: Boolean): String
|
||||
|
||||
/**
|
||||
* Post entry to social media.
|
||||
*/
|
||||
fun postEntry(index: Int) {
|
||||
if (isAutoPost && LinksManager.entries.links.size >= index) {
|
||||
try {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Posting {} to $name.", index.toLinkLabel())
|
||||
}
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
try {
|
||||
event.respond(post("$args (by ${event.user.nick} on $channel)", false))
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* SocialTimer.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.social
|
||||
|
||||
import java.util.*
|
||||
|
||||
class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() {
|
||||
override fun run() {
|
||||
socialManager.postEntry(index)
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ current.xml
|
||||
~
|
||||
~ Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ Redistributions of source code must retain the above copyright notice, this
|
||||
~ list of conditions and the following disclaimer.
|
||||
~
|
||||
~ Redistributions in binary form must reproduce the above copyright notice,
|
||||
~ this list of conditions and the following disclaimer in the documentation
|
||||
~ and/or other materials provided with the distribution.
|
||||
~
|
||||
~ Neither the name of this project nor the names of its contributors may be
|
||||
~ used to endorse or promote products derived from this software without
|
||||
~ specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
|
||||
<channel>
|
||||
<title>#mobibot IRC Links</title>
|
||||
<link>https://www.mobitopia.org/mobibot/logs</link>
|
||||
<description>Links from irc.example.com on #mobibot</description>
|
||||
<language>en</language>
|
||||
<pubDate>Sun, 31 Oct 2021 21:45:11 GMT</pubDate>
|
||||
<dc:date>2021-10-31T21:45:11Z</dc:date>
|
||||
<dc:language>en</dc:language>
|
||||
<item>
|
||||
<title>Example 2</title>
|
||||
<link>https://www.example.com/2</link>
|
||||
<description>Posted by <b>Skynx</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a></description>
|
||||
<category>tag2-1</category>
|
||||
<category>tag2-2</category>
|
||||
<pubDate>Sun, 31 Oct 2021 21:45:11 GMT</pubDate>
|
||||
<guid>https://www.foo.com</guid>
|
||||
<dc:creator>mobibot@irc.libera.chat (Skynx)</dc:creator>
|
||||
<dc:date>2021-10-31T00:01:00Z</dc:date>
|
||||
</item>
|
||||
<item>
|
||||
<title>Example 1</title>
|
||||
<link>https://www.example.com/1</link>
|
||||
<description>Posted by <b>ErikT</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a>
|
||||
<br/><br/>ErikT: This is comment 1. <br/>Skynx: This is comment 2.
|
||||
</description>
|
||||
<category>tag1-1</category>
|
||||
<category>tag1-2</category>
|
||||
<pubDate>Sun, 31 Oct 2021 21:43:15 GMT</pubDate>
|
||||
<guid>https://www.example.com/</guid>
|
||||
<dc:creator>mobibot@irc.libera.chat (ErikT)</dc:creator>
|
||||
<dc:date>2021-10-31T00:00:00Z</dc:date>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* AddonsTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.containsExactly
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.size
|
||||
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.links.Comment
|
||||
import net.thauvin.erik.mobibot.commands.links.View
|
||||
import net.thauvin.erik.mobibot.modules.*
|
||||
import kotlin.test.Test
|
||||
import java.util.*
|
||||
|
||||
class AddonsTest {
|
||||
private val p = Properties().apply {
|
||||
put("disabled-modules", "war,dice Lookup")
|
||||
put("disabled-commands", "View | comment")
|
||||
}
|
||||
private val addons = Addons(p)
|
||||
|
||||
@Test
|
||||
fun addTest() {
|
||||
// Modules
|
||||
addons.add(Joke())
|
||||
addons.add(RockPaperScissors())
|
||||
addons.add(War())
|
||||
addons.add(Dice())
|
||||
addons.add(Lookup())
|
||||
assertThat(addons::modules).size().isEqualTo(2)
|
||||
assertThat(addons.names.modules, "names.modules").containsExactly("Joke", "RockPaperScissors")
|
||||
|
||||
// Commands
|
||||
addons.add(View())
|
||||
addons.add(Comment())
|
||||
addons.add(Cycle())
|
||||
addons.add(Die()) // invisible
|
||||
addons.add(ChannelFeed("channel")) // no properties, disabled
|
||||
p[Ignore.IGNORE_PROP] = "nick"
|
||||
addons.add(Ignore())
|
||||
assertThat(addons::commands).size().isEqualTo(3)
|
||||
|
||||
assertThat(addons.names.ops, "names.ops").containsExactly("cycle")
|
||||
|
||||
assertThat(addons.names.commands, "names.command").containsExactly(
|
||||
"joke",
|
||||
"rock",
|
||||
"paper",
|
||||
"scissors",
|
||||
"ignore"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* ExceptionSanitizer.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.obfuscate
|
||||
import net.thauvin.erik.mobibot.Utils.replaceEach
|
||||
import net.thauvin.erik.mobibot.modules.ModuleException
|
||||
|
||||
object ExceptionSanitizer {
|
||||
/**
|
||||
* Returns a sanitized exception to avoid displaying api keys, etc. in CI logs.
|
||||
*/
|
||||
fun ModuleException.sanitize(vararg sanitize: String): ModuleException {
|
||||
val search = sanitize.filter { it.isNotBlank() }.toTypedArray()
|
||||
if (search.isNotEmpty()) {
|
||||
val obfuscate = search.map { it.obfuscate() }.toTypedArray()
|
||||
with(this) {
|
||||
if (!cause?.message.isNullOrBlank()) {
|
||||
return ModuleException(
|
||||
debugMessage,
|
||||
cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate),
|
||||
this
|
||||
)
|
||||
} else if (!message.isNullOrBlank()) {
|
||||
return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this)
|
||||
}
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* FeedReaderTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import com.rometools.rome.io.FeedException
|
||||
import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import kotlin.test.Test
|
||||
import java.io.IOException
|
||||
import java.net.MalformedURLException
|
||||
import java.net.UnknownHostException
|
||||
|
||||
/**
|
||||
* The `FeedReader Test` class.
|
||||
*/
|
||||
class FeedReaderTest {
|
||||
@Test
|
||||
fun readFeedTest() {
|
||||
var messages = readFeed("https://feeds.thauvin.net/ethauvin")
|
||||
assertThat(messages, "messages").all {
|
||||
size().isEqualTo(10)
|
||||
index(1).prop(Message::msg).contains("erik.thauvin.net")
|
||||
}
|
||||
|
||||
messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0")
|
||||
assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing")
|
||||
|
||||
messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42)
|
||||
assertThat(messages, "messages").size().isEqualTo(84)
|
||||
messages.forEachIndexed { i, m ->
|
||||
if (i % 2 == 0) {
|
||||
assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum")
|
||||
} else {
|
||||
assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/")
|
||||
}
|
||||
}
|
||||
|
||||
assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java)
|
||||
|
||||
assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java)
|
||||
|
||||
assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java)
|
||||
|
||||
assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java)
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* LocalProperties.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import org.testng.annotations.BeforeSuite
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Access to `local.properties`.
|
||||
*/
|
||||
open class LocalProperties {
|
||||
@BeforeSuite(alwaysRun = true)
|
||||
fun loadProperties() {
|
||||
val localPath = Paths.get("local.properties")
|
||||
if (Files.exists(localPath)) {
|
||||
try {
|
||||
Files.newInputStream(localPath).use { stream -> localProps.load(stream) }
|
||||
} catch (ignore: IOException) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val localProps = Properties()
|
||||
|
||||
fun getHostName(): String {
|
||||
val ciName = System.getenv("CI_NAME")
|
||||
return ciName ?: try {
|
||||
InetAddress.getLocalHost().hostName
|
||||
} catch (ignore: UnknownHostException) {
|
||||
"Unknown Host"
|
||||
}
|
||||
}
|
||||
|
||||
fun getProperty(key: String): String {
|
||||
return if (localProps.containsKey(key)) {
|
||||
localProps.getProperty(key)
|
||||
} else {
|
||||
val env = System.getenv(keyToEnv(key))
|
||||
env?.let {
|
||||
localProps.setProperty(key, env)
|
||||
}
|
||||
env
|
||||
}
|
||||
}
|
||||
|
||||
private fun keyToEnv(key: String): String {
|
||||
return key.replace('-', '_').uppercase()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* PinboardTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
import net.thauvin.erik.mobibot.Utils.reader
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import org.testng.Assert.assertFalse
|
||||
import org.testng.Assert.assertTrue
|
||||
import kotlin.test.Test
|
||||
import java.net.URL
|
||||
|
||||
class PinboardTest : LocalProperties() {
|
||||
private val pinboard = Pinboard()
|
||||
|
||||
@Test
|
||||
fun testPinboard() {
|
||||
val apiToken = getProperty("pinboard-api-token")
|
||||
val url = "https://www.example.com/${(1000..5000).random()}"
|
||||
val ircServer = "irc.test.com"
|
||||
val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test"))
|
||||
|
||||
pinboard.setApiToken(apiToken)
|
||||
|
||||
pinboard.addPin(ircServer, entry)
|
||||
assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin")
|
||||
|
||||
entry.link = "https://www.example.com/${(5001..9999).random()}"
|
||||
pinboard.updatePin(ircServer, url, entry)
|
||||
assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin")
|
||||
|
||||
entry.title = "Foo Title"
|
||||
pinboard.updatePin(ircServer, entry.link, entry)
|
||||
assertTrue(validatePin(apiToken, url = entry.link, entry.title), "updatePin(${entry.title}")
|
||||
|
||||
pinboard.deletePin(entry)
|
||||
assertFalse(validatePin(apiToken, url = entry.link), "deletePin")
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
matches.forEach {
|
||||
if (!response.contains(it)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return response.contains(url)
|
||||
}
|
||||
}
|
|
@ -1,278 +0,0 @@
|
|||
/*
|
||||
* UtilsTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.length
|
||||
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.capitalizeWords
|
||||
import net.thauvin.erik.mobibot.Utils.colorize
|
||||
import net.thauvin.erik.mobibot.Utils.cyan
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
import net.thauvin.erik.mobibot.Utils.getIntProperty
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.lastOrEmpty
|
||||
import net.thauvin.erik.mobibot.Utils.obfuscate
|
||||
import net.thauvin.erik.mobibot.Utils.plural
|
||||
import net.thauvin.erik.mobibot.Utils.reader
|
||||
import net.thauvin.erik.mobibot.Utils.red
|
||||
import net.thauvin.erik.mobibot.Utils.replaceEach
|
||||
import net.thauvin.erik.mobibot.Utils.reverseColor
|
||||
import net.thauvin.erik.mobibot.Utils.toIntOrDefault
|
||||
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
||||
import net.thauvin.erik.mobibot.Utils.today
|
||||
import net.thauvin.erik.mobibot.Utils.underline
|
||||
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
||||
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
||||
import org.pircbotx.Colors
|
||||
import org.testng.annotations.BeforeClass
|
||||
import kotlin.test.Test
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.time.LocalDateTime
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The `Utils Test` class.
|
||||
*/
|
||||
class UtilsTest {
|
||||
private val ascii =
|
||||
" !\"#$%&'()*+,-./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."
|
||||
|
||||
@BeforeClass
|
||||
fun setUp() {
|
||||
cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAppendIfMissing() {
|
||||
val dir = "dir"
|
||||
val sep = '/'
|
||||
val url = "https://erik.thauvin.net"
|
||||
assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)")
|
||||
.isEqualTo(dir + File.separatorChar)
|
||||
assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep")
|
||||
assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBold() {
|
||||
assertThat(1.bold(), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD)
|
||||
assertThat(2L.bold(), "bold(2L)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD)
|
||||
assertThat(ascii.bold(), "ascii.bold()").isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
||||
assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testCapitalise() {
|
||||
assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test")
|
||||
assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test")
|
||||
assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test)
|
||||
assertThat("".capitalise(), "capitalize()").isEqualTo("")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun textCapitaliseWords() {
|
||||
assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.")
|
||||
assertThat("Already Capitalized".capitalizeWords(), "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
|
||||
)
|
||||
assertThat(ascii.colorize(Colors.RED), "red.colorize()")
|
||||
.isEqualTo(Colors.RED + ascii + Colors.NORMAL)
|
||||
assertThat(ascii.colorize(Colors.BOLD), "colorized(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)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCyan() {
|
||||
assertThat(ascii.cyan()).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEncodeUrl() {
|
||||
assertThat("Hello Günter".encodeUrl()).isEqualTo("Hello%20G%C3%BCnter")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetIntProperty() {
|
||||
val p = Properties()
|
||||
p["one"] = "1"
|
||||
p["two"] = "two"
|
||||
assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1)
|
||||
assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2)
|
||||
assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGreen() {
|
||||
assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHelpCmdSyntax() {
|
||||
val bot = "mobibot"
|
||||
assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)")
|
||||
.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")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHelpFormat() {
|
||||
assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)")
|
||||
.isEqualTo("${Colors.BOLD}$test${Colors.BOLD}")
|
||||
assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)")
|
||||
.isEqualTo(test.prependIndent())
|
||||
assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)")
|
||||
.isEqualTo(test.colorize(Colors.BOLD).prependIndent())
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsoLocalDate() {
|
||||
assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17")
|
||||
assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLastOrEmpty() {
|
||||
val two = listOf("1", "2")
|
||||
assertThat(two.lastOrEmpty(), "lastOrEmpty(1,2)").isEqualTo("2")
|
||||
val one = listOf("1")
|
||||
assertThat(one.lastOrEmpty(), "lastOrEmpty(1)").isEqualTo("")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObfuscate() {
|
||||
assertThat(ascii.obfuscate(), "obfuscate()").all {
|
||||
length().isEqualTo(ascii.length)
|
||||
isEqualTo(("x".repeat(ascii.length)))
|
||||
}
|
||||
assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPlural() {
|
||||
val week = "week"
|
||||
val weeks = "weeks"
|
||||
|
||||
for (i in -1..3) {
|
||||
assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReplaceEach() {
|
||||
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(","))
|
||||
|
||||
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"))
|
||||
|
||||
assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)")
|
||||
.isEqualTo(test)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRed() {
|
||||
assertThat(ascii.red()).isEqualTo(ascii.colorize(Colors.RED))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReverseColor() {
|
||||
assertThat(ascii.reverseColor()).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToday() {
|
||||
assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToIntOrDefault() {
|
||||
assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10)
|
||||
assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnderline() {
|
||||
assertThat(ascii.underline()).isEqualTo(ascii.colorize(Colors.UNDERLINE))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnescapeXml() {
|
||||
assertThat("<a name="test & ''">".unescapeXml()).isEqualTo(
|
||||
"<a name=\"test & ''\">"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(IOException::class)
|
||||
fun testUrlReader() {
|
||||
val reader = URL("https://postman-echo.com/status/200").reader()
|
||||
assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}")
|
||||
assertThat(reader.responseCode).isEqualTo(200)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUtcDateTime() {
|
||||
assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30")
|
||||
assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30")
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* InfoTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime
|
||||
import kotlin.test.Test
|
||||
|
||||
class InfoTest {
|
||||
@Test
|
||||
fun testToUptime() {
|
||||
assertThat(
|
||||
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)"
|
||||
).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")
|
||||
assertThat(59000L.toUptime(), "upTime(59 seconds)").isEqualTo("59 seconds")
|
||||
assertThat(0L.toUptime(), "upTime(0 second)").isEqualTo("0 second")
|
||||
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* RecapTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.matches
|
||||
import assertk.assertions.prop
|
||||
import assertk.assertions.size
|
||||
import kotlin.test.Test
|
||||
|
||||
class RecapTest {
|
||||
@Test
|
||||
fun storeRecapTest() {
|
||||
for (i in 1..20) {
|
||||
Recap.storeRecap("sender$i", "test $i", false)
|
||||
}
|
||||
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())
|
||||
prop(MutableList<String>::last)
|
||||
.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())
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* LinksManagerTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isTrue
|
||||
import assertk.assertions.size
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import kotlin.test.Test
|
||||
|
||||
class LinksManagerTest {
|
||||
private val linksManager = LinksManager()
|
||||
|
||||
@Test
|
||||
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)"
|
||||
).isEqualTo(Constants.NO_TITLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMatches() {
|
||||
assertThat(linksManager.matches("https://www.example.com/"), "matches(url)").isTrue()
|
||||
assertThat(linksManager.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "matches(HTTP)").isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun matchTagKeywordsTest() {
|
||||
linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3")
|
||||
val tags = mutableListOf<String>()
|
||||
|
||||
linksManager.matchTagKeywords("Test title with key2", tags)
|
||||
assertThat(tags, "tags").contains("key2")
|
||||
tags.clear()
|
||||
|
||||
linksManager.matchTagKeywords("Test key3 title with key1", tags)
|
||||
assertThat(tags, "tags(key1, key3)").all {
|
||||
contains("key1")
|
||||
contains("key3")
|
||||
size().isEqualTo(2)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* ViewTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.prop
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import kotlin.test.Test
|
||||
|
||||
class ViewTest {
|
||||
@Test
|
||||
fun testParseArgs() {
|
||||
val view = View()
|
||||
|
||||
for (i in 1..10) {
|
||||
LinksManager.entries.links.add(
|
||||
EntryLink(
|
||||
"https://www.example.com/$i",
|
||||
"Example $i",
|
||||
"nick$i",
|
||||
"login$i",
|
||||
"#channel",
|
||||
emptyList()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs("1"), "parseArgs(1)").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||
prop(Pair<Int, String>::second).isEqualTo("")
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(1)
|
||||
prop(Pair<Int, String>::second).isEqualTo("foo")
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(2)
|
||||
prop(Pair<Int, String>::second).isEqualTo("foo")
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(3)
|
||||
prop(Pair<Int, String>::second).isEqualTo("foo bar")
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs("foo bar"), "parseArgs(foo bar)").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||
prop(Pair<Int, String>::second).isEqualTo("foo bar")
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs("${Int.MAX_VALUE}1"), "parseArgs(overflow)").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||
prop(Pair<Int, String>::second).isEqualTo("${Int.MAX_VALUE}1")
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs("1a"), "parseArgs(1a)").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||
prop(Pair<Int, String>::second).isEqualTo("1a")
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs("20"), "parseArgs(20)").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||
prop(Pair<Int, String>::second).isEqualTo("")
|
||||
}
|
||||
|
||||
assertThat(view.parseArgs(""), "parseArgs()").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(LinksManager.entries.links.size - View.MAX_ENTRIES)
|
||||
prop(Pair<Int, String>::second).isEqualTo("")
|
||||
}
|
||||
|
||||
LinksManager.entries.links.clear()
|
||||
|
||||
assertThat(view.parseArgs("4"), "parseArgs(4)").all {
|
||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||
prop(Pair<Int, String>::second).isEqualTo("")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* SeenTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.seen
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import org.testng.annotations.AfterClass
|
||||
import org.testng.annotations.BeforeClass
|
||||
import kotlin.test.Test
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.fileSize
|
||||
|
||||
class SeenTest {
|
||||
private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser")
|
||||
private val seen = Seen(tmpFile.toAbsolutePath().toString())
|
||||
private val nick = "ErikT"
|
||||
|
||||
@BeforeClass
|
||||
fun saveTest() {
|
||||
seen.add("ErikT")
|
||||
assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0)
|
||||
}
|
||||
|
||||
@AfterClass(alwaysRun = true)
|
||||
fun afterClass() {
|
||||
tmpFile.deleteIfExists()
|
||||
}
|
||||
|
||||
@Test(priority = 1, groups = ["commands"])
|
||||
fun loadTest() {
|
||||
seen.clear()
|
||||
assertThat(seen::seenNicks).isEmpty()
|
||||
seen.load()
|
||||
assertThat(seen::seenNicks).key(nick).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addTest() {
|
||||
val last = seen.seenNicks[nick]?.lastSeen
|
||||
seen.add(nick.lowercase())
|
||||
assertThat(seen).all {
|
||||
prop(Seen::seenNicks).size().isEqualTo(1)
|
||||
prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last)
|
||||
prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(nick.lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
@Test(priority = 10, groups = ["commands"])
|
||||
fun clearTest() {
|
||||
seen.clear()
|
||||
seen.save()
|
||||
seen.load()
|
||||
assertThat(seen::seenNicks).size().isEqualTo(0)
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* TellMessageTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.commands.tell
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isTrue
|
||||
import assertk.assertions.prop
|
||||
import kotlin.test.Test
|
||||
import java.time.Duration
|
||||
import java.time.LocalDateTime
|
||||
import java.time.temporal.Temporal
|
||||
|
||||
/**
|
||||
* The `TellMessageTest` class.
|
||||
*/
|
||||
class TellMessageTest {
|
||||
private fun isValidDate(date: Temporal): Boolean {
|
||||
return Duration.between(date, LocalDateTime.now()).toMinutes() < 1
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTellMessage() {
|
||||
val message = "Test message."
|
||||
val recipient = "recipient"
|
||||
val sender = "sender"
|
||||
val tellMessage = TellMessage(sender, recipient, message)
|
||||
assertThat(tellMessage).all {
|
||||
prop(TellMessage::sender).isEqualTo(sender)
|
||||
prop(TellMessage::recipient).isEqualTo(recipient)
|
||||
prop(TellMessage::message).isEqualTo(message)
|
||||
}
|
||||
assertThat(isValidDate(tellMessage.queued), "isValidDate()").isTrue()
|
||||
assertThat(tellMessage.isMatch(sender), "isMatch(sender)").isTrue()
|
||||
assertThat(tellMessage.isMatch(recipient), "isMatch(recipient)").isTrue()
|
||||
assertThat(tellMessage.isMatch("foo"), "isMatch(foo)").isFalse()
|
||||
tellMessage.isReceived = false
|
||||
assertThat(tellMessage.receptionDate, "receptionDate").isEqualTo(LocalDateTime.MIN)
|
||||
tellMessage.isReceived = true
|
||||
assertThat(isValidDate(tellMessage.receptionDate), "isValidDate(creationDate)").isTrue()
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* TellMessagesMgrTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.commands.tell
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import org.testng.annotations.AfterClass
|
||||
import org.testng.annotations.BeforeClass
|
||||
import kotlin.test.Test
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.io.path.createTempFile
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.fileSize
|
||||
|
||||
class TellMessagesMgrTest {
|
||||
private val testFile = createTempFile(suffix = ".ser")
|
||||
private val maxDays = 10L
|
||||
private val testMessages = mutableListOf<TellMessage>().apply {
|
||||
for (i in 0..5) {
|
||||
this.add(i, TellMessage("sender$i", "recipient$i", "message $i"))
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
fun saveTest() {
|
||||
TellManager.save(testFile.toAbsolutePath().toString(), testMessages)
|
||||
assertThat(testFile.fileSize()).isGreaterThan(0)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
fun afterClass() {
|
||||
testFile.deleteIfExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cleanTest() {
|
||||
testMessages.add(TellMessage("sender", "recipient", "message").apply {
|
||||
queued = LocalDateTime.now().minusDays(maxDays)
|
||||
})
|
||||
val size = testMessages.size
|
||||
assertThat(TellManager.clean(testMessages, maxDays + 2), "clean(maxDays=${maxDays + 2})").isFalse()
|
||||
assertThat(TellManager.clean(testMessages, maxDays), "clean(maxDays=$maxDays)").isTrue()
|
||||
assertThat(testMessages, "testMessages").size().isEqualTo(size - 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadTest() {
|
||||
val messages = TellManager.load(testFile.toAbsolutePath().toString())
|
||||
for (i in messages.indices) {
|
||||
assertThat(messages).index(i).all {
|
||||
prop(TellMessage::sender).isEqualTo(testMessages[i].sender)
|
||||
prop(TellMessage::recipient).isEqualTo(testMessages[i].recipient)
|
||||
prop(TellMessage::message).isEqualTo(testMessages[i].message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
* EntriesUtilsTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.entries
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isEqualTo
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
||||
import kotlin.test.Test
|
||||
|
||||
class EntriesUtilsTest {
|
||||
private val comment = EntryComment("comment", "nick")
|
||||
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")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun printCommentTest() {
|
||||
assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun printLinkTest() {
|
||||
for (i in links.indices) {
|
||||
assertThat(
|
||||
printLink(i - 1, links[i]), "link $i"
|
||||
).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )")
|
||||
}
|
||||
|
||||
assertThat(links.first().addComment(comment), "addComment()").isEqualTo(0)
|
||||
assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun printTagsTest() {
|
||||
for (i in links.indices) {
|
||||
assertThat(
|
||||
printTags(i - 1, links[i]), "tag $i"
|
||||
).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toLinkLabelTest() {
|
||||
assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2")
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* EntryLinkTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.entries
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import com.rometools.rome.feed.synd.SyndCategory
|
||||
import com.rometools.rome.feed.synd.SyndCategoryImpl
|
||||
import kotlin.test.Test
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The `EntryUtilsTest` class.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
||||
* @created 2019-04-19
|
||||
* @since 1.0
|
||||
*/
|
||||
class EntryLinkTest {
|
||||
private val entryLink = EntryLink(
|
||||
"https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia",
|
||||
listOf("tag1", "tag2", "tag3", "TAG4", "Tag5")
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testAddDeleteComment() {
|
||||
var i = 0
|
||||
while (i < 5) {
|
||||
entryLink.addComment("c$i", "u$i")
|
||||
i++
|
||||
}
|
||||
assertThat(entryLink.comments, "comments").size().isEqualTo(i)
|
||||
i = 0
|
||||
for (comment in entryLink.comments) {
|
||||
assertThat(comment).all {
|
||||
prop(EntryComment::comment).isEqualTo("c$i")
|
||||
prop(EntryComment::nick).isEqualTo("u$i")
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
val r = SecureRandom()
|
||||
while (entryLink.comments.size > 0) {
|
||||
entryLink.deleteComment(r.nextInt(entryLink.comments.size))
|
||||
}
|
||||
assertThat(entryLink.comments, "hasComments()").isEmpty()
|
||||
entryLink.addComment("nothing", "nobody")
|
||||
entryLink.setComment(0, "something", "somebody")
|
||||
val comment = entryLink.getComment(0)
|
||||
assertThat(comment, "comment[first]").all {
|
||||
prop(EntryComment::nick).isEqualTo("somebody")
|
||||
prop(EntryComment::comment).isEqualTo("something")
|
||||
}
|
||||
assertThat(entryLink.deleteComment(comment), "deleteComment").isTrue()
|
||||
assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConstructor() {
|
||||
val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" })
|
||||
val link = EntryLink("link", "title", "nick", "channel", Date(), tags)
|
||||
assertThat(link, "link").all {
|
||||
prop(EntryLink::tags).size().isEqualTo(tags.size)
|
||||
prop(EntryLink::tags).index(0).prop(SyndCategory::getName).isEqualTo("tag1")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMatches() {
|
||||
assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue()
|
||||
assertThat(entryLink.matches("skynx"), "match(nick)").isTrue()
|
||||
assertThat(entryLink.matches("www.mobitopia.org"), "matches(url)").isTrue()
|
||||
assertThat(entryLink.matches("foo"), "matches(foo)").isFalse()
|
||||
assertThat(entryLink.matches("<empty>"), "matches(empty)").isFalse()
|
||||
assertThat(entryLink.matches(null), "matches(null)").isFalse()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testTags() {
|
||||
val tags: List<SyndCategory> = entryLink.tags
|
||||
for ((i, tag) in tags.withIndex()) {
|
||||
assertThat(tag.name, "tag.name($i)").isEqualTo("tag${i + 1}")
|
||||
}
|
||||
assertThat(entryLink::tags).size().isEqualTo(5)
|
||||
entryLink.setTags("-tag5, tag4")
|
||||
entryLink.setTags("+mobitopia")
|
||||
entryLink.setTags("-mobitopia")
|
||||
assertThat(
|
||||
entryLink.formatTags(","),
|
||||
"formatTags(',')"
|
||||
).isEqualTo("tag1,tag2,tag3,tag4,mobitopia")
|
||||
entryLink.setTags("-tag4 tag5")
|
||||
assertThat(
|
||||
entryLink.formatTags(" ", ","), "formatTag(' ',',')"
|
||||
).isEqualTo(",tag1 tag2 tag3 mobitopia tag5")
|
||||
val size = entryLink.tags.size
|
||||
entryLink.setTags("")
|
||||
assertThat(entryLink.tags, "setTags('')").size().isEqualTo(size)
|
||||
entryLink.setTags(" ")
|
||||
assertThat(entryLink.tags, "setTags(' ')").size().isEqualTo(size)
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* FeedMgrTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.entries
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import net.thauvin.erik.mobibot.Utils.today
|
||||
import org.testng.annotations.BeforeSuite
|
||||
import kotlin.test.Test
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.fileSize
|
||||
import kotlin.io.path.name
|
||||
|
||||
class FeedMgrTest {
|
||||
private val entries = Entries()
|
||||
private val channel = "mobibot"
|
||||
|
||||
@BeforeSuite(alwaysRun = true)
|
||||
fun beforeSuite() {
|
||||
entries.logsDir = "src/test/resources/"
|
||||
entries.ircServer = "irc.example.com"
|
||||
entries.channel = channel
|
||||
entries.backlogs = "https://www.mobitopia.org/mobibot/logs"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFeedMgr() {
|
||||
// Load the feed
|
||||
assertThat(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31")
|
||||
|
||||
assertThat(entries.links, "entries.links").size().isEqualTo(2)
|
||||
entries.links.forEachIndexed { i, entryLink ->
|
||||
assertThat(entryLink, "entryLink[${i + 1}]").all {
|
||||
prop(EntryLink::title).isEqualTo("Example ${i + 1}")
|
||||
prop(EntryLink::link).isEqualTo("https://www.example.com/${i + 1}")
|
||||
prop(EntryLink::channel).isEqualTo(channel)
|
||||
}
|
||||
entryLink.tags.forEachIndexed { y, tag ->
|
||||
assertThat(tag.name, "tag${i + 1}-${y + 1}").isEqualTo("tag${i + 1}-${y + 1}")
|
||||
}
|
||||
}
|
||||
|
||||
with(entries.links.first()) {
|
||||
assertThat(nick, "nick[first]").isEqualTo("ErikT")
|
||||
assertThat(date, "date[first]").isEqualTo(Date(1635638400000L))
|
||||
assertThat(comments.first(), "comments[first]").all {
|
||||
prop(EntryComment::comment).endsWith("comment 1.")
|
||||
prop(EntryComment::nick).isEqualTo("ErikT")
|
||||
}
|
||||
assertThat(comments.last(), "comments[last]").all {
|
||||
prop(EntryComment::comment).endsWith("comment 2.")
|
||||
prop(EntryComment::nick).isEqualTo("Skynx")
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(entries.links, "links").index(1).all {
|
||||
prop(EntryLink::nick).isEqualTo("Skynx")
|
||||
prop(EntryLink::date).isEqualTo(Date(1635638460000L))
|
||||
}
|
||||
|
||||
val currentFile = Paths.get("${entries.logsDir}test.xml")
|
||||
val backlogFile = Paths.get("${entries.logsDir}${today()}.xml")
|
||||
|
||||
// Save the feed
|
||||
FeedsManager.saveFeed(entries, currentFile.name)
|
||||
|
||||
assertThat(currentFile, "currentFile").exists()
|
||||
assertThat(backlogFile, "backlogFile").exists()
|
||||
|
||||
assertThat(currentFile.fileSize(), "currentFile == backlogFile").isEqualTo(backlogFile.fileSize())
|
||||
|
||||
// Load the test feed
|
||||
entries.links.clear()
|
||||
FeedsManager.loadFeed(entries, currentFile.name)
|
||||
|
||||
entries.links.forEachIndexed { i, entryLink ->
|
||||
assertThat(entryLink.title, "entryLink.title[${i + 1}]").isEqualTo("Example ${i + 1}")
|
||||
}
|
||||
|
||||
assertThat(currentFile.deleteIfExists(), "currentFile.deleteIfExists()").isTrue()
|
||||
assertThat(backlogFile.deleteIfExists(), "backlogFile.deleteIfExists()").isTrue()
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* CalcTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isInstanceOf
|
||||
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* The `CalcTest` class.
|
||||
*/
|
||||
class CalcTest {
|
||||
@Test
|
||||
fun testCalculate() {
|
||||
assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}")
|
||||
assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}")
|
||||
assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}")
|
||||
assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* ChatGptTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.hasNoCause
|
||||
import assertk.assertions.isInstanceOf
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import kotlin.test.Test
|
||||
|
||||
class ChatGptTest : LocalProperties() {
|
||||
@Test
|
||||
fun testApiKey() {
|
||||
assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) }
|
||||
.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)
|
||||
).contains("XMLHttpRequest")
|
||||
assertThat(
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* CryptoPricesTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isGreaterThan
|
||||
import assertk.assertions.prop
|
||||
import net.thauvin.erik.crypto.CryptoPrice
|
||||
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice
|
||||
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName
|
||||
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies
|
||||
import org.testng.annotations.BeforeClass
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* The `CryptoPricesTest` class.
|
||||
*/
|
||||
class CryptoPricesTest {
|
||||
@BeforeClass
|
||||
@Throws(ModuleException::class)
|
||||
fun before() {
|
||||
loadCurrencies()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun testMarketPrice() {
|
||||
var price = currentPrice(listOf("BTC"))
|
||||
assertThat(price, "currentPrice(BTC)").all {
|
||||
prop(CryptoPrice::base).isEqualTo("BTC")
|
||||
prop(CryptoPrice::currency).isEqualTo("USD")
|
||||
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
||||
}
|
||||
|
||||
price = currentPrice(listOf("ETH", "EUR"))
|
||||
assertThat(price, "currentPrice(ETH, EUR)").all {
|
||||
prop(CryptoPrice::base).isEqualTo("ETH")
|
||||
prop(CryptoPrice::currency).isEqualTo("EUR")
|
||||
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCurrencyName() {
|
||||
assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar")
|
||||
assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro")
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* CurrencyConverterTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.matches
|
||||
import assertk.assertions.prop
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency
|
||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.testng.annotations.BeforeClass
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
/**
|
||||
* The `CurrencyConvertTest` class.
|
||||
*/
|
||||
class CurrencyConverterTest : LocalProperties() {
|
||||
|
||||
@BeforeClass
|
||||
@Throws(ModuleException::class)
|
||||
fun before() {
|
||||
val apiKey = getProperty(CurrencyConverter.API_KEY_PROP)
|
||||
loadSymbols(apiKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConvertCurrency() {
|
||||
val apiKey = getProperty(CurrencyConverter.API_KEY_PROP)
|
||||
assertThat(
|
||||
convertCurrency(apiKey, "100 USD to EUR").msg,
|
||||
"convertCurrency(100 USD to EUR)"
|
||||
).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex())
|
||||
assertThat(
|
||||
convertCurrency(apiKey, "1 USD to GBP").msg,
|
||||
"convertCurrency(1 USD to BGP)"
|
||||
).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex())
|
||||
assertThat(
|
||||
convertCurrency(apiKey, "100,000.00 CAD to USD").msg,
|
||||
"convertCurrency(100,000.00 GBP to USD)"
|
||||
).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex())
|
||||
assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all {
|
||||
prop(Message::msg).contains("You're kidding, right?")
|
||||
isInstanceOf(PublicMessage::class.java)
|
||||
}
|
||||
assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all {
|
||||
prop(Message::msg).contains("Invalid query.")
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* DiceTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.matches
|
||||
import kotlin.test.Test
|
||||
|
||||
class DiceTest {
|
||||
@Test
|
||||
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")
|
||||
assertThat(Dice.roll(5, 1), "roll(5d1)")
|
||||
.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())
|
||||
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())
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* GoogleSearchTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
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
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* The `GoogleSearchTest` class.
|
||||
*/
|
||||
class GoogleSearchTest : LocalProperties() {
|
||||
@Test
|
||||
fun testAPIKeys() {
|
||||
assertThat(
|
||||
searchGoogle("", "apikey", "cssKey").first(),
|
||||
"searchGoogle(empty)"
|
||||
).isInstanceOf(ErrorMessage::class.java)
|
||||
|
||||
assertFailure { searchGoogle("test", "", "apiKey") }
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
|
||||
assertFailure { searchGoogle("test", "apiKey", "") }
|
||||
.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.")
|
||||
}
|
||||
|
||||
@Test\n@DisableOnCi
|
||||
@Throws(ModuleException::class)
|
||||
fun testSearchGoogle() {
|
||||
val apiKey = getProperty(GoogleSearch.API_KEY_PROP)
|
||||
val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP)
|
||||
|
||||
try {
|
||||
var query = "mobibot"
|
||||
var messages = searchGoogle(query, apiKey, cseKey)
|
||||
assertThat(messages, "searchGoogle($query)").all {
|
||||
isNotEmpty()
|
||||
index(0).prop(Message::msg).contains(query, true)
|
||||
}
|
||||
|
||||
query = "adadflkjl"
|
||||
messages = searchGoogle(query, apiKey, cseKey)
|
||||
assertThat(messages, "searchGoogle($query)").index(0).all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo("No results found.")
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
// Avoid displaying api keys in CI logs
|
||||
if ("true" == System.getenv("CI")) {
|
||||
throw e.sanitize(apiKey, cseKey)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* JokeTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
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
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* The `JokeTest` class.
|
||||
*/
|
||||
class JokeTest {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun testRandomJoke() {
|
||||
val joke = randomJoke()
|
||||
assertThat(joke, "randomJoke()").all {
|
||||
size().isGreaterThan(0)
|
||||
each {
|
||||
it.isInstanceOf(PublicMessage::class.java)
|
||||
it.prop(Message::msg).doesNotContain("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* LookupTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.any
|
||||
import assertk.assertions.contains
|
||||
import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup
|
||||
import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* The `Lookup Test` class.
|
||||
*/
|
||||
class LookupTest {
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testLookup() {
|
||||
var result = nslookup("apple.com")
|
||||
assertThat(result, "lookup(apple.com)").contains("17.253.144.10")
|
||||
|
||||
result = nslookup("204.122.16.136")
|
||||
assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testWhois() {
|
||||
val result = whois("17.178.96.59", Lookup.WHOIS_HOST)
|
||||
assertThat(result, "whois(17.178.96.59").any { it.contains("Apple Inc.") }
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* MastodonTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot
|
||||
import kotlin.test.Test
|
||||
|
||||
class MastodonTest : LocalProperties() {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
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
|
||||
)
|
||||
).contains(msg)
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* ModuleExceptionTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
|
||||
import org.testng.annotations.DataProvider
|
||||
import kotlin.test.Test
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.Method
|
||||
|
||||
/**
|
||||
* The `ModuleExceptionTest` class.
|
||||
*/
|
||||
class ModuleExceptionTest {
|
||||
companion object {
|
||||
const val DEBUG_MESSAGE = "debugMessage"
|
||||
const val MESSAGE = "message"
|
||||
}
|
||||
|
||||
@DataProvider(name = "dp")
|
||||
fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> {
|
||||
return arrayOf(
|
||||
arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))),
|
||||
arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))),
|
||||
arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE))
|
||||
)
|
||||
}
|
||||
|
||||
@Test(dataProvider = "dp")
|
||||
fun testGetDebugMessage(e: ModuleException) {
|
||||
assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE)
|
||||
}
|
||||
|
||||
@Test(dataProvider = "dp")
|
||||
fun testGetMessage(e: ModuleException) {
|
||||
assertThat(e).hasMessage(MESSAGE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSanitizeMessage() {
|
||||
val apiKey = "1234567890"
|
||||
var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me"))
|
||||
assertThat(
|
||||
e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))"
|
||||
).isNotNull().all {
|
||||
contains("xxxxxxxxxx", "userID=xx", "java.io.IOException")
|
||||
doesNotContain(apiKey, "me")
|
||||
}
|
||||
|
||||
e = ModuleException(DEBUG_MESSAGE, MESSAGE, null)
|
||||
assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE)
|
||||
|
||||
e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException())
|
||||
assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE)
|
||||
|
||||
e = ModuleException(DEBUG_MESSAGE, apiKey)
|
||||
assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull()
|
||||
.doesNotContain(apiKey)
|
||||
|
||||
val msg: String? = null
|
||||
e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg))
|
||||
assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull()
|
||||
|
||||
e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey"))
|
||||
assertThat(
|
||||
e.sanitize(" ", apiKey, "foo").message,
|
||||
"ModuleException(debugMessage, msg, IOException(foo is $apiKey))"
|
||||
).isNotNull().all {
|
||||
doesNotContain(apiKey)
|
||||
endsWith("xxx is xxxxxxxxxx")
|
||||
}
|
||||
assertThat(e.sanitize(), "exception should be unchanged").isEqualTo(e)
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* PingTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isNotEmpty
|
||||
import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* The `PingTest` class.
|
||||
*/
|
||||
class PingTest {
|
||||
@Test
|
||||
fun testPingsArray() {
|
||||
assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRandomPing() {
|
||||
for (i in 0..9) {
|
||||
assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* RockPaperScissorsTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw
|
||||
import kotlin.test.Test
|
||||
|
||||
class RockPaperScissorsTest {
|
||||
@Test
|
||||
fun testWinLoseOrDraw() {
|
||||
assertThat(winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win")
|
||||
assertThat(winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win")
|
||||
assertThat(winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win")
|
||||
assertThat(winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose")
|
||||
assertThat(winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose")
|
||||
assertThat(winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose")
|
||||
assertThat(winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw")
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* StockQuoteTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
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
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* The `StockQuoteTest` class.
|
||||
*/
|
||||
class StockQuoteTest : LocalProperties() {
|
||||
private fun buildMatch(label: String): String {
|
||||
return "${label}:[ ]+[0-9.]+".prependIndent()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun testGetQuote() {
|
||||
val apiKey = getProperty(StockQuote.API_KEY_PROP)
|
||||
try {
|
||||
var symbol = "apple inc"
|
||||
val messages = getQuote(symbol, apiKey)
|
||||
assertThat(messages, "response not empty").isNotEmpty()
|
||||
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())
|
||||
assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex())
|
||||
|
||||
symbol = "blahfoo"
|
||||
assertThat(getQuote(symbol, apiKey).first(), "getQuote($symbol)").all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||
}
|
||||
assertThat(getQuote("", "apikey").first(), "getQuote(empty)").all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||
}
|
||||
assertFailure { getQuote("test", "") }.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
} catch (e: ModuleException) {
|
||||
// Avoid displaying api keys in CI logs
|
||||
if ("true" == System.getenv("CI")) {
|
||||
throw e.sanitize(apiKey)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Weather2Test.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import net.aksingh.owmjapis.api.APIException
|
||||
import net.aksingh.owmjapis.core.OWM
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.API_KEY_PROP
|
||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC
|
||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry
|
||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather
|
||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* The `Weather2Test` class.
|
||||
*/
|
||||
class Weather2Test : LocalProperties() {
|
||||
@Test
|
||||
fun testFtoC() {
|
||||
val t = ftoC(32.0)
|
||||
assertThat(t.second, "32 °F is 0 °C").isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCountry() {
|
||||
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.entries.toTypedArray()
|
||||
repeat(3) {
|
||||
val rand = country[(country.indices).random()]
|
||||
assertThat(getCountry(rand.value), rand.name).isEqualTo(rand)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMphToKmh() {
|
||||
val w = mphToKmh(0.62)
|
||||
assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun testWeather() {
|
||||
var query = "98204"
|
||||
var messages = getWeather(query, getProperty(API_KEY_PROP))
|
||||
assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all {
|
||||
contains("Everett, United States")
|
||||
contains("US")
|
||||
}
|
||||
assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS")
|
||||
|
||||
query = "San Francisco"
|
||||
messages = getWeather(query, getProperty(API_KEY_PROP))
|
||||
assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all {
|
||||
contains("San Francisco")
|
||||
contains("US")
|
||||
}
|
||||
assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959")
|
||||
|
||||
query = "London, GB"
|
||||
messages = getWeather(query, getProperty(API_KEY_PROP))
|
||||
assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all {
|
||||
contains("London, United Kingdom")
|
||||
contains("GB")
|
||||
}
|
||||
assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("2643743")
|
||||
|
||||
try {
|
||||
query = "Foo, US"
|
||||
getWeather(query, getProperty(API_KEY_PROP))
|
||||
} catch (e: ModuleException) {
|
||||
assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java)
|
||||
}
|
||||
|
||||
query = "test"
|
||||
assertFailure { getWeather(query, "") }.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
assertFailure { getWeather(query, null) }.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
|
||||
messages = getWeather("", "apikey")
|
||||
assertThat(messages, "getWeather(empty)").index(0).prop(Message::isError).isTrue()
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* WolframAlphaTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.hasMessage
|
||||
import assertk.assertions.isInstanceOf
|
||||
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram
|
||||
import kotlin.test.Test
|
||||
|
||||
class WolframAlphaTest : LocalProperties() {
|
||||
@Test
|
||||
fun testAppId() {
|
||||
assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") }
|
||||
.isInstanceOf(ModuleException::class.java)
|
||||
.hasMessage("Error 1: Invalid appid")
|
||||
|
||||
assertFailure { queryWolfram("1 gallon to liter", appId = "") }
|
||||
.isInstanceOf(ModuleException::class.java)
|
||||
}
|
||||
|
||||
@Test(groups = ["modules", "no-ci"])
|
||||
@Throws(ModuleException::class)
|
||||
fun queryWolframTest() {
|
||||
val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP)
|
||||
try {
|
||||
var query = "SFO to SEA"
|
||||
assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles")
|
||||
|
||||
query = "SFO to LAX"
|
||||
assertThat(
|
||||
queryWolfram(query, WolframAlpha.METRIC, apiKey),
|
||||
"queryWolfram($query)"
|
||||
).contains("kilometers")
|
||||
} catch (e: ModuleException) {
|
||||
// Avoid displaying api key in CI logs
|
||||
if ("true" == System.getenv("CI")) {
|
||||
throw e.sanitize(apiKey)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* WordTimeTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.endsWith
|
||||
import assertk.assertions.matches
|
||||
import assertk.assertions.startsWith
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.BEATS_KEYWORD
|
||||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP
|
||||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time
|
||||
import org.pircbotx.Colors
|
||||
import kotlin.test.Test
|
||||
import java.time.ZoneId
|
||||
|
||||
/**
|
||||
* The `WordTimeTest` class.
|
||||
*/
|
||||
class WordTimeTest {
|
||||
@Test
|
||||
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()
|
||||
)
|
||||
assertThat(time(""), "time()").endsWith("Los Angeles".bold())
|
||||
assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold())
|
||||
assertThat(time("GB"), "time(GB)").endsWith("London".bold())
|
||||
assertThat(time("FR"), "time(FR)").endsWith("Paris".bold())
|
||||
assertThat(time("BLAH"), "time(BLAH)").startsWith("Unsupported")
|
||||
assertThat(time("BEAT"), "time($BEATS_KEYWORD)").matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testZones() {
|
||||
COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach {
|
||||
assertThat(ZoneId.of(it.value))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* MessageTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.msg
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isTrue
|
||||
import assertk.assertions.prop
|
||||
import kotlin.test.Test
|
||||
|
||||
class MessageTest {
|
||||
@Test
|
||||
fun testConstructor() {
|
||||
var msg = Message("foo")
|
||||
|
||||
msg.isError = true
|
||||
assertThat(msg.isNotice, "message is notice").isTrue()
|
||||
|
||||
msg = Message("foo", isError = true)
|
||||
assertThat(msg.isNotice, "message is notice too").isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testErrorMessage() {
|
||||
val msg = ErrorMessage("foo")
|
||||
assertThat(msg).all {
|
||||
prop(Message::isError).isTrue()
|
||||
prop(Message::isNotice).isTrue()
|
||||
prop(Message::isPrivate).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsError() {
|
||||
val msg = Message("foo")
|
||||
msg.isError = true
|
||||
assertThat(msg).all {
|
||||
prop(Message::isError).isTrue()
|
||||
prop(Message::isNotice).isTrue()
|
||||
prop(Message::isPrivate).isFalse()
|
||||
}
|
||||
msg.isError = false
|
||||
assertThat(msg).all {
|
||||
prop(Message::isError).isFalse()
|
||||
prop(Message::isNotice).isTrue()
|
||||
prop(Message::isPrivate).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoticeMessage() {
|
||||
val msg = NoticeMessage("food")
|
||||
assertThat(msg).all {
|
||||
prop(Message::isError).isFalse()
|
||||
prop(Message::isNotice).isTrue()
|
||||
prop(Message::isPrivate).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPrivateMessage() {
|
||||
val msg = PrivateMessage("foo")
|
||||
assertThat(msg).all {
|
||||
prop(Message::isPrivate).isTrue()
|
||||
prop(Message::isError).isFalse()
|
||||
prop(Message::isNotice).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPublicMessage() {
|
||||
val msg = PublicMessage("foo")
|
||||
assertThat(msg).all {
|
||||
prop(Message::isError).isFalse()
|
||||
prop(Message::isNotice).isFalse()
|
||||
prop(Message::isPrivate).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,11 +14,11 @@ import java.time.ZoneId
|
|||
*/
|
||||
object ReleaseInfo {
|
||||
const val PROJECT = "mobibot"
|
||||
const val VERSION = "0.8.0-rc+20231110234054"
|
||||
const val VERSION = "0.8.0-rc+20231111131146"
|
||||
|
||||
@JvmField
|
||||
val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(1699688454937L), ZoneId.systemDefault()
|
||||
Instant.ofEpochMilli(1699737106723L), ZoneId.systemDefault()
|
||||
)
|
||||
|
||||
const val WEBSITE = "https://www.mobitopia.org/mobibot/"
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="warn">
|
||||
<Appenders>
|
||||
<Console name="stderr" target="SYSTEM_ERR">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
<Console name="input" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{UNIX_MILLIS} %msg%n"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</Filters>
|
||||
</Console>
|
||||
<Console name="output" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{UNIX_MILLIS} >>>%msg%n"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</Filters>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="warn" additivity="false">
|
||||
<AppenderRef ref="stderr"/>
|
||||
</Root>
|
||||
<logger level="debug" name="org.pircbotx.InputParser" additivity="false">
|
||||
<appender-ref ref="input"/>
|
||||
<appender-ref ref="stderr" level="warn"/>
|
||||
</logger>
|
||||
<logger level="debug" name="org.pircbotx.output.OutputRaw" additivity="false">
|
||||
<appender-ref ref="output"/>
|
||||
<appender-ref ref="stderr" level="warn"/>
|
||||
</logger>
|
||||
<logger level="trace" name="net.thauvin.erik.mobibot" additivity="false">
|
||||
<appender-ref ref="stderr"/>
|
||||
</logger>
|
||||
</Loggers>
|
||||
</Configuration>
|
Loading…
Add table
Add a link
Reference in a new issue