Version 0.8 starts.

This commit is contained in:
Erik C. Thauvin 2020-03-28 11:31:52 -07:00
parent 0d913eca4d
commit 4b95b0bbe2
49 changed files with 2284 additions and 1318 deletions

2
.idea/mobibot.iml generated
View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.7.3-beta+739" type="JAVA_MODULE" version="4">
<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-alpha+033" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.7.3-beta+739" type="JAVA_MODULE" version="4">
<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-alpha+033" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false">

File diff suppressed because one or more lines are too long

View file

@ -88,7 +88,7 @@ spotbugs {
tasks.withType(com.github.spotbugs.snom.SpotBugsTask) {
reports {
xml.enabled = false
xml.enabled = true
html.enabled = true
}
excludeFilter.set(file("$projectDir/config/spotbugs/excludeFilter.xml"))
@ -140,7 +140,7 @@ clean {
}
run {
args '--v'
//args '--v'
}
incrementBuildMeta {

View file

@ -6,7 +6,7 @@
<Match>
<Or>
<Package name="net.thauvin.erik.mobibot.*"/>
<Package name="net.thauvin.erik.mobibot.tell.*"/>
<Package name="net.thauvin.erik.mobibot.commands.tell.*"/>
<Package name="net.thauvin.erik.mobibot.entries.*"/>
</Or>
<Or>
@ -21,7 +21,7 @@
<Class name="net.thauvin.erik.mobibot.Mobibot"/>
<Class name="net.thauvin.erik.mobibot.Pinboard"/>
<Class name="net.thauvin.erik.mobibot.FeedReader"/>
<Class name="net.thauvin.erik.mobibot.tell.Tell"/>
<Class name="net.thauvin.erik.mobibot.commands.tell.Tell"/>
<Package name="net.thauvin.erik.mobibot.modules.*"/>
<Package name="net.thauvin.erik.mobibot.entries.*"/>
</Or>
@ -33,4 +33,7 @@
<Bug pattern="PATH_TRAVERSAL_OUT"/>
<Confidence value="1"/>
</Match>
<Match>
<Source name="~.*\.kt"/>
</Match>
</FindBugsFilter>

View file

@ -1,37 +1,23 @@
<?xml version="1.0" ?>
<!--
~ detekt-baseline.xml
~
~ Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
~ All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice, this
~ list of conditions and the following disclaimer.
~
~ Redistributions in binary form must reproduce the above copyright notice,
~ this list of conditions and the following disclaimer in the documentation
~ and/or other materials provided with the distribution.
~
~ Neither the name of this project nor the names of its contributors may be
~ used to endorse or promote products derived from this software without
~ specific prior written permission.
~
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<SmellBaseline>
<Blacklist></Blacklist>
<Whitelist></Whitelist>
<Whitelist>
<ID>ComplexMethod:Links.kt$Links$commandResponse</ID>
<ID>ComplexMethod:UrlMgr.kt$UrlMgr$commandResponse</ID>
<ID>ComplexMethod:View.kt$View$commandResponse</ID>
<ID>LongMethod:Links.kt$Links$commandResponse</ID>
<ID>LongMethod:UrlMgr.kt$UrlMgr$commandResponse</ID>
<ID>LongMethod:View.kt$View$commandResponse</ID>
<ID>LongParameterList:AbstractCommand.kt$AbstractCommand$( bot: Mobibot, sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID>
<ID>LoopWithTooManyJumpStatements:View.kt$View$while (i &lt; max) { entry = getEntry(i) if (lcArgs.isNotEmpty()) { if (entry.link.toLowerCase().contains(lcArgs) || entry.title.toLowerCase().contains(lcArgs) || entry.nick.toLowerCase().contains(lcArgs)) { if (sent &gt; maxEntries) { bot.send( sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"), isPrivate ) break } bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) sent++ } } else { if (sent &gt; maxEntries) { bot.send( sender, "To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}"), isPrivate ) break } bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate) sent++ } i++ }</ID>
<ID>MagicNumber:Comments.kt$Comments$3</ID>
<ID>MagicNumber:Cycle.kt$Cycle$10</ID>
<ID>MagicNumber:Recap.kt$Recap.Companion$10</ID>
<ID>MagicNumber:View.kt$View$8</ID>
<ID>NestedBlockDepth:Comments.kt$Comments$commandResponse</ID>
<ID>NestedBlockDepth:Ignore.kt$Ignore$commandResponse</ID>
<ID>NestedBlockDepth:Links.kt$Links$commandResponse</ID>
<ID>NestedBlockDepth:UrlMgr.kt$UrlMgr$commandResponse</ID>
<ID>NestedBlockDepth:View.kt$View$commandResponse</ID>
</Whitelist>
</SmellBaseline>

View file

@ -14,13 +14,13 @@ import java.time.*;
public final class ReleaseInfo {
public static final String PROJECT = "mobibot";
public static final LocalDateTime BUILDDATE =
LocalDateTime.ofInstant(Instant.ofEpochMilli(1585119580379L), ZoneId.systemDefault());
LocalDateTime.ofInstant(Instant.ofEpochMilli(1585416159853L), ZoneId.systemDefault());
public static final int MAJOR = 0;
public static final int MINOR = 7;
public static final int PATCH = 3;
public static final String PRERELEASE = "beta";
public static final String BUILDMETA = "760";
public static final String VERSION = "0.7.3-beta+760";
public static final int MINOR = 8;
public static final int PATCH = 0;
public static final String PRERELEASE = "alpha";
public static final String BUILDMETA = "045";
public static final String VERSION = "0.8.0-alpha+045";
/**
* Disables the default constructor.

View file

@ -1,149 +0,0 @@
/*
* Commands.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot;
/**
* The <code>commands</code>, <code>keywords</code> and <code>arguments</code>.
*
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
* @created 2014-04-26
* @since 1.0
*/
public final class Commands {
/**
* The link command.
*/
public static final String LINK_CMD = "L";
/**
* The view command.
*/
public static final String VIEW_CMD = "view";
/**
* The add (back)log command.
*/
static final String ADDLOG_CMD = "addlog";
/**
* The cycle command.
*/
static final String CYCLE_CMD = "cycle";
/**
* Debug command line argument.
*/
static final String DEBUG_ARG = "debug";
/**
* The debug command.
*/
static final String DEBUG_CMD = "debug";
/**
* The die command.
*/
static final String DIE_CMD = "die";
/**
* Help command line argument.
*/
static final String HELP_ARG = "help";
/**
* The help command.
*/
static final String HELP_CMD = "help";
/**
* The help on posting keyword.
*/
static final String HELP_POSTING_KEYWORD = "posting";
/**
* The help on tags keyword.
*/
static final String HELP_TAGS_KEYWORD = "tags";
/**
* The ignore command.
*/
static final String IGNORE_CMD = "ignore";
/**
* The ignore <code>me</code> keyword.
*/
static final String IGNORE_ME_KEYWORD = "me";
/**
* The info command.
*/
static final String INFO_CMD = "info";
/**
* The me command.
*/
static final String ME_CMD = "me";
/**
* The modules command.
*/
static final String MODULES_CMD = "modules";
/**
* The msg command.
*/
static final String MSG_CMD = "msg";
/**
* The nick command.
*/
static final String NICK_CMD = "nick";
/**
* Properties command line argument.
*/
static final String PROPS_ARG = "properties";
/**
* The recap command.
*/
static final String RECAP_CMD = "recap";
/**
* The say command.
*/
static final String SAY_CMD = "say";
/**
* The users command.
*/
static final String USERS_CMD = "users";
/**
* Properties version line argument.
*/
static final String VERSION_ARG = "version";
/**
* The version command.
*/
static final String VERSION_CMD = "version";
/**
* Disables the default constructor.
*
* @throws UnsupportedOperationException If the constructor is called.
*/
private Commands() {
throw new UnsupportedOperationException("Illegal constructor call.");
}
}

View file

@ -1,7 +1,7 @@
/*
* Constants.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -42,10 +42,38 @@ import java.util.Locale;
* @since 1.0
*/
public final class Constants {
/**
* The add (back)log command.
*/
public static final String ADDLOG_CMD = "addlog";
/**
* The connect/read timeout in ms.
*/
public static final int CONNECT_TIMEOUT = 5000;
/**
* Debug command line argument.
*/
public static final String DEBUG_ARG = "debug";
/**
* The debug command.
*/
public static final String DEBUG_CMD = "debug";
/**
* The die command.
*/
public static final String DIE_CMD = "die";
/**
* Help command line argument.
*/
public static final String HELP_ARG = "help";
/**
* The help command.
*/
public static final String HELP_CMD = "help";
/**
* The link command.
*/
public static final String LINK_CMD = "L";
/**
* Default locale.
*/
@ -54,6 +82,10 @@ public final class Constants {
* The empty title string.
*/
public static final String NO_TITLE = "No Title";
/**
* Properties command line argument.
*/
public static final String PROPS_ARG = "properties";
/**
* The timer delay in minutes.
*/
@ -66,6 +98,10 @@ public final class Constants {
* The Twitter handle property key.
*/
public static final String TWITTER_HANDLE_PROP = "twitter-handle";
/**
* Properties version line argument.
*/
public static final String VERSION_ARG = "version";
/**
* Disables the default constructor.

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,6 @@ import java.util.*
class TwitterTimer(var bot: Mobibot, private var index: Int) : TimerTask() {
override fun run() {
bot.twitterAutoPost(index)
bot.twitterEntryPost(index)
}
}

View file

@ -159,6 +159,16 @@ public final class Utils {
return colorize(s, Colors.DARK_GREEN);
}
/**
* Returns indented help string.
*
* @param help The help string.
* @return The indented help string.
*/
public static String helpIndent(final String help) {
return " " + help;
}
/**
* Returns the specified date as an ISO local date string.
*

View file

@ -0,0 +1,67 @@
/*
* AbstractCommand.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
abstract class AbstractCommand {
abstract val command: String
abstract val help: List<String>
abstract val isOp: Boolean
abstract val isPublic: Boolean
abstract val isVisible: Boolean
abstract fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
)
open fun helpResponse(bot: Mobibot, command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean {
if (!this.isOp || this.isOp == isOp) {
for (h in help) {
bot.send(sender, String.format(h, bot.nick), isPrivate)
}
return true
}
return false
}
open fun matches(message: String): Boolean {
return false
}
}

View file

@ -0,0 +1,68 @@
/*
* Cycle.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
class Cycle : AbstractCommand() {
private val wait = 10
override val command = "cycle"
override val help = listOf(
Utils.bold("To have the bot leave the channel and come back:"),
Utils.helpIndent("/msg %s $command")
)
override val isOp = true
override val isPublic = false
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (isOp) {
bot.send(bot.channel, "$sender has just asked me to leave. I'll be back!")
bot.sleep(wait)
bot.partChannel(bot.channel)
bot.sleep(wait)
bot.joinChannel(bot.channel)
} else {
bot.helpDefault(sender, isOp)
}
}
}

View file

@ -0,0 +1,111 @@
/*
* Ignore.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import java.util.*
class Ignore(defaultIgnore: String) : AbstractCommand() {
private val keyword = "me"
init {
if (defaultIgnore.isNotBlank()) {
ignored.addAll(defaultIgnore.split(", +?| +"))
}
}
override val command = IGNORE
override val help = listOf(
Utils.bold("To check your ignore status:"),
Utils.helpIndent("%s: $command"),
Utils.bold("To toggle your ignore status:"),
Utils.helpIndent("%s: $command $keyword")
)
override val isOp = false
override val isPublic = true
override val isVisible = true
companion object {
const val IGNORE = "ignore"
private val ignored = HashSet<String>()
@JvmStatic
fun isNotIgnored(nick: String): Boolean {
return !ignored.contains(nick.toLowerCase())
}
}
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (isOp) {
val nick = sender.toLowerCase()
val isMe = args.toLowerCase().startsWith(keyword)
if (isMe) {
if (ignored.remove(nick)) {
bot.send(sender, "You are no longer ignored.")
} else {
ignored.add(nick)
bot.send(sender, "You are now ignored.")
}
} else {
if (ignored.contains(nick)) {
bot.send(sender, "You are currently ignored.")
} else {
bot.send(sender, "You are not currently ignored.")
}
}
} else {
if (args.isNotEmpty()) {
val nicks = args.toLowerCase().split(" ")
for (nick in nicks) {
val ignore = if (keyword == nick) {
sender.toLowerCase()
} else {
nick
}
if (!ignored.remove(ignore)) {
ignored.add(ignore)
}
}
}
bot.send(sender, "The following nicks are ignored: ${ignored.joinToString(", ")}")
}
}
}

View file

@ -0,0 +1,92 @@
/*
* Info.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import net.thauvin.erik.mobibot.commands.links.UrlMgr
import org.jibble.pircbot.Colors
import java.lang.management.ManagementFactory
class Info : AbstractCommand() {
override val command = "info"
override val help = listOf(
Utils.bold("To view information about the bot:"),
Utils.helpIndent("%s: $command")
)
override val isOp = false
override val isPublic = true
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
for (info in Mobibot.INFO) {
if (info.startsWith("https://")) {
bot.send(sender, info, Colors.DARK_GREEN, isPrivate)
} else {
bot.send(sender, info, isPrivate)
}
}
val info = StringBuilder("Uptime: ")
with(info) {
append(Utils.uptime(ManagementFactory.getRuntimeMXBean().uptime))
append(" [Entries: ")
append(UrlMgr.entriesCount)
if (isOp) {
if (bot.tell.isEnabled) {
append(", Messages: ")
append(bot.tell.size())
}
if (bot.isTwitterAutoPost) {
append(", Twitter: ")
append(bot.twitterLinksCount())
}
}
append(", Recap: ")
append(Recap.recapCount())
append(']')
}
bot.send(sender, info.toString(), isPrivate)
}
}

View file

@ -0,0 +1,62 @@
/*
* Me.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
class Me : AbstractCommand() {
override val command = "me"
override val help = listOf(
Utils.bold("To have the bot perform an action:"),
Utils.helpIndent("/msg %s $command <action>")
)
override val isOp = true
override val isPublic = false
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (isOp) {
bot.action(args)
} else {
bot.helpDefault(sender, isOp)
}
}
}

View file

@ -0,0 +1,64 @@
/*
* AbstractCommandImpl.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
class MobibotVersion : AbstractCommand() {
override val command = "version"
override val help = listOf(
Utils.bold("To view the version data (bot, java, etc.):"),
Utils.helpIndent("/msg %s $command")
)
override val isOp = true
override val isPublic = false
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (isOp) {
for (v in Mobibot.MOBIBOT_VERSIONS) {
bot.send(sender, v, isPrivate)
}
} else {
bot.helpDefault(sender, isOp)
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Modules.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
class Modules : AbstractCommand() {
override val command = "modules"
override val help = listOf(
Utils.bold("To view a list of enabled modules:"),
Utils.helpIndent("/msg %s $command")
)
override val isOp = true
override val isPublic = false
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (isOp) {
val modulesNames = bot.modulesNames
if (modulesNames.isEmpty()) {
bot.send(sender, "There are no enabled modules.", true)
} else {
bot.send(sender, Utils.bold("The enabled modules are: "))
bot.send(sender, Utils.helpIndent(modulesNames.joinToString(", ")))
}
} else {
bot.helpDefault(sender, isOp)
}
}
}

View file

@ -0,0 +1,67 @@
/*
* Msg.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
class Msg : AbstractCommand() {
override val command = "msg"
override val help = listOf(
Utils.bold("To have the bot send a private message to someone:"),
Utils.helpIndent("/msg %s $command <nick> <text>")
)
override val isOp = true
override val isPublic = true
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (isOp) {
val msg = args.split(" ", limit = 2)
if (args.length > 2) {
bot.send(msg[0], msg[1], true)
} else {
helpResponse(bot, command, sender, isOp, isPrivate)
}
} else {
bot.helpDefault(sender, isOp)
}
}
}

View file

@ -0,0 +1,62 @@
/*
* Nick.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
class Nick : AbstractCommand() {
override val command = "nick"
override val help = listOf(
Utils.bold("To change the bot's nickname:"),
Utils.helpIndent("/msg %s $command <nick>")
)
override val isOp = true
override val isPublic = true
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (isOp) {
bot.changeNick(args)
} else {
bot.helpDefault(sender, isOp)
}
}
}

View file

@ -0,0 +1,95 @@
/*
* Recap.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import java.time.Clock
import java.time.LocalDateTime
import java.util.*
class Recap : AbstractCommand() {
override val command = "recap"
override val help = listOf(
Utils.bold("To list the last 10 public channel messages:"),
Utils.helpIndent("%s: $command"),
Utils.helpIndent("/msg %s $command")
)
override val isOp = false
override val isPublic = true
override val isVisible = true
companion object {
private val recaps = ArrayList<String>(0)
@JvmStatic
fun recapCount(): Int {
return recaps.size
}
/**
* Stores the last 10 public messages and actions.
*
* @param sender The nick of the person who sent the private message.
* @param message The actual message sent.
* @param isAction Set to `true` if the message is an action.
*/
@JvmStatic
fun storeRecap(sender: String, message: String, isAction: Boolean) {
recaps.add(
Utils.utcDateTime(LocalDateTime.now(Clock.systemUTC()))
+ " -> ${sender}: " + (if (isAction) " " else ": ") + message
)
if (recaps.size > 10) {
recaps.removeAt(0)
}
}
}
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (recaps.isNotEmpty()) {
for (r in recaps) {
bot.send(sender, r, isPrivate)
}
} else {
bot.send(sender, "Sorry, nothing to recap.", true)
}
}
}

View file

@ -0,0 +1,63 @@
/*
* Say.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
class Say : AbstractCommand() {
override val command = "say"
override val help = listOf(
Utils.bold("To have the bot say something on the channel:"),
Utils.helpIndent("/msg %s $command <text>")
)
override val isOp = true
override val isPublic = false
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (isOp) {
bot.send(bot.channel, args, true)
} else {
bot.helpDefault(sender, isOp)
}
}
}

View file

@ -0,0 +1,71 @@
/*
* Users.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import org.jibble.pircbot.User
import java.util.*
class Users : AbstractCommand() {
override val command = "users"
override val help = listOf(
Utils.bold("To list the users present on the channel:"),
Utils.helpIndent("/msg %s $command")
)
override val isOp = false
override val isPublic = true
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
val users: Array<User> = bot.getUsers(bot.channel)
val nicks = ArrayList<String>()
users.forEach { user ->
if (bot.isOp(user.nick)) {
nicks.add('@'.toString() + user.nick)
} else {
nicks.add(user.nick)
}
}
bot.send(sender, nicks.sorted().joinToString(" "), isPrivate)
}
}

View file

@ -0,0 +1,130 @@
/*
* Links.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands.links
import net.thauvin.erik.mobibot.Constants
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import net.thauvin.erik.mobibot.commands.AbstractCommand
import net.thauvin.erik.mobibot.entries.EntriesUtils
import net.thauvin.erik.mobibot.entries.EntryLink
class Comment : AbstractCommand() {
override val command = COMMAND
override val help = listOf(
Utils.bold("To add a comment:"),
Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"),
"I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1",
Utils.bold("To edit a comment, use its label: "),
Utils.helpIndent("${Constants.LINK_CMD}1.1:This is an edited comment"),
Utils.bold("To delete a comment, use its label and a minus sign: "),
Utils.helpIndent("${Constants.LINK_CMD}1.1:-")
)
override val isOp = false
override val isPublic = true
override val isVisible = true
companion object {
const val COMMAND = "comment"
}
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
val cmds = args.substring(1).split("[.:]".toRegex(), 3)
val index = cmds[0].toInt() - 1
if (index < UrlMgr.entriesCount) {
val entry: EntryLink = UrlMgr.getEntry(index)
val cindex = cmds[1].toInt() - 1
if (cindex < entry.commentsCount) {
val cmd = cmds[2].trim()
// L1.1:
if (cmd.isEmpty()) {
val comment = entry.getComment(cindex)
bot.send(bot.channel, EntriesUtils.buildComment(index, cindex, comment))
} else if ("-" == cmd) { // L11:-
entry.deleteComment(cindex)
bot.send(
bot.channel,
"Comment ${Constants.LINK_CMD}${index + 1}.${cindex + 1} removed."
)
UrlMgr.saveEntries(bot, false)
} else if (cmd[0] == '?') { // L1.1:?<author>
if (isOp) {
if (cmd.length > 1) {
val comment = entry.getComment(cindex)
comment.nick = cmd.substring(1)
bot.send(bot.channel, EntriesUtils.buildComment(index, cindex, comment))
UrlMgr.saveEntries(bot, false)
}
} else {
bot.send(sender, "Please ask a channel op to change the author of this comment for you.")
}
} else {
entry.setComment(cindex, cmd, sender)
val comment = entry.getComment(cindex)
bot.send(sender, EntriesUtils.buildComment(index, cindex, comment))
UrlMgr.saveEntries(bot, false)
}
}
}
}
override fun helpResponse(
bot: Mobibot,
command: String,
sender: String,
isOp: Boolean,
isPrivate: Boolean
): Boolean {
if (super.helpResponse(bot, command, sender, isOp, isPrivate)) {
if (isOp) {
bot.send(sender, Utils.bold("To change a comment's author:"), isPrivate)
bot.send(sender, Utils.helpIndent("/msg ${bot.nick} ${Constants.LINK_CMD}1.1:?<nick>"), isPrivate)
}
return true
}
return false
}
override fun matches(message: String): Boolean {
return message.matches("^${Constants.LINK_CMD}[0-9]+\\.[0-9]+:.*".toRegex())
}
}

View file

@ -0,0 +1,147 @@
/*
* Links.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands.links
import net.thauvin.erik.mobibot.Constants
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import net.thauvin.erik.mobibot.commands.AbstractCommand
import net.thauvin.erik.mobibot.entries.EntriesUtils
import net.thauvin.erik.mobibot.entries.EntryLink
class Posting : AbstractCommand() {
override val command = "posting"
override val help = listOf(
Utils.bold("Post a URL, by saying it on a line on its own:"),
Utils.helpIndent("<url> [<title>] ${Tags.COMMAND}}: <+tag> [...]]"),
"I will reply with a label, for example:" + Utils.bold("${Constants.LINK_CMD}1"),
Utils.bold("To add a title, use its label and a pipe:"),
Utils.helpIndent("${Constants.LINK_CMD}1:|This is the title"),
Utils.bold("To add a comment:"),
Utils.helpIndent("${Constants.LINK_CMD}1:This is a comment"),
"I will reply with a label, for example: ${Utils.bold(Constants.LINK_CMD)}1.1",
Utils.bold("To edit a comment, see: "),
Utils.helpIndent("/msg %s ${Constants.HELP_CMD} ${Comment.COMMAND}")
)
override val isOp = false
override val isPublic = true
override val isVisible = true
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
val cmds = args.substring(1).split(":", limit = 2)
val index = cmds[0].toInt() - 1
if (index < UrlMgr.entriesCount) {
val cmd = cmds[1].trim()
if (cmd.isEmpty()) {
val entry: EntryLink = UrlMgr.getEntry(index)
bot.send(bot.channel, EntriesUtils.buildLink(index, entry))
if (entry.hasTags()) {
bot.send(bot.channel, EntriesUtils.buildTags(index, entry))
}
if (entry.hasComments()) {
val comments = entry.comments
for (i in comments.indices) {
bot.send(bot.channel, EntriesUtils.buildComment(index, i, comments[i]))
}
}
} else {
// L1:-
if ("-" == cmd) {
val entry: EntryLink = UrlMgr.getEntry(index)
if (entry.login == login || isOp) {
bot.deletePin(entry)
if (bot.isTwitterAutoPost) {
bot.twitterRemoveEntry(index)
}
UrlMgr.removeEntry(index)
bot.send(bot.channel, "Entry ${Constants.LINK_CMD}${index + 1} removed.")
UrlMgr.saveEntries(bot, false)
} else {
bot.send(sender, "Please ask a channel op to remove this entry for you.")
}
} else if (cmd[0] == '|') { // L1:|<title>
if (cmd.length > 1) {
val entry: EntryLink = UrlMgr.getEntry(index)
entry.title = cmd.substring(1).trim()
bot.updatePin(entry.link, entry)
bot.send(bot.channel, EntriesUtils.buildLink(index, entry))
UrlMgr.saveEntries(bot, false)
}
} else if (cmd[0] == '=') { // L1:=<url>
val entry: EntryLink = UrlMgr.getEntry(index)
if (entry.login == login || isOp) {
val link = cmd.substring(1)
if (link.matches(UrlMgr.LINK_MATCH.toRegex())) {
val oldLink = entry.link
entry.link = link
bot.updatePin(oldLink, entry)
bot.send(bot.channel, EntriesUtils.buildLink(index, entry))
UrlMgr.saveEntries(bot, false)
}
} else {
bot.send(sender, "Please ask channel op to change this link for you.")
}
} else if (cmd[0] == '?') { // L1:?<author>
if (isOp) {
if (cmd.length > 1) {
val entry: EntryLink = UrlMgr.getEntry(index)
entry.nick = cmd.substring(1)
bot.send(bot.channel, EntriesUtils.buildLink(index, entry))
UrlMgr.saveEntries(bot, false)
}
} else {
bot.send(sender, "Please ask a channel op to change the author of this link for you.")
}
} else {
val entry: EntryLink = UrlMgr.getEntry(index)
val cindex = entry.addComment(cmd, sender)
val comment = entry.getComment(cindex)
bot.send(sender, EntriesUtils.buildComment(index, cindex, comment))
UrlMgr.saveEntries(bot, false)
}
}
}
}
override fun matches(message: String): Boolean {
return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex())
}
}

View file

@ -0,0 +1,92 @@
/*
* Links.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands.links
import net.thauvin.erik.mobibot.Constants
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import net.thauvin.erik.mobibot.commands.AbstractCommand
import net.thauvin.erik.mobibot.entries.EntriesUtils
import net.thauvin.erik.mobibot.entries.EntryLink
class Tags : AbstractCommand() {
override val command = COMMAND
override val help = listOf(
Utils.bold("To categorize or tag a URL, use its label and a T:"),
Utils.helpIndent("${Constants.LINK_CMD}1T:<+tag|-tag> [...]")
)
override val isOp = false
override val isPublic = true
override val isVisible = true
companion object {
const val COMMAND = "tags"
}
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
val cmds = args.substring(1).split("T:", limit = 2)
val index = cmds[0].toInt() - 1
if (index < UrlMgr.entriesCount) {
val cmd = cmds[1].trim()
val entry: EntryLink = UrlMgr.getEntry(index)
if (cmd.isNotEmpty()) {
if (entry.login == login || isOp) {
entry.setTags(cmd)
bot.updatePin(entry.link, entry)
bot.send(bot.channel, EntriesUtils.buildTags(index, entry))
UrlMgr.saveEntries(bot, false)
} else {
bot.send(sender, "Please ask a channel op to change the tags for you.")
}
} else {
if (entry.hasTags()) {
bot.send(bot.channel, EntriesUtils.buildTags(index, entry))
} else {
bot.send(sender, "The entry has no tags. Why don't add some?")
}
}
}
}
override fun matches(message: String): Boolean {
return message.matches("^${Constants.LINK_CMD}[0-9]+T:.*".toRegex())
}
}

View file

@ -0,0 +1,243 @@
/*
* Links.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands.links
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import net.thauvin.erik.mobibot.Constants
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.TwitterTimer
import net.thauvin.erik.mobibot.Utils
import net.thauvin.erik.mobibot.commands.AbstractCommand
import net.thauvin.erik.mobibot.commands.Ignore
import net.thauvin.erik.mobibot.entries.EntriesMgr
import net.thauvin.erik.mobibot.entries.EntriesUtils
import net.thauvin.erik.mobibot.entries.EntryLink
import org.jsoup.Jsoup
import java.io.IOException
import java.util.*
class UrlMgr(defaultTags: String, keywords: String) : AbstractCommand() {
private val tagsKeywords = ArrayList<String>()
override val command = Constants.LINK_CMD
override val help = emptyList<String>()
override val isOp = false
override val isPublic = false
override val isVisible = false
init {
tagsKeywords.addAll(keywords.split(", +?| +"))
Companion.defaultTags = defaultTags
}
companion object {
const val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"
// Entries array
private val entries = ArrayList<EntryLink>(0)
// History/backlogs array
private val history = ArrayList<String>(0)
private lateinit var defaultTags: String
@JvmStatic
val entriesCount
get() = entries.size
@JvmStatic
var today: String = Utils.today()
private set
@JvmStatic
fun addHistory(index: Int, entry: String) {
history.add(index, entry)
}
/**
* Saves the entries.
*
* @param isDayBackup Set the `true` if the daily backup file should also be created.
*/
@JvmStatic
fun saveEntries(bot: Mobibot, isDayBackup: Boolean) {
EntriesMgr.saveEntries(bot, entries, history, isDayBackup)
}
@JvmStatic
fun removeEntry(index: Int) {
entries.removeAt(index)
}
@JvmStatic
fun getEntry(index: Int): EntryLink {
return entries[index]
}
@JvmStatic
fun getHistory(): List<String> {
return history
}
@JvmStatic
fun startup(current: String, backlogs: String, channel: String) {
today = EntriesMgr.loadEntries(current, channel, entries)
if (Utils.today() != today) {
this.entries.clear()
today = Utils.today()
}
EntriesMgr.loadBacklogs(backlogs, history)
}
}
@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
val cmds = args.split(" ".toRegex(), 2)
if (Ignore.isNotIgnored(sender) && (cmds.size == 1
|| !cmds[1].contains(bot.nick)
|| !cmds[1].endsWith(" (${Ignore.IGNORE}"))) {
val link = cmds[0].trim()
var isBackup = false
val dupIndex: Int = findDupEntry(link)
if (dupIndex == -1) {
if (Utils.today() != today) {
isBackup = true
saveEntries(bot, true)
entries.clear()
today = Utils.today()
}
val tags: StringBuilder = StringBuilder(defaultTags)
var title = Constants.NO_TITLE
if (cmds.size == 2) {
val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2)
if (data.size == 1) {
title = data[0].trim()
} else {
if (data[0].isNotBlank()) {
title = data[0].trim()
}
tags.append(' ').append(data[1].trim())
}
}
if (Constants.NO_TITLE == title) {
try {
val html = Jsoup.connect(link).userAgent("Mozilla").get()
val htmlTitle = html.title()
if (htmlTitle.isNotBlank()) {
val split = htmlTitle.split("( \\| )".toRegex(), 2)
title = if (split.size == 2) {
split[0]
} else {
htmlTitle
}
}
} catch (ignore: IOException) {
// Do nothing
}
}
if (tagsKeywords.isNotEmpty()) {
for (match in tagsKeywords) {
val m = match.trim()
if (title.matches("(?i).*\\b$m\\b.*".toRegex())) {
tags.append(' ').append(m)
}
}
}
entries.add(EntryLink(link, title, sender, login, bot.channel, tags.toString()))
val index: Int = entries.size - 1
val entry: EntryLink = entries[index]
bot.send(bot.channel, EntriesUtils.buildLink(index, entry))
// Add Entry to pinboard.
bot.addPin(entry)
// Queue link for posting to twitter
if (bot.isTwitterAutoPost) {
bot.twitterAddEntry(index)
Mobibot.timer.schedule(
TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L
)
}
saveEntries(bot, isBackup)
if (Constants.NO_TITLE == entry.title) {
bot.send(sender, Utils.bold("Please specify a title, by typing:"), true)
bot.send(
sender,
Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title"),
true
)
}
} else {
val entry: EntryLink = entries[dupIndex]
bot.send(
sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry)
)
}
}
}
override fun helpResponse(
bot: Mobibot,
command: String,
sender: String,
isOp: Boolean,
isPrivate: Boolean
): Boolean = false
/**
* Returns the index of the specified duplicate entry, if any.
*
* @param link The link.
* @return The index or -1 if none.
*/
private fun findDupEntry(link: String): Int {
synchronized(entries) {
for (i in entries.indices) {
if (link == entries[i].link) {
return i
}
}
}
return -1
}
override fun matches(message: String): Boolean {
return message.matches(LINK_MATCH.toRegex())
}
}

View file

@ -0,0 +1,130 @@
/*
* Links.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.commands.links
import net.thauvin.erik.mobibot.Constants
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import net.thauvin.erik.mobibot.commands.AbstractCommand
import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.entriesCount
import net.thauvin.erik.mobibot.commands.links.UrlMgr.Companion.getEntry
import net.thauvin.erik.mobibot.entries.EntriesUtils
import net.thauvin.erik.mobibot.entries.EntryLink
class View : AbstractCommand() {
private val maxEntries = 8
override val command = VIEW_CMD
override val help = listOf(
Utils.bold("To list or search the current URL posts:"),
Utils.helpIndent("%s: $command [<start>] [<query>]")
)
override val isOp = false
override val isPublic = true
override val isVisible = true
companion object {
const val VIEW_CMD = "view"
}
override fun commandResponse(
bot: Mobibot,
sender: String,
login: String,
args: String,
isOp: Boolean,
isPrivate: Boolean
) {
if (entriesCount != 0) {
val max = entriesCount
var lcArgs = args.toLowerCase(Constants.LOCALE)
var i = 0
if (lcArgs.isEmpty() && max > maxEntries) {
i = max - maxEntries
}
if (lcArgs.matches("^\\d+(| .*)".toRegex())) {
val split = lcArgs.split(" ", limit = 2)
try {
i = split[0].toInt()
if (i > 0) {
i--
}
lcArgs = if (split.size == 2) {
split[1].trim()
} else {
""
}
if (i > max) {
i = 0
}
} catch (ignore: NumberFormatException) {
// Do nothing
}
}
var entry: EntryLink
var sent = 0
while (i < max) {
entry = getEntry(i)
if (lcArgs.isNotEmpty()) {
if (entry.link.toLowerCase().contains(lcArgs)
|| entry.title.toLowerCase().contains(lcArgs)
|| entry.nick.toLowerCase().contains(lcArgs)) {
if (sent > maxEntries) {
bot.send(
sender, "To view more, try: "
+ Utils.bold("${bot.nick}: $command ${i + 1} $lcArgs"),
isPrivate
)
break
}
bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate)
sent++
}
} else {
if (sent > maxEntries) {
bot.send(
sender,
"To view more, try: " + Utils.bold("${bot.nick}: $command ${i + 1}"),
isPrivate
)
break
}
bot.send(sender, EntriesUtils.buildLink(i, entry, true), isPrivate)
sent++
}
i++
}
} else {
bot.send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate)
}
}
}

View file

@ -30,12 +30,12 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.tell;
package net.thauvin.erik.mobibot.commands.tell;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.thauvin.erik.mobibot.Commands;
import net.thauvin.erik.mobibot.Mobibot;
import net.thauvin.erik.mobibot.Utils;
import net.thauvin.erik.mobibot.commands.links.View;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@ -112,16 +112,16 @@ public class Tell {
}
/**
* Responds with help.
* Responds with Constants.
*
* @param sender The sender.
*/
public void helpResponse(final String sender) {
bot.send(sender, "To send a message to someone when they join the channel:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>"));
bot.send(sender, "To view queued and sent messages:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + Commands.VIEW_CMD));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + View.VIEW_CMD));
bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."));
}
@ -141,19 +141,20 @@ public class Tell {
* @param sender The sender's nick.
* @param cmds The commands string.
*/
@SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.")
@SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY",
justification = "Working on it.")
public void response(final String sender, final String cmds) {
final String arrow = " --> ";
if (StringUtils.isBlank(cmds)) {
helpResponse(sender);
} else if (cmds.startsWith(Commands.VIEW_CMD)) {
if (bot.isOp(sender) && (Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) {
} else if (cmds.startsWith(View.VIEW_CMD)) {
if (bot.isOp(sender) && (View.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) {
if (!messages.isEmpty()) {
for (final TellMessage message : messages) {
bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient())
+ " [ID: " + message.getId() + ", "
+ (message.isReceived() ? "DELIVERED" : "QUEUED") + ']',
true);
+ " [ID: " + message.getId() + ", "
+ (message.isReceived() ? "DELIVERED" : "QUEUED") + ']',
true);
}
} else {
bot.send(sender, "There are no messages in the queue.", true);
@ -170,20 +171,20 @@ public class Tell {
if (message.isReceived()) {
bot.send(sender,
Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient())
+ " [" + Utils.utcDateTime(message.getReceived()) + ", ID: "
+ message.getId() + ", DELIVERED]",
true);
Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient())
+ " [" + Utils.utcDateTime(message.getReceived()) + ", ID: "
+ message.getId() + ", DELIVERED]",
true);
} else {
bot.send(sender,
Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient())
+ " [" + Utils.utcDateTime(message.getQueued()) + ", ID: "
+ message.getId() + ", QUEUED]",
true);
Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient())
+ " [" + Utils.utcDateTime(message.getQueued()) + ", ID: "
+ message.getId() + ", QUEUED]",
true);
}
bot.send(sender, bot.helpIndent(message.getMessage(), false), true);
bot.send(sender, Utils.helpIndent(message.getMessage()), true);
}
}
@ -192,10 +193,10 @@ public class Tell {
} else {
bot.send(sender, "To delete one or all delivered messages:");
bot.send(sender,
bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|"
+ TELL_ALL_KEYWORD + '>'));
Utils.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|"
+ TELL_ALL_KEYWORD + '>'));
bot.send(sender, "Messages are kept for " + Utils.bold(maxDays)
+ Utils.plural(maxDays, " day.", " days."));
+ Utils.plural(maxDays, " day.", " days."));
}
}
} else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) {
@ -259,7 +260,7 @@ public class Tell {
save();
bot.send(sender, "Message [ID " + message.getId() + "] was queued for "
+ Utils.bold(message.getRecipient()), true);
+ Utils.bold(message.getRecipient()), true);
} else {
bot.send(sender, "Sorry, the messages queue is currently full.", true);
}
@ -289,43 +290,42 @@ public class Tell {
*/
public void send(final String nickname, final boolean isMessage) {
if (!nickname.equals(bot.getNick()) && isEnabled()) {
messages.stream().filter(message -> message.isMatch(nickname)).forEach(
message -> {
if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) {
if (message.getSender().equals(nickname)) {
if (!isMessage) {
bot.send(nickname, Utils.bold("You") + " wanted me to remind you: "
+ Utils.reverseColor(message.getMessage()),
true);
message.setIsReceived();
message.setIsNotified();
save();
}
} else {
bot.send(nickname, message.getSender() + " wanted me to tell you: "
+ Utils.reverseColor(message.getMessage()),
true);
messages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> {
if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) {
if (message.getSender().equals(nickname)) {
if (!isMessage) {
bot.send(nickname, Utils.bold("You") + " wanted me to remind you: "
+ Utils.reverseColor(message.getMessage()),
true);
message.setIsReceived();
message.setIsNotified();
save();
}
} else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived()
&& !message.isNotified()) {
bot.send(nickname,
"Your message "
+ Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to "
+ Utils.bold(message.getRecipient()) + " on "
+ Utils.utcDateTime(message.getReceived()),
true);
} else {
bot.send(nickname, message.getSender() + " wanted me to tell you: "
+ Utils.reverseColor(message.getMessage()),
true);
message.setIsNotified();
message.setIsReceived();
save();
}
});
} else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived()
&& !message.isNotified()) {
bot.send(nickname,
"Your message "
+ Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to "
+ Utils.bold(message.getRecipient()) + " on "
+ Utils.utcDateTime(message.getReceived()),
true);
message.setIsNotified();
save();
}
});
}
}

View file

@ -30,7 +30,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.tell;
package net.thauvin.erik.mobibot.commands.tell;
import java.io.Serializable;
import java.time.Clock;

View file

@ -30,7 +30,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.tell;
package net.thauvin.erik.mobibot.commands.tell;
import org.apache.logging.log4j.Logger;

View file

@ -33,7 +33,6 @@
package net.thauvin.erik.mobibot.entries;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.thauvin.erik.mobibot.Commands;
import net.thauvin.erik.mobibot.Constants;
import net.thauvin.erik.mobibot.Utils;
@ -61,8 +60,8 @@ public final class EntriesUtils {
* @return The entry's comment.
*/
public static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) {
return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] "
+ comment.getComment());
return (Constants.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] "
+ comment.getComment());
}
/**
@ -87,8 +86,8 @@ public final class EntriesUtils {
*/
@SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.")
public static String buildLink(final int index, final EntryLink entry, final boolean isView) {
final StringBuilder buff = new StringBuilder().append(Commands.LINK_CMD).append(index + 1)
.append(": ").append('[').append(entry.getNick()).append(']');
final StringBuilder buff = new StringBuilder().append(Constants.LINK_CMD).append(index + 1)
.append(": ").append('[').append(entry.getNick()).append(']');
if (isView && entry.hasComments()) {
buff.append("[+").append(entry.getCommentsCount()).append(']');
@ -115,6 +114,6 @@ public final class EntriesUtils {
* @return The entry's tags.
*/
public static String buildTags(final int entryIndex, final EntryLink entry) {
return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replace(",", ", "));
return (Constants.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replace(",", ", "));
}
}

View file

@ -95,7 +95,7 @@ public abstract class AbstractModule {
}
/**
* Responds with the module's help.
* Responds with the module's Constants.
*
* @param bot The bot's instance.
* @param sender The sender.

View file

@ -35,10 +35,13 @@ package net.thauvin.erik.mobibot.modules;
import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import net.thauvin.erik.mobibot.Mobibot;
import net.thauvin.erik.mobibot.Utils;
import org.apache.commons.lang3.StringUtils;
import java.text.DecimalFormat;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The Calc module.
*
@ -88,7 +91,6 @@ public class Calc extends AbstractModule {
final boolean isPrivate) {
if (StringUtils.isNotBlank(args)) {
bot.send(calc(args));
} else {
helpResponse(bot, sender, args, isPrivate);
}
@ -99,7 +101,7 @@ public class Calc extends AbstractModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To solve a mathematical calculation:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>"));
bot.send(sender, bold("To solve a mathematical calculation:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>"));
}
}

View file

@ -56,6 +56,8 @@ import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The CurrentConverter module.
*
@ -228,14 +230,14 @@ public final class CurrencyConverter extends ThreadedModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To convert from one currency to another:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]"));
bot.send(sender, bold("To convert from one currency to another:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]"));
if (args.endsWith(CURRENCY_CMD)) {
bot.send(sender, "For a listing of currency rates:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD);
bot.send(sender, "For a listing of supported currencies:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD));
bot.send(sender, bold("For a listing of currency rates:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD);
bot.send(sender, bold("For a listing of supported currencies:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + CURRENCY_CMD));
}
}
}

View file

@ -37,6 +37,8 @@ import net.thauvin.erik.mobibot.Utils;
import java.security.SecureRandom;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The Dice module.
*
@ -72,15 +74,15 @@ public final class Dice extends AbstractModule {
final int playerTotal = i + y;
bot.send(bot.getChannel(),
sender + " rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of "
+ Utils.bold(playerTotal));
sender + " rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of "
+ bold(playerTotal));
i = r.nextInt(6) + 1;
y = r.nextInt(6) + 1;
final int total = i + y;
bot.action(
"rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total));
"rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " + bold(total));
if (playerTotal < total) {
bot.action("wins.");
@ -96,7 +98,7 @@ public final class Dice extends AbstractModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To roll the dice:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + DICE_CMD));
bot.send(sender, bold("To roll the dice:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + DICE_CMD));
}
}

View file

@ -52,6 +52,8 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The GoogleSearch module.
*
@ -79,14 +81,74 @@ public final class GoogleSearch extends ThreadedModule {
properties.put(GOOGLE_CSE_KEY_PROP, "");
}
/**
* Performs a search on Google.
*
* @param query The search query.
* @param apiKey The Google API key.
* @param cseKey The Google CSE key.
* @return The {@link Message} array containing the search results.
* @throws ModuleException If an error occurs while searching.
*/
@SuppressFBWarnings({ "URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION" })
@SuppressWarnings(("PMD.AvoidInstantiatingObjectsInLoops"))
static List<Message> searchGoogle(final String query, final String apiKey, final String cseKey)
throws ModuleException {
if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) {
throw new ModuleException(StringUtils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing.");
}
if (StringUtils.isNotBlank(query)) {
final ArrayList<Message> results = new ArrayList<>();
try {
final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString());
final URL url =
new URL("https://www.googleapis.com/customsearch/v1?key="
+ apiKey
+ "&cx="
+ cseKey
+ "&q="
+ q
+ "&filter=1&num=5&alt=json");
final URLConnection conn = url.openConnection();
final StringBuilder sb = new StringBuilder();
try (final BufferedReader reader =
new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
final JSONObject json = new JSONObject(sb.toString());
final JSONArray ja = json.getJSONArray("items");
for (int i = 0; i < ja.length(); i++) {
final JSONObject j = ja.getJSONObject(i);
results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title"))));
results.add(
new NoticeMessage(TAB_INDENT + j.getString("link"), Colors.DARK_GREEN));
}
}
} catch (IOException e) {
throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e);
}
return results;
} else {
throw new ModuleException("Invalid query.");
}
}
/**
* {@inheritDoc}
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
if (isEnabled()) {
bot.send(sender, "To search Google:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>"));
bot.send(sender, bold("To search Google:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>"));
} else {
bot.send(sender, "The Google search module is disabled.");
}
@ -100,7 +162,7 @@ public final class GoogleSearch extends ThreadedModule {
if (StringUtils.isNotBlank(query)) {
try {
final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP),
properties.get(GOOGLE_CSE_KEY_PROP));
properties.get(GOOGLE_CSE_KEY_PROP));
for (final Message msg : results) {
bot.send(sender, msg);
}
@ -112,64 +174,4 @@ public final class GoogleSearch extends ThreadedModule {
helpResponse(bot, sender, query, true);
}
}
/**
* Performs a search on Google.
*
* @param query The search query.
* @param apiKey The Google API key.
* @param cseKey The Google CSE key.
* @return The {@link Message} array containing the search results.
* @throws ModuleException If an error occurs while searching.
*/
@SuppressFBWarnings({"URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION"})
@SuppressWarnings(("PMD.AvoidInstantiatingObjectsInLoops"))
static List<Message> searchGoogle(final String query, final String apiKey, final String cseKey)
throws ModuleException {
if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) {
throw new ModuleException(StringUtils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing.");
}
if (StringUtils.isNotBlank(query)) {
final ArrayList<Message> results = new ArrayList<>();
try {
final String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString());
final URL url =
new URL("https://www.googleapis.com/customsearch/v1?key="
+ apiKey
+ "&cx="
+ cseKey
+ "&q="
+ q
+ "&filter=1&num=5&alt=json");
final URLConnection conn = url.openConnection();
final StringBuilder sb = new StringBuilder();
try (final BufferedReader reader =
new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
final JSONObject json = new JSONObject(sb.toString());
final JSONArray ja = json.getJSONArray("items");
for (int i = 0; i < ja.length(); i++) {
final JSONObject j = ja.getJSONObject(i);
results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title"))));
results.add(
new NoticeMessage(TAB_INDENT + j.getString("link"), Colors.DARK_GREEN));
}
}
} catch (IOException e) {
throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e);
}
return results;
} else {
throw new ModuleException("Invalid query.");
}
}
}

View file

@ -44,6 +44,8 @@ import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The Joke module.
*
@ -126,7 +128,7 @@ public final class Joke extends ThreadedModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To retrieve a random joke:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + JOKE_CMD));
bot.send(sender, bold("To retrieve a random joke:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + JOKE_CMD));
}
}

View file

@ -34,12 +34,15 @@ package net.thauvin.erik.mobibot.modules;
import net.thauvin.erik.mobibot.Constants;
import net.thauvin.erik.mobibot.Mobibot;
import net.thauvin.erik.mobibot.Utils;
import org.apache.commons.net.whois.WhoisClient;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The Lookup module.
*
@ -195,7 +198,7 @@ public final class Lookup extends AbstractModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To perform a DNS lookup query:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>"));
bot.send(sender, bold("To perform a DNS lookup query:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + LOOKUP_CMD + " <ip address or hostname>"));
}
}

View file

@ -33,11 +33,14 @@
package net.thauvin.erik.mobibot.modules;
import net.thauvin.erik.mobibot.Mobibot;
import net.thauvin.erik.mobibot.Utils;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The Ping module.
*
@ -94,7 +97,7 @@ public class Ping extends AbstractModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To ping the bot:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + PING_CMD));
bot.send(sender, bold("To ping the bot:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + PING_CMD));
}
}

View file

@ -33,7 +33,10 @@
package net.thauvin.erik.mobibot.modules
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import net.thauvin.erik.mobibot.Utils.bold
import net.thauvin.erik.mobibot.Utils.green
import net.thauvin.erik.mobibot.Utils.helpIndent
import net.thauvin.erik.mobibot.Utils.red
import kotlin.random.Random
@ -71,7 +74,7 @@ class RockPaperScissors : AbstractModule() {
companion object {
// For testing.
fun winLoseOrDraw(player: String, bot: String ): String {
fun winLoseOrDraw(player: String, bot: String): String {
val hand = Hands.valueOf(player.toUpperCase())
val botHand = Hands.valueOf(bot.toUpperCase())
@ -88,26 +91,26 @@ class RockPaperScissors : AbstractModule() {
val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)]
when {
hand == botHand -> {
bot.action("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)} ~ The game is tied ~")
bot.action("${green(hand.name)} vs. ${green(botHand.name)} ~ The game is tied ~")
}
hand.beats(botHand) -> {
bot.action(
"${Utils.green(hand.name)} ${Utils.bold(hand.action)} ${Utils.red(botHand.name)} ~ You win ~"
"${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)} ~ You win ~"
)
}
else -> {
bot.action(
"${Utils.green(botHand.name)} ${Utils.bold(botHand.action)} ${Utils.red(botHand.name)} ~ You lose ~"
"${green(botHand.name)} ${bold(botHand.action)} ${red(botHand.name)} ~ You lose ~"
)
}
}
}
override fun helpResponse(bot: Mobibot, sender: String, args: String?, isPrivate: Boolean) {
bot.send(sender, "To play Rock Paper Scissors:")
bot.send(sender, bold("To play Rock Paper Scissors:"))
bot.send(
sender,
bot.helpIndent(
helpIndent(
"${bot.nick}: ${Hands.ROCK.name.toLowerCase()} or ${Hands.PAPER.name.toLowerCase()}"
+ " or ${Hands.SCISSORS.name.toLowerCase()}"
)

View file

@ -52,6 +52,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The StockQuote module.
*
@ -82,9 +84,10 @@ public final class StockQuote extends ThreadedModule {
properties.put(ALPHAVANTAGE_API_KEY_PROP, "");
}
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?")
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE",
justification = "false positive?")
private static JSONObject getJsonResponse(final Response response, final String debugMessage)
throws IOException, ModuleException {
throws IOException, ModuleException {
if (response.isSuccessful()) {
if (response.body() != null) {
final JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string());
@ -132,7 +135,7 @@ public final class StockQuote extends ThreadedModule {
* @return The {@link Message} array containing the stock quote.
* @throws ModuleException If an errors occurs.
*/
@SuppressWarnings({"PMD.CloseResource"})
@SuppressWarnings({ "PMD.CloseResource" })
static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException {
if (StringUtils.isBlank(apiKey)) {
throw new ModuleException(StringUtils.capitalize(STOCK_CMD) + " is disabled. The API key is missing.");
@ -146,7 +149,7 @@ public final class StockQuote extends ThreadedModule {
try {
// Search for symbol/keywords
Request request = new Request.Builder().url(
ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build();
ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build();
Response response = client.newCall(request).execute();
JSONObject json = getJsonResponse(response, debugMessage);
@ -161,8 +164,8 @@ public final class StockQuote extends ThreadedModule {
// Get quote for symbol
request = new Request.Builder().url(
ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey="
+ apiKey).build();
ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey="
+ apiKey).build();
response = client.newCall(request).execute();
json = getJsonResponse(response, debugMessage);
@ -175,20 +178,21 @@ public final class StockQuote extends ThreadedModule {
}
messages.add(new PublicMessage(
"Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils
.unescapeXml(symbolInfo.getString("2. name") + ']')));
"Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils.unescapeXml(
symbolInfo.getString("2. name") + ']')));
messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price"))));
messages.add(
new PublicMessage(" Previous: " + Utils.unescapeXml(quote.getString("08. previous close"))));
messages.add(new PublicMessage(
" Previous: "
+ Utils.unescapeXml(quote.getString("08. previous close"))));
messages.add(new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open"))));
messages.add(new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high"))));
messages.add(new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low"))));
messages.add(new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume"))));
messages.add(new NoticeMessage(
" Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day"))));
" Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day"))));
messages.add(new NoticeMessage(
" Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils
.unescapeXml(quote.getString("10. change percent")) + ']'));
" Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils.unescapeXml(
quote.getString("10. change percent")) + ']'));
} catch (IOException | NullPointerException e) {
throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e);
}
@ -203,8 +207,8 @@ public final class StockQuote extends ThreadedModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To retrieve a stock quote:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>"));
bot.send(sender, bold("To retrieve a stock quote:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>"));
}
/**

View file

@ -33,6 +33,7 @@
package net.thauvin.erik.mobibot.modules;
import net.thauvin.erik.mobibot.Mobibot;
import net.thauvin.erik.mobibot.Utils;
import net.thauvin.erik.mobibot.msg.Message;
import net.thauvin.erik.mobibot.msg.NoticeMessage;
import twitter4j.DirectMessage;
@ -40,6 +41,8 @@ import twitter4j.Status;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The Twitter module.
*
@ -115,8 +118,8 @@ public final class Twitter extends ThreadedModule {
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
if (isEnabled()) {
bot.send(sender, "To post to Twitter:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>"));
bot.send(sender, bold("To post to Twitter:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>"));
} else {
bot.send(sender, "The Twitter posting facility is disabled.");
}

View file

@ -37,6 +37,8 @@ import net.thauvin.erik.mobibot.Utils;
import java.security.SecureRandom;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The War module.
*
@ -80,14 +82,14 @@ public final class War extends AbstractModule {
y = r.nextInt(WAR_DECK.length);
bot.send(bot.getChannel(),
sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]);
bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]);
sender + " drew the " + bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]);
bot.action("drew the " + bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]);
if (i != y) {
break;
}
bot.send("This means " + Utils.bold("WAR") + '!');
bot.send("This means " + bold("WAR") + '!');
}
if (i < y) {
@ -102,7 +104,7 @@ public final class War extends AbstractModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To play war:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WAR_CMD));
bot.send(sender, bold("To play war:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WAR_CMD));
}
}

View file

@ -52,6 +52,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The <code>Weather2</code> module.
*
@ -177,7 +179,7 @@ public class Weather2 extends ThreadedModule {
Colors.GREEN));
} else {
final HttpUrl url = Objects.requireNonNull(HttpUrl.parse(
"https://openweathermap.org/find"))
"https://openweathermap.org/find"))
.newBuilder()
.addQueryParameter("q",
city + ',' + country)
@ -199,17 +201,22 @@ public class Weather2 extends ThreadedModule {
return messages;
}
private static String wind(final Double w) {
final double kmh = w * 1.60934;
return Math.round(w) + " mph, " + Math.round(kmh) + " km/h";
}
/**
* {@inheritDoc}
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To display weather information:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]"));
bot.send(sender, bold("To display weather information:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]"));
bot.send(sender, "For example:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr"));
bot.send(sender,
"The default ISO 3166 country code is " + Utils.bold("US")
"The default ISO 3166 country code is " + bold("US")
+ ". Zip codes are supported in most countries.");
}
@ -236,9 +243,4 @@ public class Weather2 extends ThreadedModule {
helpResponse(bot, sender, args, true);
}
}
private static String wind(final Double w) {
final double kmh = w * 1.60934;
return Math.round(w) + " mph, " + Math.round(kmh) + " km/h";
}
}

View file

@ -35,6 +35,7 @@ package net.thauvin.erik.mobibot.modules;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.thauvin.erik.mobibot.Constants;
import net.thauvin.erik.mobibot.Mobibot;
import net.thauvin.erik.mobibot.Utils;
import net.thauvin.erik.mobibot.msg.ErrorMessage;
import net.thauvin.erik.mobibot.msg.Message;
import net.thauvin.erik.mobibot.msg.PublicMessage;
@ -47,6 +48,8 @@ import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import static net.thauvin.erik.mobibot.Utils.bold;
/**
* The WorldTime module.
*
@ -232,11 +235,11 @@ public final class WorldTime extends AbstractModule {
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To display a country's current date/time:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]");
bot.send(sender, bold("To display a country's current date/time:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]");
bot.send(sender, "For a listing of the supported countries:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD));
bot.send(sender, bold("For a listing of the supported countries:"));
bot.send(sender, Utils.helpIndent(bot.getNick() + ": " + TIME_CMD));
}
/**

View file

@ -30,7 +30,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot.tell;
package net.thauvin.erik.mobibot.commands.tell;
import org.testng.annotations.Test;

View file

@ -1,9 +1,9 @@
#Generated by the Semver Plugin for Gradle
#Mon Mar 23 23:04:40 PDT 2020
version.buildmeta=714
#Sat Mar 28 10:22:37 PDT 2020
version.buildmeta=045
version.major=0
version.minor=7
version.patch=3
version.prerelease=beta
version.minor=8
version.patch=0
version.prerelease=alpha
version.project=mobibot
version.semver=0.7.3-beta+714
version.semver=0.8.0-alpha+045