@@ -56,29 +56,29 @@
-
+
-
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
+
+
@@ -89,7 +89,7 @@
-
+
diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml
index 93d9e37..ed4ec34 100644
--- a/config/detekt/baseline.xml
+++ b/config/detekt/baseline.xml
@@ -2,18 +2,64 @@
+ ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message>
+ LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message>
+ LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)
+ LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )MagicNumber:Comment.kt$Comment$3
+ MagicNumber:CurrencyConverter.kt$CurrencyConverter$11
+ MagicNumber:CurrencyConverter.kt$CurrencyConverter$3
+ MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3
+ MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$33
+ MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4
+ MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8MagicNumber:Cycle.kt$Cycle$10
+ MagicNumber:Dice.kt$Dice$7MagicNumber:Ignore.kt$Ignore$8MagicNumber:Modules.kt$Modules$7MagicNumber:Recap.kt$Recap.Companion$10
+ MagicNumber:Twitter.kt$Twitter$1000L
+ MagicNumber:Twitter.kt$Twitter$60LMagicNumber:Users.kt$Users$8
+ MagicNumber:Utils.kt$Utils.Companion$30
+ MagicNumber:Utils.kt$Utils.Companion$365
+ MagicNumber:Utils.kt$Utils.Companion$7MagicNumber:View.kt$View$8
+ MagicNumber:Weather2.kt$Weather2.Companion$1.60934
+ MagicNumber:Weather2.kt$Weather2.Companion$32
+ MagicNumber:Weather2.kt$Weather2.Companion$5
+ MagicNumber:Weather2.kt$Weather2.Companion$9
+ MagicNumber:WorldTime.kt$WorldTime$17
+ MagicNumber:WorldTime.kt$WorldTime.Companion$3
+ MagicNumber:WorldTime.kt$WorldTime.Companion$3600
+ MagicNumber:WorldTime.kt$WorldTime.Companion$60
+ MagicNumber:WorldTime.kt$WorldTime.Companion$86.4
+ MemberNameEqualsClassName:Calc.kt$Calc.Companion$ @JvmStatic fun calc(query: String): String
+ MemberNameEqualsClassName:Lookup.kt$Lookup.Companion$ @JvmStatic @Throws(UnknownHostException::class) fun lookup(query: String): String
+ MemberNameEqualsClassName:WorldTime.kt$WorldTime.Companion$ @JvmStatic fun worldTime(query: String): MessageNestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )
+ NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message
+ NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()
+ NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )
+ NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )
+ NestedBlockDepth:StockQuote.kt$StockQuote$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)
+ NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message>
+ NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)
+ NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message>
+ NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )
+ ReturnCount:Utils.kt$Utils.Companion$ @JvmStatic fun colorize(s: String?, color: String): String
+ ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message>
+ ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message>
+ ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject
+ ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message>
+ TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception
+ TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException
+ TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException
+ TooManyFunctions:Utils.kt$Utils$Companion
diff --git a/src/main/java/net/thauvin/erik/mobibot/Constants.java b/src/main/java/net/thauvin/erik/mobibot/Constants.kt
similarity index 65%
rename from src/main/java/net/thauvin/erik/mobibot/Constants.java
rename to src/main/java/net/thauvin/erik/mobibot/Constants.kt
index ee86294..4f3d1ae 100644
--- a/src/main/java/net/thauvin/erik/mobibot/Constants.java
+++ b/src/main/java/net/thauvin/erik/mobibot/Constants.kt
@@ -1,5 +1,5 @@
/*
- * Constants.java
+ * Constants.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -29,81 +29,81 @@
* 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
-package net.thauvin.erik.mobibot;
-
-import java.util.Locale;
+import java.util.*
/**
- * The Constants class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-19
- * @since 1.0
+ * The `Constants` class.
*/
-public final class Constants {
+object Constants {
/**
* The connect/read timeout in ms.
*/
- public static final int CONNECT_TIMEOUT = 5000;
+ const val CONNECT_TIMEOUT = 5000
+
/**
* Debug command line argument.
*/
- public static final String DEBUG_ARG = "debug";
+ const val DEBUG_ARG = "debug"
+
/**
* The debug command.
*/
- public static final String DEBUG_CMD = "debug";
+ const val DEBUG_CMD = "debug"
+
/**
* Default IRC Port.
*/
- public static final int DEFAULT_PORT = 6667;
+ const val DEFAULT_PORT = 6667
+
/**
* Default IRC Server.
*/
- public static final String DEFAULT_SERVER = "irc.freenode.net";
+ const val DEFAULT_SERVER = "irc.freenode.net"
+
/**
* The die command.
*/
- public static final String DIE_CMD = "die";
+ const val DIE_CMD = "die"
+
/**
* Help command line argument.
*/
- public static final String HELP_ARG = "help";
+ const val HELP_ARG = "help"
+
/**
* The help command.
*/
- public static final String HELP_CMD = "help";
+ const val HELP_CMD = "help"
+
/**
* The link command.
*/
- public static final String LINK_CMD = "L";
+ const val LINK_CMD = "L"
+
/**
* Default locale.
*/
- public static final Locale LOCALE = Locale.getDefault();
+ val LOCALE: Locale = Locale.getDefault()
+
/**
* The empty title string.
*/
- public static final String NO_TITLE = "No Title";
+ const val NO_TITLE = "No Title"
+
/**
* Properties command line argument.
*/
- public static final String PROPS_ARG = "properties";
+ const val PROPS_ARG = "properties"
+
/**
* The timer delay in minutes.
*/
- public static final long TIMER_DELAY = 10L;
+ const val TIMER_DELAY = 10L
/**
* Properties version line argument.
*/
- public static final String VERSION_ARG = "version";
-
- /**
- * Disables the default constructor.
- */
- private Constants() {
- throw new UnsupportedOperationException("Illegal constructor call.");
- }
+ const val VERSION_ARG = "version"
}
diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java b/src/main/java/net/thauvin/erik/mobibot/FeedReader.java
deleted file mode 100644
index 41795f3..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/FeedReader.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * FeedReader.java
- *
- * 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;
-
-import com.rometools.rome.feed.synd.SyndEntry;
-import com.rometools.rome.feed.synd.SyndFeed;
-import com.rometools.rome.io.SyndFeedInput;
-import com.rometools.rome.io.XmlReader;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-/**
- * Reads a RSS feed.
- *
- * @author Erik C. Thauvin
- * @created Feb 1, 2004
- * @since 1.0
- */
-public class FeedReader implements Runnable {
- // Maximum number of feed items to display
- private static final int MAX_ITEMS = 5;
-
- // Bot
- private final Mobibot bot;
-
- // Nick of the person who sent the message
- private final String sender;
-
- // URL to fetch
- private final String url;
-
- /**
- * Creates a new {@link FeedReader} instance.
- *
- * @param bot The bot's instance.
- * @param sender The nick of the person who sent the message.
- * @param url The URL to fetch.
- */
- public FeedReader(final Mobibot bot, final String sender, final String url) {
- this.bot = bot;
- this.sender = sender;
- this.url = url;
- }
-
- /**
- * Fetches the Feed's items.
- */
- @Override
- public final void run() {
- try {
- final SyndFeedInput input = new SyndFeedInput();
- try (final XmlReader reader = new XmlReader(new URL(url))) {
- final SyndFeed feed = input.build(reader);
-
- final List items = feed.getEntries();
- if (items.isEmpty()) {
- bot.send(sender, "There is currently nothing to view.", false);
- } else {
- SyndEntry item;
- for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) {
- item = items.get(i);
- bot.send(sender, item.getTitle(), false);
- bot.send(sender, Utils.helpIndent(Utils.green(item.getLink()), false), false);
- }
- }
- }
- } catch (MalformedURLException e) {
- bot.getLogger().debug("Invalid feed URL.", e);
- bot.send(sender, "The feed URL is invalid.", false);
- } catch (Exception e) {
- bot.getLogger().debug("Unable to fetch the feed.", e);
- bot.send(sender, "An error has occurred while fetching the feed: " + e.getMessage(), false);
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt
new file mode 100644
index 0000000..0dcdbac
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/FeedReader.kt
@@ -0,0 +1,85 @@
+/*
+ * FeedReader.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
+
+import com.rometools.rome.io.SyndFeedInput
+import com.rometools.rome.io.XmlReader
+import java.net.MalformedURLException
+import java.net.URL
+
+/**
+ * Reads a RSS feed.
+ */
+class FeedReader(
+ // Bot
+ private val bot: Mobibot,
+ // Nick of the person who sent the message
+ private val sender: String,
+ // URL to fetch
+ private val url: String
+) : Runnable {
+ /**
+ * Fetches the Feed's items.
+ */
+ override fun run() {
+ with(bot) {
+ try {
+ val input = SyndFeedInput()
+ XmlReader(URL(url)).use { reader ->
+ val feed = input.build(reader)
+ val items = feed.entries
+ if (items.isEmpty()) {
+ send(sender, "There is currently nothing to view.", false)
+ } else {
+ var i = 0
+ while (i < items.size && i < MAX_ITEMS) {
+ send(sender, items[i].title, false)
+ send(sender, Utils.helpIndent(Utils.green(items[i].link), false), false)
+ i++
+ }
+ }
+ }
+ } catch (e: MalformedURLException) {
+ logger.debug("Invalid feed URL.", e)
+ send(sender, "The feed URL is invalid.", false)
+ } catch (e: Exception) {
+ logger.debug("Unable to fetch the feed.", e)
+ send(sender, "An error has occurred while fetching the feed: ${e.message}", false)
+ }
+ }
+ }
+
+ companion object {
+ // Maximum number of feed items to display
+ private const val MAX_ITEMS = 5
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java
index 1289405..63ec7cf 100644
--- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.java
+++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.java
@@ -579,7 +579,7 @@ public class Mobibot extends PircBot {
*/
private boolean helpModules(final String sender, final String topic, final boolean isPrivate) {
for (final AbstractModule module : addons.getModules()) {
- for (final String cmd : module.getCommands()) {
+ for (final String cmd : module.commands) {
if (topic.equals(cmd)) {
module.helpResponse(sender, isPrivate);
return true;
@@ -646,7 +646,7 @@ public class Mobibot extends PircBot {
*/
public final void joinChannel() {
joinChannel(ircChannel);
- twitter.notification("%1$s %2$s has joined %3$s");
+ twitter.notification(getName() + " " + ReleaseInfo.VERSION + " has joined " + getChannel());
}
/**
@@ -695,7 +695,7 @@ public class Mobibot extends PircBot {
}
// Modules
for (final AbstractModule module : addons.getModules()) { // modules
- for (final String c : module.getCommands()) {
+ for (final String c : module.commands) {
if (cmd.startsWith(c)) {
module.commandResponse(sender, cmd, args, false);
return;
@@ -739,7 +739,7 @@ public class Mobibot extends PircBot {
if (cmd.startsWith(Constants.HELP_CMD)) { // help
helpResponse(sender, args, true);
} else if (isOp && "kill".equals(cmd)) { // kill
- twitter.notification("%1$s killed by " + sender + " on %3$s");
+ twitter.notification(getName() + " killed by " + sender + " on " + getChannel());
sendRawLine("QUIT : Poof!");
System.exit(0);
} else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug
@@ -753,7 +753,7 @@ public class Mobibot extends PircBot {
send(sender + " has just signed my death sentence.");
TIMER.cancel();
twitter.shutdown();
- twitter.notification("%1$s stopped by " + sender + " on %3$s");
+ twitter.notification(getName() + " stopped by " + sender + " on " + getChannel());
sleep(3);
quitServer("The Bot Is Out There!");
System.exit(0);
@@ -766,7 +766,7 @@ public class Mobibot extends PircBot {
}
for (final AbstractModule module : addons.getModules()) {
if (module.isPrivateMsgEnabled()) {
- for (final String c : module.getCommands()) {
+ for (final String c : module.commands) {
if (cmd.equals(c)) {
module.commandResponse(sender, cmd, args, true);
return;
diff --git a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java
index 7d807d0..dbc7cb9 100644
--- a/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java
+++ b/src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java
@@ -69,8 +69,8 @@ public final class TwitterOAuth {
* @throws TwitterException If an error occurs.
* @throws IOException If an IO error occurs.
*/
- @SuppressFBWarnings({"DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE"})
- @SuppressWarnings({"PMD.AvoidPrintStackTrace", "PMD.SystemPrintln"})
+ @SuppressFBWarnings({ "DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE" })
+ @SuppressWarnings({ "PMD.AvoidPrintStackTrace", "PMD.SystemPrintln" })
public static void main(final String[] args) throws TwitterException, IOException {
if (args.length == 2) {
final twitter4j.Twitter twitter = new TwitterFactory().getInstance();
@@ -91,9 +91,10 @@ public final class TwitterOAuth {
}
System.out.println(
- "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey="
- + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token="
- + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken.getTokenSecret());
+ "Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey="
+ + args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token="
+ + accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken
+ .getTokenSecret());
} catch (TwitterException te) {
if (401 == te.getStatusCode()) {
System.out.println("Unable to get the access token.");
diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.java b/src/main/java/net/thauvin/erik/mobibot/Utils.java
deleted file mode 100644
index 69bd6a9..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/Utils.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Utils.java
- *
- * 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;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import org.apache.commons.lang3.StringUtils;
-import org.jibble.pircbot.Colors;
-import org.jsoup.Jsoup;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.util.Date;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-/**
- * Miscellaneous utilities class.
- *
- * @author Erik C. Thauvin
- * @created 2014-04-26
- * @since 1.0
- */
-public final class Utils {
- private static final String[] searchFlags = { "%c", "%n" };
-
- /**
- * Disables the default constructor.
- *
- * @throws UnsupportedOperationException If the constructor is called.
- */
- private Utils() {
- throw new UnsupportedOperationException("Illegal constructor call.");
- }
-
- /**
- * Makes the given int bold.
- *
- * @param i The int.
- * @return The bold string.
- */
- public static String bold(final int i) {
- return bold(Integer.toString(i));
- }
-
- /**
- * Makes the given string bold.
- *
- * @param s The string.
- * @return The bold string.
- */
- public static String bold(final String s) {
- return colorize(s, Colors.BOLD);
- }
-
- /**
- * Colorize a string.
- *
- * @param s The string.
- * @param color The color.
- * @return The colorized string.
- */
- static String colorize(final String s, final String color) {
- if (s == null) {
- return Colors.NORMAL;
- } else if (Colors.BOLD.equals(color) || Colors.REVERSE.equals(color)) {
- return color + s + color;
- }
-
- return color + s + Colors.NORMAL;
- }
-
- /**
- * Makes the given string cyan.
- *
- * @param s The string.
- * @return The cyan string.
- */
- public static String cyan(final String s) {
- return colorize(s, Colors.CYAN);
- }
-
- /**
- * URL encodes the given string.
- *
- * @param s The string to encode.
- * @return The encoded string.
- */
- public static String encodeUrl(final String s) {
- return URLEncoder.encode(s, StandardCharsets.UTF_8);
- }
-
- /**
- * Ensures that the given location (File/URL) has a trailing slash (/) to indicate a directory.
- *
- * @param location The File or URL location.
- * @param isUrl Set to true if the location is a URL
- * @return The location ending with a slash.
- */
- static String ensureDir(final String location, final boolean isUrl) {
- if (isUrl) {
- if (location.charAt(location.length() - 1) == '/') {
- return location;
- } else {
- return location + '/';
- }
- } else {
- if (location.charAt(location.length() - 1) == File.separatorChar) {
- return location;
- } else {
- return location + File.separatorChar;
- }
- }
- }
-
- /**
- * Returns a property as an int.
- *
- * @param property The property value.
- * @param def The default property value.
- * @return The port or default value if invalid.
- */
- public static int getIntProperty(final String property, final int def) {
- int prop;
-
- try {
- prop = Integer.parseInt(property);
- } catch (NumberFormatException ignore) {
- prop = def;
- }
-
- return prop;
- }
-
- /**
- * Makes the given string green.
- *
- * @param s The string.
- * @return The green string.
- */
- public static String green(final String s) {
- return colorize(s, Colors.DARK_GREEN);
- }
-
- /**
- * Formats a help command by replacing {@code %c} with the bot's pub/priv command, and {@code %n} with the bot's
- * nick.
- *
- * @param text The help command text.
- * @param botNick The bot's nick.
- * @param isPrivate The private flag.
- * @return The formatted help command.
- */
- public static String helpFormat(final String text, final String botNick, final boolean isPrivate) {
- final String[] replace = { (isPrivate) ? "/msg " + botNick : botNick + ':', botNick };
- return StringUtils.replaceEach(text, searchFlags, replace);
- }
-
- /**
- * Returns indented help string.
- *
- * @param help The help string.
- * @return The indented help string.
- * @see #helpIndent(String, boolean)
- */
- public static String helpIndent(final String help) {
- return helpIndent(help, true);
- }
-
- /**
- * Returns indented help string.
- *
- * @param help The help string.
- * @param isBold The bold flag.
- * @return The indented help string.
- */
- public static String helpIndent(final String help, final boolean isBold) {
- return " " + (isBold ? bold(help) : help);
- }
-
- /**
- * Returns the specified date as an ISO local date string.
- *
- * @param date The date.
- * @return The date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format.
- */
- public static String isoLocalDate(final Date date) {
- return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()));
- }
-
- /**
- * Returns the specified date as an ISO local date string.
- *
- * @param date The date.
- * @return The date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format.
- */
- public static String isoLocalDate(final LocalDateTime date) {
- return date.format(DateTimeFormatter.ISO_LOCAL_DATE);
- }
-
- /**
- * Obfuscates the given string.
- *
- * @param s The string.
- * @return The obfuscated string.
- */
- public static String obfuscate(final String s) {
- if (StringUtils.isNotBlank(s)) {
- return StringUtils.repeat('x', s.length());
- }
- return s;
- }
-
- /**
- * Returns the plural form of a word, if count > 1.
- *
- * @param count The count.
- * @param word The word.
- * @param plural The plural word.
- * @return The plural string.
- */
- public static String plural(final long count, final String word, final String plural) {
- if (count > 1) {
- return plural;
- } else {
- return word;
- }
- }
-
- /**
- * Makes the given string red.
- *
- * @param s The string.
- * @return The red string.
- */
- public static String red(final String s) {
- return colorize(s, Colors.RED);
- }
-
- /**
- * Makes the given string reverse color.
- *
- * @param s The string.
- * @return The reverse color string.
- */
- public static String reverseColor(final String s) {
- return colorize(s, Colors.REVERSE);
- }
-
- /**
- * Returns today's date.
- *
- * @return Today's date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format.
- */
- public static String today() {
- return isoLocalDate(LocalDateTime.now());
- }
-
- /**
- * Converts XML/XHTML entities to plain text.
- *
- * @param str The string to unescape.
- * @return The unescaped string.
- */
- public static String unescapeXml(final String str) {
- return Jsoup.parse(str).text();
- }
-
- /**
- * Converts milliseconds to year month week day hour and minutes.
- *
- * @param uptime The uptime in milliseconds.
- * @return The uptime in year month week day hours and minutes.
- */
- public static String uptime(final long uptime) {
- final StringBuilder info = new StringBuilder();
-
- long days = TimeUnit.MILLISECONDS.toDays(uptime);
- final long years = days / 365;
- days %= 365;
- final long months = days / 30;
- days %= 30;
- final long weeks = days / 7;
- days %= 7;
- final long hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours(
- TimeUnit.MILLISECONDS.toDays(uptime));
- final long minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes(
- TimeUnit.MILLISECONDS.toHours(uptime));
-
- if (years > 0) {
- info.append(years).append(plural(years, " year ", " years "));
- }
-
- if (months > 0) {
- info.append(weeks).append(plural(months, " month ", " months "));
- }
-
- if (weeks > 0) {
- info.append(weeks).append(plural(weeks, " week ", " weeks "));
- }
-
-
- if (days > 0) {
- info.append(days).append(plural(days, " day ", " days "));
- }
-
- if (hours > 0) {
- info.append(hours).append(plural(hours, " hour ", " hours "));
- }
-
- info.append(minutes).append(plural(minutes, " minute", " minutes"));
-
- return info.toString();
- }
-
- /**
- * Reads contents of a URL.
- *
- * @param url The URL to read.
- * @return The URL contents.
- * @throws IOException If an IO error occurs.
- */
- @SuppressFBWarnings("SECSSSRFUC")
- public static String urlReader(final URL url) throws IOException {
- try (final BufferedReader reader = new BufferedReader(
- new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
- return reader.lines().collect(Collectors.joining(System.lineSeparator()));
- }
- }
-
- /**
- * Returns the specified date formatted as yyyy-MM-dd HH:mm.
- *
- * @param date The date.
- * @return The formatted date.
- */
- public static String utcDateTime(final Date date) {
- return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()));
- }
-
- /**
- * Returns the specified date formatted as yyyy-MM-dd HH:mm.
- *
- * @param date The date.
- * @return The formatted date.
- */
- public static String utcDateTime(final LocalDateTime date) {
- return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
- }
-
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/Utils.kt b/src/main/java/net/thauvin/erik/mobibot/Utils.kt
new file mode 100644
index 0000000..c1cae4b
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/Utils.kt
@@ -0,0 +1,301 @@
+/*
+ * Utils.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
+
+import org.apache.commons.lang3.StringUtils
+import org.jibble.pircbot.Colors
+import org.jsoup.Jsoup
+import java.io.BufferedReader
+import java.io.File
+import java.io.IOException
+import java.io.InputStreamReader
+import java.net.URL
+import java.net.URLEncoder
+import java.nio.charset.StandardCharsets
+import java.time.LocalDateTime
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.util.*
+import java.util.concurrent.TimeUnit
+import java.util.stream.Collectors
+
+/**
+ * Miscellaneous utilities class.
+ */
+class Utils private constructor() {
+ companion object {
+ private val searchFlags = arrayOf("%c", "%n")
+
+ /**
+ * Makes the given int bold.
+ */
+ @JvmStatic
+ fun bold(i: Int): String {
+ return bold(i.toString())
+ }
+
+ /**
+ * Makes the given string bold.
+ */
+ @JvmStatic
+ fun bold(s: String?): String {
+ return colorize(s, Colors.BOLD)
+ }
+
+ /**
+ * Colorize a string.
+ */
+ @JvmStatic
+ fun colorize(s: String?, color: String): String {
+ if (s == null) {
+ return Colors.NORMAL
+ } else if (Colors.BOLD == color || Colors.REVERSE == color) {
+ return color + s + color
+ }
+ return color + s + Colors.NORMAL
+ }
+
+ /**
+ * Makes the given string cyan.
+ */
+ @JvmStatic
+ fun cyan(s: String?): String {
+ return colorize(s, Colors.CYAN)
+ }
+
+ /**
+ * URL encodes the given string.
+ */
+ fun encodeUrl(s: String?): String {
+ return URLEncoder.encode(s, StandardCharsets.UTF_8)
+ }
+
+ /**
+ * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory.
+ */
+ @JvmStatic
+ fun ensureDir(location: String, isUrl: Boolean): String {
+ return if (location.isNotEmpty()) {
+ if (isUrl) {
+ if (location[location.length - 1] == '/') {
+ location
+ } else {
+ "$location/"
+ }
+ } else {
+ if (location[location.length - 1] == File.separatorChar) {
+ location
+ } else {
+ location + File.separatorChar
+ }
+ }
+ } else {
+ location
+ }
+ }
+
+ /**
+ * Returns a property as an int.
+ */
+ @JvmStatic
+ fun getIntProperty(property: String, def: Int): Int {
+ return try {
+ property.toInt()
+ } catch (ignore: NumberFormatException) {
+ def
+ }
+ }
+
+ /**
+ * Makes the given string green.
+ */
+ @JvmStatic
+ fun green(s: String?): String {
+ return colorize(s, Colors.DARK_GREEN)
+ }
+
+ /**
+ * Formats a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's
+ * nick.
+ */
+ @JvmStatic
+ fun helpFormat(text: String?, botNick: String, isPrivate: Boolean): String {
+ val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick)
+ return StringUtils.replaceEach(text, searchFlags, replace)
+ }
+
+ /**
+ * Returns indented help string.
+ */
+ @JvmStatic
+ @JvmOverloads
+ fun helpIndent(help: String?, isBold: Boolean = true): String {
+ return " " + if (isBold) bold(help) else help
+ }
+
+ /**
+ * Returns the specified date as an ISO local date string.
+ */
+ @JvmStatic
+ fun isoLocalDate(date: Date): String {
+ return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()))
+ }
+
+ /**
+ * Returns the specified date as an ISO local date string.
+ */
+ @JvmStatic
+ fun isoLocalDate(date: LocalDateTime): String {
+ return date.format(DateTimeFormatter.ISO_LOCAL_DATE)
+ }
+
+ /**
+ * Obfuscates the given string.
+ */
+ @JvmStatic
+ fun obfuscate(s: String?): String {
+ return if (s!!.isNotBlank()) {
+ StringUtils.repeat('x', s.length)
+ } else s
+ }
+
+ /**
+ * Returns the plural form of a word, if count > 1.
+ */
+ @JvmStatic
+ fun plural(count: Long, word: String, plural: String): String {
+ return if (count > 1) {
+ plural
+ } else {
+ word
+ }
+ }
+
+ /**
+ * Makes the given string red.
+ */
+ @JvmStatic
+ fun red(s: String?): String {
+ return colorize(s, Colors.RED)
+ }
+
+ /**
+ * Makes the given string reverse color.
+ */
+ @JvmStatic
+ fun reverseColor(s: String?): String {
+ return colorize(s, Colors.REVERSE)
+ }
+
+ /**
+ * Returns today's date.
+ */
+ @JvmStatic
+ fun today(): String {
+ return isoLocalDate(LocalDateTime.now())
+ }
+
+ /**
+ * Converts XML/XHTML entities to plain text.
+ */
+ @JvmStatic
+ fun unescapeXml(str: String?): String {
+ return Jsoup.parse(str).text()
+ }
+
+ /**
+ * Converts milliseconds to year month week day hour and minutes.
+ */
+ @JvmStatic
+ fun uptime(uptime: Long): String {
+ val info = StringBuilder()
+ var days = TimeUnit.MILLISECONDS.toDays(uptime)
+ val years = days / 365
+ days %= 365
+ val months = days / 30
+ days %= 30
+ val weeks = days / 7
+ days %= 7
+ val hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours(
+ TimeUnit.MILLISECONDS.toDays(uptime)
+ )
+ val minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes(
+ TimeUnit.MILLISECONDS.toHours(uptime)
+ )
+ with(info) {
+ if (years > 0) {
+ append(years).append(plural(years, " year ", " years "))
+ }
+ if (months > 0) {
+ append(weeks).append(plural(months, " month ", " months "))
+ }
+ if (weeks > 0) {
+ append(weeks).append(plural(weeks, " week ", " weeks "))
+ }
+ if (days > 0) {
+ append(days).append(plural(days, " day ", " days "))
+ }
+ if (hours > 0) {
+ append(hours).append(plural(hours, " hour ", " hours "))
+ }
+ append(minutes).append(plural(minutes, " minute", " minutes"))
+ return toString()
+ }
+ }
+
+ /**
+ * Reads contents of a URL.
+ */
+ @JvmStatic
+ @Throws(IOException::class)
+ fun urlReader(url: URL): String {
+ BufferedReader(InputStreamReader(url.openStream(), StandardCharsets.UTF_8))
+ .use { reader -> return reader.lines().collect(Collectors.joining(System.lineSeparator())) }
+ }
+
+ /**
+ * Returns the specified date formatted as `yyyy-MM-dd HH:mm`.
+ */
+ @JvmStatic
+ fun utcDateTime(date: Date): String {
+ return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()))
+ }
+
+ /**
+ * Returns the specified date formatted as `yyyy-MM-dd HH:mm`.
+ */
+ @JvmStatic
+ fun utcDateTime(date: LocalDateTime): String {
+ return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
+ }
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt
index df19332..3168dde 100644
--- a/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt
+++ b/src/main/java/net/thauvin/erik/mobibot/commands/AbstractCommand.kt
@@ -63,9 +63,10 @@ abstract class AbstractCommand(val bot: Mobibot) {
return false
}
- open fun getProperty(key: String) : String? {
+ open fun getProperty(key: String): String? {
return properties[key]
}
+
open fun getPropertyKeys(): Set {
return properties.keys
}
diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt
index dbccdd6..1ca4ed2 100644
--- a/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt
+++ b/src/main/java/net/thauvin/erik/mobibot/commands/Cycle.kt
@@ -38,10 +38,7 @@ import net.thauvin.erik.mobibot.Utils
class Cycle(bot: Mobibot) : AbstractCommand(bot) {
private val wait = 10
override val name = "cycle"
- override val help = listOf(
- "To have the bot leave the channel and come back:",
- Utils.helpIndent("%c $name")
- )
+ override val help = listOf("To have the bot leave the channel and come back:", Utils.helpIndent("%c $name"))
override val isOp = true
override val isPublic = false
override val isVisible = true
diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java
index aed639c..1282d78 100644
--- a/src/main/java/net/thauvin/erik/mobibot/commands/Info.java
+++ b/src/main/java/net/thauvin/erik/mobibot/commands/Info.java
@@ -48,7 +48,7 @@ public class Info extends AbstractCommand {
+ " (" + Utils.green(ReleaseInfo.WEBSITE) + ')',
"Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')');
- public Info(@NotNull final Mobibot bot) {
+ public Info(final Mobibot bot) {
super(bot);
}
@@ -97,18 +97,14 @@ public class Info extends AbstractCommand {
if (isOp) {
if (getBot().getTell().isEnabled()) {
- info.append(", Messages: ")
- .append(getBot().getTell().size());
+ info.append(", Messages: ").append(getBot().getTell().size());
}
if (getBot().getTwitter().isAutoPost()) {
- info.append(", Twitter: ")
- .append(getBot().getTwitter().entriesCount());
+ info.append(", Twitter: ").append(getBot().getTwitter().entriesCount());
}
}
- info.append(", Recap: ")
- .append(Recap.recapCount())
- .append(']');
+ info.append(", Recap: ").append(Recap.recapCount()).append(']');
getBot().send(sender, info.toString(), isPrivate);
}
diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java
index 05efbec..8470320 100644
--- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java
+++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/Tell.java
@@ -173,7 +173,8 @@ public class Tell extends AbstractCommand {
Utils.helpIndent("%c " + TELL_CMD + " "),
"To view queued and sent messages:",
Utils.helpIndent("%c " + TELL_CMD + ' ' + View.VIEW_CMD),
- "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."));
+ "Messages are kept for " + Utils.bold(maxDays)
+ + Utils.plural(maxDays, " day.", " days."));
}
@Override
@@ -383,8 +384,8 @@ public class Tell extends AbstractCommand {
isPrivate)),
isPrivate);
getBot().send(sender,
- "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."),
- isPrivate);
+ "Messages are kept for " + Utils.bold(maxDays)
+ + Utils.plural(maxDays, " day.", " days."), isPrivate);
}
}
}
diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java
index f0485db..e114d66 100644
--- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java
+++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesUtils.java
@@ -84,7 +84,8 @@ public final class EntriesUtils {
* @param isView Set to true to display the number of comments.
* @return The entry's link.
*/
- @SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.")
+ @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(Constants.LINK_CMD).append(index + 1)
.append(": ").append('[').append(entry.getNick()).append(']');
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java
deleted file mode 100644
index f8483af..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * AbstractModule.java
- *
- * 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.modules;
-
-import net.thauvin.erik.mobibot.Mobibot;
-import net.thauvin.erik.mobibot.Utils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * The Module abstract class.
- *
- * @author Erik C. Thauvin
- * @created 2016-07-01
- * @since 1.0
- */
-public abstract class AbstractModule {
- final Mobibot bot;
- final List commands = new ArrayList<>();
- final List help = new ArrayList<>();
- final Map properties = new ConcurrentHashMap<>();
-
- AbstractModule(final Mobibot bot) {
- this.bot = bot;
- }
-
- /**
- * Responds to a command.
- *
- * @param sender The sender.
- * @param cmd The command.
- * @param args The command arguments.
- * @param isPrivate Set to true if the response should be sent as a private message.
- */
- public abstract void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate);
-
- /**
- * Returns the module's commands, if any.
- *
- * @return The commands.
- */
- public List getCommands() {
- return commands;
- }
-
- /**
- * Returns the module's property keys.
- *
- * @return The keys.
- */
- public Set getPropertyKeys() {
- return properties.keySet();
- }
-
- /**
- * Returns true if the module has properties.
- *
- * @return true or false .
- */
- public boolean hasProperties() {
- return !properties.isEmpty();
- }
-
- /**
- * Responds with the module's help.
- *
- * @param sender The sender.
- * @param isPrivate Set to true if the response should be sent as a private message.
- */
- public void helpResponse(final String sender, final boolean isPrivate) {
- for (final String h : help) {
- bot.send(sender, Utils.helpFormat(h, bot.getNick(), isPrivateMsgEnabled() && isPrivate), isPrivate);
- }
- }
-
- /**
- * Initializes the properties.
- *
- * @param keys The properties keys to initialize.
- */
- public void initProperties(final String... keys) {
- for (final String key : keys) {
- properties.put(key, "");
- }
- }
-
- /**
- * Returns true if the module is enabled.
- *
- * @return true or false
- */
- public boolean isEnabled() {
- if (hasProperties()) {
- return isValidProperties();
- } else {
- return true;
- }
- }
-
- /**
- * Returns true if the module responds to private messages.
- *
- * @return true or false
- */
- public boolean isPrivateMsgEnabled() {
- return false;
- }
-
- /**
- * Ensures that all properties have values.
- *
- * @return true if the properties are valid, false otherwise.
- */
- boolean isValidProperties() {
- for (final String s : getPropertyKeys()) {
- if (StringUtils.isBlank(properties.get(s))) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Sets a property key and value.
- *
- * @param key The key.
- * @param value The value.
- */
- public void setProperty(final String key, final String value) {
- if (StringUtils.isNotBlank(key)) {
- properties.put(key, value);
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt
new file mode 100644
index 0000000..dd40a8c
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt
@@ -0,0 +1,131 @@
+/*
+ * AbstractModule.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.modules
+
+import net.thauvin.erik.mobibot.Mobibot
+import net.thauvin.erik.mobibot.Utils
+import org.apache.commons.lang3.StringUtils
+import java.util.*
+import java.util.concurrent.ConcurrentHashMap
+
+/**
+ * The `Module` abstract class.
+ */
+abstract class AbstractModule(val bot: Mobibot) {
+ /**
+ * The module's commands, if any.
+ */
+ @JvmField
+ val commands: MutableList = ArrayList()
+
+ @JvmField
+ val help: MutableList = ArrayList()
+ val properties: MutableMap = ConcurrentHashMap()
+
+ /**
+ * Responds to a command.
+ */
+ abstract fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ )
+
+ /**
+ * Returns the module's property keys.
+ */
+ val propertyKeys: Set
+ get() = properties.keys
+
+ /**
+ * Returns `true` if the module has properties.
+ */
+ fun hasProperties(): Boolean {
+ return properties.isNotEmpty()
+ }
+
+ /**
+ * Responds with the module's help.
+ */
+ open fun helpResponse(sender: String, isPrivate: Boolean) {
+ for (h in help) {
+ bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate)
+ }
+ }
+
+ /**
+ * Initializes the properties.
+ */
+ fun initProperties(vararg keys: String) {
+ for (key in keys) {
+ properties[key] = ""
+ }
+ }
+
+ /**
+ * Returns `true` if the module is enabled.
+ */
+ val isEnabled: Boolean
+ get() = if (hasProperties()) {
+ isValidProperties
+ } else {
+ true
+ }
+
+ /**
+ * Returns `true` if the module responds to private messages.
+ */
+ open val isPrivateMsgEnabled: Boolean = false
+
+ /**
+ * Ensures that all properties have values.
+ */
+ open val isValidProperties: Boolean
+ get() {
+ for (s in propertyKeys) {
+ if (StringUtils.isBlank(properties[s])) {
+ return false
+ }
+ }
+ return true
+ }
+
+ /**
+ * Sets a property key and value.
+ */
+ fun setProperty(key: String, value: String) {
+ if (StringUtils.isNotBlank(key)) {
+ properties[key] = value
+ }
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java
deleted file mode 100644
index 862711f..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/Calc.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Calc.java
- *
- * 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.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;
-
-/**
- * The Calc module.
- *
- * @author Erik C. Thauvin
- * @created 2016-07-01
- * @since 1.0
- */
-public class Calc extends AbstractModule {
- // Calc command
- private static final String CALC_CMD = "calc";
-
- /**
- * The default constructor.
- */
- public Calc(final Mobibot bot) {
- super(bot);
-
- commands.add(CALC_CMD);
-
- help.add("To solve a mathematical calculation:");
- help.add(Utils.helpIndent("%c " + CALC_CMD + " "));
- }
-
- /**
- * Performs a calculation.
- *
- *
1 + 1 * 2
- *
- * @param query The query.
- * @return The calculation result.
- */
- static String calc(final String query) {
- final DecimalFormat decimalFormat = new DecimalFormat("#.##");
-
- try {
- final Expression calc = new ExpressionBuilder(query).build();
- return query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate()));
- } catch (Exception e) {
- return "No idea. This is the kind of math I don't get.";
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate) {
- if (StringUtils.isNotBlank(args)) {
- bot.send(calc(args));
- } else {
- helpResponse(sender, isPrivate);
- }
- }
-}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt
similarity index 54%
rename from src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java
rename to src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt
index 5eab364..22a6c7e 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/AbstractModuleTest.java
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/Calc.kt
@@ -1,5 +1,5 @@
/*
- * AbstractModuleTest.java
+ * Calc.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -29,41 +29,53 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package net.thauvin.erik.mobibot.modules
-package net.thauvin.erik.mobibot.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-import static org.assertj.core.api.Assertions.assertThat;
+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
/**
- * The AbstractModuleTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-07
- * @since 1.0
+ * The Calc module.
*/
-
-final class AbstractModuleTest {
- private AbstractModuleTest() {
- throw new UnsupportedOperationException("Illegal constructor call.");
+class Calc(bot: Mobibot) : AbstractModule(bot) {
+ override fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ ) {
+ if (StringUtils.isNotBlank(args)) {
+ bot.send(calc(args))
+ } else {
+ helpResponse(sender, isPrivate)
+ }
}
- @SuppressFBWarnings("CE_CLASS_ENVY")
- static void testAbstractModule(final AbstractModule module) {
- final String name = module.getClass().getName();
+ companion object {
+ // Calc command
+ private const val CALC_CMD = "calc"
- assertThat(module.isEnabled()).as(name + ": enabled").isNotEqualTo(module.hasProperties());
- assertThat(module.getCommands().size()).as(name + ": commands > 0").isGreaterThan(0);
- if (!module.hasProperties()) {
- assertThat(module.getPropertyKeys().size()).as(name + ": no properties").isEqualTo(0);
- module.setProperty("test", "test");
- module.setProperty("", "invalid");
+ /**
+ * Performs a calculation. e.g.: 1 + 1 * 2
+ */
+ @JvmStatic
+ fun calc(query: String): String {
+ val decimalFormat = DecimalFormat("#.##")
+ return try {
+ val calc = ExpressionBuilder(query).build()
+ query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate()))
+ } catch (e: IllegalArgumentException) {
+ "No idea. This is the kind of math I don't get."
+ }
}
+ }
- assertThat(module.getPropertyKeys().size()).as(name + ": properties > 0").isGreaterThan(0);
-
- module.setProperty("invalid", "");
- assertThat(module.isValidProperties()).as(name + ": invalid properties").isFalse();
+ init {
+ commands.add(CALC_CMD)
+ help.add("To solve a mathematical calculation:")
+ help.add(Utils.helpIndent("%c $CALC_CMD "))
}
}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java
deleted file mode 100644
index 2794395..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * CurrencyConverter.java
- *
- * 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.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;
-import org.apache.commons.lang3.StringUtils;
-import org.jdom2.Document;
-import org.jdom2.Element;
-import org.jdom2.JDOMException;
-import org.jdom2.Namespace;
-import org.jdom2.input.SAXBuilder;
-
-import javax.xml.XMLConstants;
-import java.io.IOException;
-import java.net.URL;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import static org.apache.commons.lang3.StringUtils.upperCase;
-
-/**
- * The CurrentConverter module.
- *
- * @author Erik C. Thauvin
- * @created Feb 11, 2004
- * @since 1.0
- */
-@SuppressWarnings("PMD.UseConcurrentHashMap")
-public final class CurrencyConverter extends ThreadedModule {
- // Currency command
- private static final String CURRENCY_CMD = "currency";
- // Rates keyword
- private static final String CURRENCY_RATES_KEYWORD = "rates";
- // Empty rate table.
- private static final String EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty.";
- // Exchange rates
- private static final Map EXCHANGE_RATES = new TreeMap<>();
- // Exchange rates table URL
- private static final String EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
- // Last exchange rates table publication date
- private static String pubDate = "";
-
- /**
- * Creates a new {@link CurrencyConverter} instance.
- */
- public CurrencyConverter(final Mobibot bot) {
- super(bot);
-
- commands.add(CURRENCY_CMD);
- }
-
- /**
- * Converts from a currency to another.
- *
- *
100 USD to EUR
- *
- * @param query The query.
- * @return The {@link Message} contained the converted currency.
- */
- static Message convertCurrency(final String query) {
- final String[] cmds = query.split(" ");
-
- if (cmds.length == 4) {
- if (cmds[3].equals(cmds[1]) || "0".equals(cmds[0])) {
- return new PublicMessage("You're kidding, right?");
- } else {
- final String to = upperCase(cmds[1]);
- final String from = upperCase(cmds[3]);
-
- if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) {
- try {
- final double amt = Double.parseDouble(cmds[0].replace(",", ""));
- final double doubleFrom = Double.parseDouble(EXCHANGE_RATES.get(to));
- final double doubleTo = Double.parseDouble(EXCHANGE_RATES.get(from));
-
- return new PublicMessage(
- NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1)
- + ' '
- + upperCase(cmds[1])
- + " = "
- + NumberFormat.getCurrencyInstance(Constants.LOCALE)
- .format((amt * doubleTo) / doubleFrom)
- .substring(1)
- + ' '
- + upperCase(cmds[3]));
- } catch (NumberFormatException e) {
- return new ErrorMessage("Let's try with some real numbers next time, okay?");
- }
- } else {
- return new ErrorMessage("Sounds like monopoly money to me!");
- }
- }
- }
- return new ErrorMessage("Invalid query. Let's try again.");
- }
-
- static List currencyRates() {
- final List rates = new ArrayList<>(33);
- for (final Map.Entry rate : EXCHANGE_RATES.entrySet()) {
- rates.add(" " + rate.getKey() + ": " + StringUtils.leftPad(rate.getValue(), 8));
- }
-
- return rates;
- }
-
- static void loadRates() throws ModuleException {
- if (EXCHANGE_RATES.isEmpty()) {
- try {
- final SAXBuilder builder = new SAXBuilder();
- // See https://rules.sonarsourcecom/java/tag/owasp/RSPEC-2755
- builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
- builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
- builder.setIgnoringElementContentWhitespace(true);
-
- final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL));
- final Element root = doc.getRootElement();
- final Namespace ns = root.getNamespace("");
- final Element cubeRoot = root.getChild("Cube", ns);
- final Element cubeTime = cubeRoot.getChild("Cube", ns);
-
- pubDate = cubeTime.getAttribute("time").getValue();
-
- final List cubes = cubeTime.getChildren();
-
- for (final Element cube : cubes) {
- EXCHANGE_RATES.put(
- cube.getAttribute("currency").getValue(),
- cube.getAttribute("rate").getValue());
- }
-
- EXCHANGE_RATES.put("EUR", "1");
- } catch (JDOMException | IOException e) {
- throw new ModuleException(e.getMessage(),
- "An error has occurred while parsing the exchange rates table.",
- e);
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate) {
- synchronized (this) {
- if (!pubDate.equals(Utils.today())) {
- EXCHANGE_RATES.clear();
- }
- }
-
- super.commandResponse(sender, cmd, args, isPrivate);
- }
-
- /**
- * Converts the specified currencies.
- */
- @SuppressFBWarnings("REDOS")
- @Override
- void run(final String sender, final String cmd, final String query, final boolean isPrivate) {
- if (EXCHANGE_RATES.isEmpty()) {
- try {
- loadRates();
- } catch (ModuleException e) {
- bot.getLogger().warn(e.getDebugMessage(), e);
- }
- }
-
- if (EXCHANGE_RATES.isEmpty()) {
- bot.send(sender, EMPTY_RATE_TABLE, true);
- } else if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) {
- final Message msg = convertCurrency(query);
- bot.send(sender, msg);
- if (msg.isError()) {
- helpResponse(sender, isPrivate);
- }
- } else if (query.contains(CURRENCY_RATES_KEYWORD)) {
- bot.send(sender, "The currency rates for " + Utils.bold(pubDate) + " are:", isPrivate);
- bot.sendList(sender, currencyRates(), 3, isPrivate, false);
- } else {
- helpResponse(sender, isPrivate);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void helpResponse(final String sender, final boolean isPrivate) {
- if (EXCHANGE_RATES.isEmpty()) {
- try {
- loadRates();
- } catch (ModuleException e) {
- bot.getLogger().debug(e.getDebugMessage(), e);
- }
- }
- if (EXCHANGE_RATES.isEmpty()) {
- bot.send(sender, EMPTY_RATE_TABLE, isPrivate);
- } else {
- bot.send(sender, "To convert from one currency to another:", isPrivate);
- bot.send(sender,
- Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + " 100 USD to EUR",
- bot.getNick(), isPrivateMsgEnabled())), isPrivate);
- bot.send(sender, "For a listing of current rates:", isPrivate);
- bot.send(sender,
- Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + ' ' + CURRENCY_RATES_KEYWORD,
- bot.getNick(), isPrivateMsgEnabled())), isPrivate);
- bot.send(sender, "The supported currencies are: ", isPrivate);
- bot.sendList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false);
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt
new file mode 100644
index 0000000..c72e73d
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt
@@ -0,0 +1,234 @@
+/*
+ * CurrencyConverter.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.modules
+
+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
+import org.apache.commons.lang3.StringUtils
+import org.jdom2.JDOMException
+import org.jdom2.input.SAXBuilder
+import java.io.IOException
+import java.net.URL
+import java.text.NumberFormat
+import java.util.*
+import javax.xml.XMLConstants
+
+/**
+ * The CurrentConverter module.
+ */
+class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
+ override fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ ) {
+ synchronized(this) {
+ if (pubDate != Utils.today()) {
+ EXCHANGE_RATES.clear()
+ }
+ }
+ super.commandResponse(sender, cmd, args, isPrivate)
+ }
+
+ /**
+ * Converts the specified currencies.
+ */
+ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
+ with(bot) {
+ if (EXCHANGE_RATES.isEmpty()) {
+ try {
+ loadRates()
+ } catch (e: ModuleException) {
+ logger.warn(e.debugMessage, e)
+ }
+ }
+
+ if (EXCHANGE_RATES.isEmpty()) {
+ send(sender, EMPTY_RATE_TABLE, true)
+ } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) {
+ val msg = convertCurrency(args)
+ send(sender, msg)
+ if (msg.isError) {
+ helpResponse(sender, isPrivate)
+ }
+ } else if (args.contains(CURRENCY_RATES_KEYWORD)) {
+ send(sender, "The currency rates for ${Utils.bold(pubDate)} are:", isPrivate)
+ sendList(sender, currencyRates(), 3, isPrivate, false)
+ } else {
+ helpResponse(sender, isPrivate)
+ }
+ }
+ }
+
+ override fun helpResponse(sender: String, isPrivate: Boolean) {
+ with(bot) {
+ if (EXCHANGE_RATES.isEmpty()) {
+ try {
+ loadRates()
+ } catch (e: ModuleException) {
+ logger.debug(e.debugMessage, e)
+ }
+ }
+ if (EXCHANGE_RATES.isEmpty()) {
+ send(sender, EMPTY_RATE_TABLE, isPrivate)
+ } else {
+ send(sender, "To convert from one currency to another:", isPrivate)
+ send(
+ sender,
+ Utils.helpIndent(
+ Utils.helpFormat("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)
+ ),
+ isPrivate
+ )
+ send(sender, "For a listing of current rates:", isPrivate)
+ send(
+ sender,
+ Utils.helpIndent(
+ Utils.helpFormat("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled)
+ ),
+ isPrivate
+ )
+ send(sender, "The supported currencies are: ", isPrivate)
+ sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate, false)
+ }
+ }
+ }
+
+ companion object {
+ // Currency command
+ private const val CURRENCY_CMD = "currency"
+
+ // Rates keyword
+ private const val CURRENCY_RATES_KEYWORD = "rates"
+
+ // Empty rate table.
+ private const val EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty."
+
+ // Exchange rates
+ private val EXCHANGE_RATES: MutableMap = TreeMap()
+
+ // Exchange rates table URL
+ private const val EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
+
+ // Last exchange rates table publication date
+ private var pubDate = ""
+
+ /**
+ * Converts from a currency to another.
+ */
+ @JvmStatic
+ fun convertCurrency(query: String): Message {
+ val cmds = query.split(" ").toTypedArray()
+ return if (cmds.size == 4) {
+ if (cmds[3] == cmds[1] || "0" == cmds[0]) {
+ PublicMessage("You're kidding, right?")
+ } else {
+ val to = StringUtils.upperCase(cmds[1])
+ val from = StringUtils.upperCase(cmds[3])
+ if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) {
+ try {
+ val amt = cmds[0].replace(",", "").toDouble()
+ val doubleFrom = EXCHANGE_RATES[to]!!.toDouble()
+ val doubleTo = EXCHANGE_RATES[from]!!.toDouble()
+ PublicMessage(
+ NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1)
+ + " ${StringUtils.upperCase(cmds[1])} = "
+ + NumberFormat.getCurrencyInstance(Constants.LOCALE)
+ .format(amt * doubleTo / doubleFrom).substring(1)
+ + " ${StringUtils.upperCase(cmds[3])}"
+ )
+ } catch (e: NumberFormatException) {
+ ErrorMessage("Let's try with some real numbers next time, okay?")
+ }
+ } else {
+ ErrorMessage("Sounds like monopoly money to me!")
+ }
+ }
+ } else ErrorMessage("Invalid query. Let's try again.")
+ }
+
+ @JvmStatic
+ fun currencyRates(): List {
+ val rates = ArrayList(33)
+ for ((key, value) in EXCHANGE_RATES) {
+ rates.add(" $key: ${StringUtils.leftPad(value, 8)}")
+ }
+ return rates
+ }
+
+ @JvmStatic
+ @Throws(ModuleException::class)
+ fun loadRates() {
+ if (EXCHANGE_RATES.isEmpty()) {
+ try {
+ val builder = SAXBuilder()
+ // See https://rules.sonarsourcecom/java/tag/owasp/RSPEC-2755
+ builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "")
+ builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "")
+ builder.ignoringElementContentWhitespace = true
+ val doc = builder.build(URL(EXCHANGE_TABLE_URL))
+ val root = doc.rootElement
+ val ns = root.getNamespace("")
+ val cubeRoot = root.getChild("Cube", ns)
+ val cubeTime = cubeRoot.getChild("Cube", ns)
+ pubDate = cubeTime.getAttribute("time").value
+ val cubes = cubeTime.children
+ for (cube in cubes) {
+ EXCHANGE_RATES[cube.getAttribute("currency").value] = cube.getAttribute("rate").value
+ }
+ EXCHANGE_RATES["EUR"] = "1"
+ } catch (e: JDOMException) {
+ throw ModuleException(
+ e.message,
+ "An JDOM parsing error has occurred while parsing the exchange rates table.",
+ e
+ )
+ } catch (e: IOException) {
+ throw ModuleException(
+ e.message,
+ "An IO error has occurred while parsing the exchange rates table.",
+ e
+ )
+ }
+ }
+ }
+ }
+
+ init {
+ commands.add(CURRENCY_CMD)
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java
deleted file mode 100644
index 861a244..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Dice.java
- *
- * 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.modules;
-
-import net.thauvin.erik.mobibot.Mobibot;
-import net.thauvin.erik.mobibot.Utils;
-
-import java.security.SecureRandom;
-
-import static net.thauvin.erik.mobibot.Utils.bold;
-
-/**
- * The Dice module.
- *
- * @author Erik C. Thauvin
- * @created 2014-04-28
- * @since 1.0
- */
-public final class Dice extends AbstractModule {
- // Dice command
- private static final String DICE_CMD = "dice";
-
- /**
- * The default constructor.
- */
- public Dice(final Mobibot bot) {
- super(bot);
-
- commands.add(DICE_CMD);
-
- help.add("To roll the dice:");
- help.add(Utils.helpIndent("%c " + DICE_CMD));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate) {
- final SecureRandom r = new SecureRandom();
-
- int i = r.nextInt(6) + 1;
- int y = r.nextInt(6) + 1;
- final int playerTotal = i + y;
-
- bot.send(bot.getChannel(),
- sender + " rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of "
- + bold(playerTotal), isPrivate);
-
- i = r.nextInt(6) + 1;
- y = r.nextInt(6) + 1;
- final int total = i + y;
-
- bot.action(
- "rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " + bold(total));
-
- if (playerTotal < total) {
- bot.action("wins.");
- } else if (playerTotal > total) {
- bot.action("lost.");
- } else {
- bot.action("tied.");
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt
new file mode 100644
index 0000000..4f6a314
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt
@@ -0,0 +1,103 @@
+/*
+ * Dice.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.modules
+
+import net.thauvin.erik.mobibot.Mobibot
+import net.thauvin.erik.mobibot.Utils
+import kotlin.random.Random
+
+/**
+ * The Dice module.
+ */
+class Dice(bot: Mobibot) : AbstractModule(bot) {
+ override fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ ) {
+ val roll = roll()
+ val playerRoll = roll()
+ val total = roll.first + roll.second
+ val playerTotal = playerRoll.first + playerRoll.second
+ with(bot) {
+ send(
+ channel,
+ "$sender rolled two dice: ${Utils.bold(playerRoll.first)} and ${Utils.bold(playerRoll.second)}"
+ + " for a total of ${Utils.bold(playerTotal)}",
+ isPrivate
+ )
+ action(
+ "rolled two dice: ${Utils.bold(roll.first)} and ${Utils.bold(roll.second)}" +
+ " for a total of ${Utils.bold(total)}"
+ )
+ when (winLoseOrTie(total, playerTotal)) {
+ Result.WIN -> action("wins.")
+ Result.LOSE -> action("lost.")
+ else -> action("tied.")
+ }
+ }
+ }
+
+ enum class Result {
+ WIN, LOSE, TIE
+ }
+
+ private fun roll(): Pair {
+ return Pair(Random.nextInt(1, 7), Random.nextInt(1, 7))
+ }
+
+ companion object {
+ // Dice command
+ private const val DICE_CMD = "dice"
+
+ fun winLoseOrTie(bot: Int, player: Int): Result {
+ return when {
+ bot > player -> {
+ Result.WIN
+ }
+ bot < player -> {
+ Result.LOSE
+ }
+ else -> {
+ Result.TIE
+ }
+ }
+ }
+ }
+
+ init {
+ commands.add(DICE_CMD)
+ help.add("To roll the dice:")
+ help.add(Utils.helpIndent("%c $DICE_CMD"))
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java
deleted file mode 100644
index 478a64e..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * GoogleSearch.java
- *
- * 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.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-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 org.apache.commons.lang3.StringUtils;
-import org.jibble.pircbot.Colors;
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The GoogleSearch module.
- *
- * @author Erik C. Thauvin
- * @created Feb 7, 2004
- * @since 1.0
- */
-public final class GoogleSearch extends ThreadedModule {
- // Google API Key property
- static final String GOOGLE_API_KEY_PROP = "google-api-key";
- // Google Custom Search Engine ID property
- static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx";
- // Google command
- private static final String GOOGLE_CMD = "google";
-
- /**
- * Creates a new {@link GoogleSearch} instance.
- */
- public GoogleSearch(final Mobibot bot) {
- super(bot);
-
- commands.add(GOOGLE_CMD);
-
- help.add("To search Google:");
- help.add(Utils.helpIndent("%c " + GOOGLE_CMD + " "));
-
- initProperties(GOOGLE_API_KEY_PROP, 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 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 results = new ArrayList<>();
- try {
- final URL url =
- new URL("https://www.googleapis.com/customsearch/v1?key="
- + apiKey
- + "&cx="
- + cseKey
- + "&q="
- + Utils.encodeUrl(query)
- + "&filter=1&num=5&alt=json");
-
- final JSONObject json = new JSONObject(Utils.urlReader(url));
- 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(Utils.helpIndent(j.getString("link"), false), 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. Please try again.");
- }
- }
-
- /**
- * Searches Google.
- */
- @Override
- void run(final String sender, final String cmd, final String query, final boolean isPrivate) {
- if (StringUtils.isNotBlank(query)) {
- try {
- final List results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP),
- properties.get(GOOGLE_CSE_KEY_PROP));
- for (final Message msg : results) {
- bot.send(sender, msg);
- }
- } catch (ModuleException e) {
- bot.getLogger().warn(e.getDebugMessage(), e);
- bot.send(sender, e.getMessage(), isPrivate);
- }
- } else {
- helpResponse(sender, isPrivate);
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt
new file mode 100644
index 0000000..b05a0cb
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt
@@ -0,0 +1,125 @@
+/*
+ * GoogleSearch.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.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 org.apache.commons.lang3.StringUtils
+import org.jibble.pircbot.Colors
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.IOException
+import java.net.URL
+import java.util.*
+
+/**
+ * The GoogleSearch module.
+ */
+class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) {
+ /**
+ * Searches Google.
+ */
+ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
+ with(bot) {
+ if (StringUtils.isNotBlank(args)) {
+ try {
+ val results = searchGoogle(
+ args, properties[GOOGLE_API_KEY_PROP],
+ properties[GOOGLE_CSE_KEY_PROP]
+ )
+ for (msg in results) {
+ send(sender, msg)
+ }
+ } catch (e: ModuleException) {
+ logger.warn(e.debugMessage, e)
+ send(sender, e.message, isPrivate)
+ }
+ } else {
+ helpResponse(sender, isPrivate)
+ }
+ }
+ }
+
+ companion object {
+ // Google API Key property
+ const val GOOGLE_API_KEY_PROP = "google-api-key"
+
+ // Google Custom Search Engine ID property
+ const val GOOGLE_CSE_KEY_PROP = "google-cse-cx"
+
+ // Google command
+ private const val GOOGLE_CMD = "google"
+
+ /**
+ * Performs a search on Google.
+ */
+ @JvmStatic
+ @Throws(ModuleException::class)
+ fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List {
+ if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) {
+ throw ModuleException("${StringUtils.capitalize(GOOGLE_CMD)} is disabled. The API keys are missing.")
+ }
+ return if (StringUtils.isNotBlank(query)) {
+ val results = ArrayList()
+ try {
+ val url = URL(
+ "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" +
+ "&q=${Utils.encodeUrl(query)}&filter=1&num=5&alt=json"
+ )
+ val json = JSONObject(Utils.urlReader(url))
+ val ja = json.getJSONArray("items")
+ for (i in 0 until ja.length()) {
+ val j = ja.getJSONObject(i)
+ results.add(NoticeMessage(Utils.unescapeXml(j.getString("title"))))
+ results.add(NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN))
+ }
+ } catch (e: IOException) {
+ throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e)
+ } catch (e: JSONException) {
+ throw ModuleException("searchGoogle($query)", "A JSON error has occurred searching Google.", e)
+ }
+ results
+ } else {
+ throw ModuleException("Invalid query. Please try again.")
+ }
+ }
+ }
+
+ init {
+ commands.add(GOOGLE_CMD)
+ help.add("To search Google:")
+ help.add(Utils.helpIndent("%c $GOOGLE_CMD "))
+ initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP)
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java
deleted file mode 100644
index 23ed888..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Joke.java
- *
- * 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.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.PublicMessage;
-import org.json.JSONObject;
-
-import java.net.URL;
-
-/**
- * The Joke module.
- *
- * @author Erik C. Thauvin
- * @created 2014-04-20
- * @since 1.0
- */
-public final class Joke extends ThreadedModule {
- // Joke command
- private static final String JOKE_CMD = "joke";
- // ICNDB URL
- private static final String JOKE_URL =
- "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]";
-
- /**
- * Creates a new {@link Joke} instance.
- */
- public Joke(final Mobibot bot) {
- super(bot);
-
- commands.add(JOKE_CMD);
-
- help.add("To retrieve a random joke:");
- help.add(Utils.helpIndent("%c " + JOKE_CMD));
- }
-
- /**
- * Retrieves a random joke.
- *
- * @return The {@link Message} containing the new joke.
- * @throws ModuleException If an error occurs while retrieving a new joke.
- */
- static Message randomJoke() throws ModuleException {
- try {
- final URL url = new URL(JOKE_URL);
- final JSONObject json = new JSONObject(Utils.urlReader(url));
- return new PublicMessage(
- json.getJSONObject("value").get("joke").toString().replace("\\'", "'")
- .replace("\\\"", "\""));
- } catch (Exception e) {
- throw new ModuleException("randomJoke()", "An error has occurred retrieving a random joke.", e);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate) {
- new Thread(() -> run(sender, cmd, args, isPrivate)).start();
- }
-
- /**
- * Returns a random joke from The Internet Chuck Norris Database.
- */
- @Override
- void run(final String sender, final String cmd, final String arg, final boolean isPrivate) {
- try {
- bot.send(Utils.cyan(randomJoke().getMsg()));
- } catch (ModuleException e) {
- bot.getLogger().warn(e.getDebugMessage(), e);
- bot.send(sender, e.getMessage(), isPrivate);
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt
new file mode 100644
index 0000000..722ab75
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt
@@ -0,0 +1,100 @@
+/*
+ * Joke.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.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.PublicMessage
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.IOException
+import java.net.URL
+
+/**
+ * The Joke module.
+ */
+class Joke(bot: Mobibot) : ThreadedModule(bot) {
+ override fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ ) {
+ Thread { run(sender, cmd, args, isPrivate) }.start()
+ }
+
+ /**
+ * Returns a random joke from [The Internet Chuck Norris Database](http://www.icndb.com/).
+ */
+ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) = try {
+ bot.send(Utils.cyan(randomJoke().msg))
+ } catch (e: ModuleException) {
+ bot.logger.warn(e.debugMessage, e)
+ bot.send(sender, e.message, isPrivate)
+ }
+
+ companion object {
+ // Joke command
+ private const val JOKE_CMD = "joke"
+
+ // ICNDB URL
+ private const val JOKE_URL =
+ "http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"
+
+ /**
+ * Retrieves a random joke.
+ */
+ @JvmStatic
+ @Throws(ModuleException::class)
+ fun randomJoke(): Message {
+ return try {
+ val url = URL(JOKE_URL)
+ val json = JSONObject(Utils.urlReader(url))
+ PublicMessage(
+ json.getJSONObject("value")["joke"].toString().replace("\\'", "'")
+ .replace("\\\"", "\"")
+ )
+ } catch (e: IOException) {
+ throw ModuleException("randomJoke()", "An IO error has occurred retrieving a random joke.", e)
+ } catch (e: JSONException) {
+ throw ModuleException("randomJoke()", "An JSON error has occurred retrieving a random joke.", e)
+ }
+ }
+ }
+
+ init {
+ commands.add(JOKE_CMD)
+ help.add("To retrieve a random joke:")
+ help.add(Utils.helpIndent("%c $JOKE_CMD"))
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java
deleted file mode 100644
index f97284d..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Lookup.java
- *
- * 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.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;
-
-/**
- * The Lookup module.
- *
- * @author Erik C. Thauvin
- * @created 2014-04-26
- * @since 1.0
- */
-public final class Lookup extends AbstractModule {
- /**
- * The whois default host.
- */
- static final String WHOIS_HOST = "whois.arin.net";
-
- // Lookup command
- private static final String LOOKUP_CMD = "lookup";
-
- /**
- * The default constructor.
- */
- public Lookup(final Mobibot bot) {
- super(bot);
-
- commands.add(LOOKUP_CMD);
-
- help.add("To perform a DNS lookup query:");
- help.add(Utils.helpIndent("%c " + LOOKUP_CMD + " "));
- }
-
- /**
- * Performs a DNS lookup on the specified query.
- *
- * @param query The IP address or hostname.
- * @return The lookup query result string.
- * @throws java.net.UnknownHostException If the host is unknown.
- */
- public static String lookup(final String query)
- throws UnknownHostException {
- final StringBuilder buffer = new StringBuilder();
-
- final InetAddress[] results = InetAddress.getAllByName(query);
- String hostInfo;
-
- for (final InetAddress result : results) {
- if (result.getHostAddress().equals(query)) {
- hostInfo = result.getHostName();
-
- if (hostInfo.equals(query)) {
- throw new UnknownHostException();
- }
- } else {
- hostInfo = result.getHostAddress();
- }
-
- if (buffer.length() > 0) {
- buffer.append(", ");
- }
-
- buffer.append(hostInfo);
- }
-
- return buffer.toString();
- }
-
- /**
- * Performs a whois IP query.
- *
- * @param query The IP address.
- * @return The IP whois data, if any.
- * @throws java.io.IOException If a connection error occurs.
- */
- private static String[] whois(final String query)
- throws IOException {
- return whois(query, WHOIS_HOST);
- }
-
- /**
- * Performs a whois IP query.
- *
- * @param query The IP address.
- * @param host The whois host.
- * @return The IP whois data, if any.
- * @throws java.io.IOException If a connection error occurs.
- */
- public static String[] whois(final String query, final String host)
- throws IOException {
- final WhoisClient whoisClient = new WhoisClient();
- final String[] lines;
-
- try {
- whoisClient.setDefaultTimeout(Constants.CONNECT_TIMEOUT);
- whoisClient.connect(host);
- whoisClient.setSoTimeout(Constants.CONNECT_TIMEOUT);
- whoisClient.setSoLinger(false, 0);
-
- if (WHOIS_HOST.equals(host)) {
- lines = whoisClient.query("n - " + query).split("\n");
- } else {
- lines = whoisClient.query(query).split("\n");
- }
- } finally {
- whoisClient.disconnect();
- }
-
- return lines;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate) {
- if (args.matches("(\\S.)+(\\S)+")) {
- try {
- bot.send(Lookup.lookup(args));
- } catch (UnknownHostException ignore) {
- if (args.matches(
- "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\."
- + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) {
- try {
- final String[] lines = Lookup.whois(args);
-
- if ((lines != null) && (lines.length > 0)) {
- String line;
-
- for (final String rawLine : lines) {
- line = rawLine.trim();
-
- if ((line.length() > 0) && (line.charAt(0) != '#')) {
- bot.send(line);
- }
- }
- } else {
- bot.send("Unknown host.");
- }
- } catch (IOException ioe) {
- bot.getLogger().debug("Unable to perform whois IP lookup: {}", args, ioe);
- bot.send("Unable to perform whois IP lookup: " + ioe.getMessage());
- }
- } else {
- bot.send("Unknown host.");
- }
- }
- } else {
- helpResponse(sender, true);
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt
new file mode 100644
index 0000000..cca419e
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt
@@ -0,0 +1,169 @@
+/*
+ * Lookup.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.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
+
+/**
+ * The Lookup module.
+ */
+class Lookup(bot: Mobibot) : AbstractModule(bot) {
+ override fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ ) {
+ if (args.matches("(\\S.)+(\\S)+".toRegex())) {
+ with(bot) {
+ try {
+ send(lookup(args))
+ } catch (ignore: UnknownHostException) {
+ if (args.matches(
+ ("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
+ "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
+ "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").toRegex()
+ )
+ ) {
+ try {
+ val lines = whois(args)
+ if (lines.isNotEmpty()) {
+ var line: String
+ for (rawLine in lines) {
+ line = rawLine.trim()
+ if (line.isNotEmpty() && line[0] != '#') {
+ send(line)
+ }
+ }
+ } else {
+ send("Unknown host.")
+ }
+ } catch (ioe: IOException) {
+ logger.debug("Unable to perform whois IP lookup: {}", args, ioe)
+ send("Unable to perform whois IP lookup: ${ioe.message}")
+ }
+ } else {
+ send("Unknown host.")
+ }
+ }
+ }
+ } else {
+ helpResponse(sender, true)
+ }
+ }
+
+ companion object {
+ /**
+ * The whois default host.
+ */
+ const val WHOIS_HOST = "whois.arin.net"
+
+ // Lookup command
+ private const val LOOKUP_CMD = "lookup"
+
+ /**
+ * Performs a DNS lookup on the specified query.
+ */
+ @JvmStatic
+ @Throws(UnknownHostException::class)
+ fun lookup(query: String): String {
+ val buffer = StringBuilder()
+ val results = InetAddress.getAllByName(query)
+ var hostInfo: String
+ for (result in results) {
+ if (result.hostAddress == query) {
+ hostInfo = result.hostName
+ if (hostInfo == query) {
+ throw UnknownHostException()
+ }
+ } else {
+ hostInfo = result.hostAddress
+ }
+ if (buffer.isNotEmpty()) {
+ buffer.append(", ")
+ }
+ buffer.append(hostInfo)
+ }
+ return buffer.toString()
+ }
+
+ /**
+ * Performs a whois IP query.
+ *
+ * @param query The IP address.
+ * @return The IP whois data, if any.
+ * @throws java.io.IOException If a connection error occurs.
+ */
+ @Throws(IOException::class)
+ private fun whois(query: String): Array {
+ return whois(query, WHOIS_HOST)
+ }
+
+ /**
+ * Performs a whois IP query.
+ */
+ @JvmStatic
+ @Throws(IOException::class)
+ fun whois(query: String, host: String): Array {
+ val whoisClient = WhoisClient()
+ val lines: Array
+ with(whoisClient) {
+ try {
+ defaultTimeout = Constants.CONNECT_TIMEOUT
+ connect(host)
+ soTimeout = Constants.CONNECT_TIMEOUT
+ setSoLinger(false, 0)
+ lines = if (WHOIS_HOST == host) {
+ query("n - $query").split("\n").toTypedArray()
+ } else {
+ query(query).split("\n").toTypedArray()
+ }
+ } finally {
+ disconnect()
+ }
+ }
+ return lines
+ }
+ }
+
+ init {
+ commands.add(LOOKUP_CMD)
+ help.add("To perform a DNS lookup query:")
+ help.add(Utils.helpIndent("%c $LOOKUP_CMD "))
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java
deleted file mode 100644
index 13ab2fa..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * ModuleException.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.modules;
-
-import net.thauvin.erik.mobibot.Utils;
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * The ModuleException class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-07
- * @since 1.0
- */
-public class ModuleException extends Exception {
- private static final long serialVersionUID = 1L;
-
- private final String debugMessage;
-
- /**
- * Creates a new exception.
- *
- * @param message The exception message.
- */
- ModuleException(final String message) {
- super(message);
- this.debugMessage = message;
- }
-
- /**
- * Creates a new exception.
- *
- * @param debugMessage The debug message.
- * @param message The exception message.
- * @param cause The cause.
- */
- ModuleException(final String debugMessage, final String message, final Throwable cause) {
- super(message, cause);
- this.debugMessage = debugMessage;
- }
-
- /**
- * Creates a new exception.
- *
- * @param debugMessage The debug message.
- * @param message The exception message.
- */
- ModuleException(final String debugMessage, final String message) {
- super(message);
- this.debugMessage = debugMessage;
- }
-
- /**
- * Returns the debug message.
- *
- * @return The debug message.
- */
- String getDebugMessage() {
- return debugMessage;
- }
-
- /**
- * Return the sanitized message (e.g. remove API keys, etc.)
- *
- * @param sanitize The words to sanitize.
- * @return The sanitized message.
- */
- String getSanitizedMessage(final String... sanitize) {
- final String[] obfuscate = new String[sanitize.length];
- for (int i = 0; i < sanitize.length; i++) {
- obfuscate[i] = Utils.obfuscate(sanitize[i]);
- }
- return getCause().getClass().getName() + ": " + StringUtils.replaceEach(getCause().getMessage(),
- sanitize,
- obfuscate);
- }
-
- /**
- * Return true if the exception has a cause.
- *
- * @return true or false
- */
- @SuppressWarnings("unused")
- boolean hasCause() {
- return getCause() != null;
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt
new file mode 100644
index 0000000..3b10ada
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/ModuleException.kt
@@ -0,0 +1,89 @@
+/*
+ * ModuleException.kit
+ *
+ * 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.modules
+
+import net.thauvin.erik.mobibot.Utils
+import org.apache.commons.lang3.StringUtils
+
+/**
+ * The `ModuleException` class.
+ */
+class ModuleException : Exception {
+ /**
+ * Returns the debug message.
+ */
+ val debugMessage: String?
+
+ /**
+ * Creates a new exception.
+ */
+ constructor(message: String?) : super(message) {
+ debugMessage = message
+ }
+
+ /**
+ * Creates a new exception.
+ */
+ constructor(debugMessage: String?, message: String?, cause: Throwable?) : super(message, cause) {
+ this.debugMessage = debugMessage
+ }
+
+ /**
+ * Creates a new exception.
+ */
+ constructor(debugMessage: String?, message: String?) : super(message) {
+ this.debugMessage = debugMessage
+ }
+
+ /**
+ * Return the sanitized message (e.g. remove API keys, etc.)
+ */
+ fun getSanitizedMessage(vararg sanitize: String?): String {
+ val obfuscate = arrayOfNulls(sanitize.size)
+ for (i in sanitize.indices) {
+ obfuscate[i] = Utils.obfuscate(sanitize[i])
+ }
+ return cause!!.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate)
+ }
+
+ /**
+ * Return `true` if the exception has a cause.
+ */
+ @Suppress("unused")
+ fun hasCause(): Boolean {
+ return cause != null
+ }
+
+ companion object {
+ private const val serialVersionUID = 1L
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java
deleted file mode 100644
index 2e10363..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Ping.java
- *
- * 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.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;
-
-/**
- * The Ping module.
- *
- * @author Erik C. Thauvin
- * @created 2016-07-02
- * @since 1.0
- */
-public class Ping extends AbstractModule {
- /**
- * The ping responses.
- */
- static final List PINGS =
- Arrays.asList(
- "is barely alive.",
- "is trying to stay awake.",
- "has gone fishing.",
- "is somewhere over the rainbow.",
- "has fallen and can't get up.",
- "is running. You better go chase it.",
- "has just spontaneously combusted.",
- "is talking to itself... don't interrupt. That's rude.",
- "is bartending at an AA meeting.",
- "is hibernating.",
- "is saving energy: apathetic mode activated.",
- "is busy. Go away!");
- /**
- * The ping command.
- */
- private static final String PING_CMD = "ping";
-
- /**
- * The default constructor.
- */
- public Ping(final Mobibot bot) {
- super(bot);
-
- commands.add(PING_CMD);
-
- help.add("To ping the bot:");
- help.add(Utils.helpIndent("%c " + PING_CMD));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate) {
- final SecureRandom r = new SecureRandom();
- bot.action(PINGS.get(r.nextInt(PINGS.size())));
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt
new file mode 100644
index 0000000..980ece9
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt
@@ -0,0 +1,90 @@
+/*
+ * Ping.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.modules
+
+import net.thauvin.erik.mobibot.Mobibot
+import net.thauvin.erik.mobibot.Utils
+import kotlin.random.Random
+
+/**
+ * The Ping module.
+ */
+class Ping(bot: Mobibot) : AbstractModule(bot) {
+ /**
+ * {@inheritDoc}
+ */
+ override fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ ) {
+ bot.action(randomPing())
+ }
+
+ companion object {
+ /**
+ * The ping responses.
+ */
+ @JvmField
+ val PINGS = listOf(
+ "is barely alive.",
+ "is trying to stay awake.",
+ "has gone fishing.",
+ "is somewhere over the rainbow.",
+ "has fallen and can't get up.",
+ "is running. You better go chase it.",
+ "has just spontaneously combusted.",
+ "is talking to itself... don't interrupt. That's rude.",
+ "is bartending at an AA meeting.",
+ "is hibernating.",
+ "is saving energy: apathetic mode activated.",
+ "is busy. Go away!"
+ )
+
+ @JvmStatic
+ fun randomPing(): String {
+ return PINGS[Random.nextInt(PINGS.size)]
+ }
+
+ /**
+ * The ping command.
+ */
+ private const val PING_CMD = "ping"
+ }
+
+ init {
+ commands.add(PING_CMD)
+ help.add("To ping the bot:")
+ help.add(Utils.helpIndent("%c $PING_CMD"))
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt
index 68bf1e3..9bcc1ad 100644
--- a/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt
@@ -34,9 +34,6 @@ 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.red
import kotlin.random.Random
@@ -96,21 +93,23 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) {
}
}
- override fun commandResponse(sender: String, cmd: String, args: String?, isPrivate: Boolean) {
+ override fun commandResponse(sender: String, cmd: String, args: String, isPrivate: Boolean) {
val hand = Hands.valueOf(cmd.toUpperCase())
val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)]
- when {
- hand == botHand -> {
- bot.send("${green(hand.name)} vs. ${green(botHand.name)}")
- bot.action("tied.")
- }
- hand.beats(botHand) -> {
- bot.send("${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)}")
- bot.action("lost.")
- }
- else -> {
- bot.send("${green(botHand.name)} ${bold(botHand.action)} ${red(hand.name)}")
- bot.action("wins.")
+ with(bot) {
+ when {
+ hand == botHand -> {
+ send("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)}")
+ action("tied.")
+ }
+ hand.beats(botHand) -> {
+ send("${Utils.green(hand.name)} ${Utils.bold(hand.action)} ${Utils.red(botHand.name)}")
+ action("lost.")
+ }
+ else -> {
+ send("${Utils.green(botHand.name)} ${Utils.bold(botHand.action)} ${Utils.red(hand.name)}")
+ action("wins.")
+ }
}
}
}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java
deleted file mode 100644
index 4e5c1c9..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * StockQuote.java
- *
- * 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.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-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.NoticeMessage;
-import net.thauvin.erik.mobibot.msg.PublicMessage;
-import org.apache.commons.lang3.StringUtils;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The StockQuote module.
- *
- * @author Erik C. Thauvin
- * @created Feb 7, 2004
- * @since 1.0
- */
-public final class StockQuote extends ThreadedModule {
- /**
- * The Alpha Advantage property key.
- */
- static final String ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key";
- /**
- * The Invalid Symbol error string.
- */
- static final String INVALID_SYMBOL = "Invalid symbol.";
- // Alpha Advantage URL
- private static final String ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function=";
- // Quote command
- private static final String STOCK_CMD = "stock";
-
- /**
- * Creates a new {@link StockQuote} instance.
- */
- public StockQuote(final Mobibot bot) {
- super(bot);
- commands.add(STOCK_CMD);
-
- help.add("To retrieve a stock quote:");
- help.add(Utils.helpIndent("%c " + STOCK_CMD + " "));
-
- initProperties(ALPHAVANTAGE_API_KEY_PROP);
- }
-
- @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE",
- justification = "false positive?")
- private static JSONObject getJsonResponse(final String response, final String debugMessage)
- throws ModuleException {
- if (StringUtils.isNotBlank(response)) {
- final JSONObject json = new JSONObject(response);
-
- try {
- final String info = json.getString("Information");
- if (!info.isEmpty()) {
- throw new ModuleException(debugMessage, Utils.unescapeXml(info));
- }
- } catch (JSONException ignore) {
- // Do nothing
- }
-
- try {
- final String error = json.getString("Note");
- if (!error.isEmpty()) {
- throw new ModuleException(debugMessage, Utils.unescapeXml(error));
- }
- } catch (JSONException ignore) {
- // Do nothing
- }
-
- try {
- final String error = json.getString("Error Message");
- if (!error.isEmpty()) {
- throw new ModuleException(debugMessage, Utils.unescapeXml(error));
- }
- } catch (JSONException ignore) {
- // Do nothing
- }
-
- return json;
- } else {
- throw new ModuleException(debugMessage, "Empty Response.");
- }
- }
-
- /**
- * Retrieves a stock quote.
- *
- * @param symbol The stock symbol.
- * @return The {@link Message} array containing the stock quote.
- * @throws ModuleException If an errors occurs.
- */
- @SuppressWarnings({ "PMD.CloseResource" })
- static List 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.");
- }
-
- if (StringUtils.isNotBlank(symbol)) {
- final String debugMessage = "getQuote(" + symbol + ')';
- final ArrayList messages = new ArrayList<>();
-
- String response;
- try {
- // Search for symbol/keywords
- response = Utils.urlReader(new URL(
- ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + "&apikey="
- + Utils.encodeUrl(apiKey)));
-
- JSONObject json = getJsonResponse(response, debugMessage);
-
- final JSONArray symbols = json.getJSONArray("bestMatches");
- if (symbols.isEmpty()) {
- messages.add(new ErrorMessage(INVALID_SYMBOL));
- return messages;
- }
-
- final JSONObject symbolInfo = symbols.getJSONObject(0);
-
- // Get quote for symbol
- response = Utils.urlReader(new URL(
- ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + Utils.encodeUrl(symbolInfo.getString("1. symbol"))
- + "&apikey=" + Utils.encodeUrl(apiKey)));
-
- json = getJsonResponse(response, debugMessage);
-
- final JSONObject quote = json.getJSONObject("Global Quote");
-
- if (quote.isEmpty()) {
- messages.add(new ErrorMessage(INVALID_SYMBOL));
- return messages;
- }
-
- messages.add(new PublicMessage(
- "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 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"))));
- messages.add(new NoticeMessage(
- " 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);
- }
- return messages;
- } else {
- throw new ModuleException(INVALID_SYMBOL);
- }
- }
-
- /**
- * Returns the specified stock quote from Alpha Avantage.
- */
- @Override
- void run(final String sender, final String cmd, final String symbol, final boolean isPrivate) {
- if (StringUtils.isNotBlank(symbol)) {
- try {
- final List messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP));
- for (final Message msg : messages) {
- bot.send(sender, msg);
- }
- } catch (ModuleException e) {
- bot.getLogger().warn(e.getDebugMessage(), e);
- bot.send(e.getMessage());
- }
- } else {
- helpResponse(sender, isPrivate);
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt
new file mode 100644
index 0000000..bcf1838
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt
@@ -0,0 +1,214 @@
+/*
+ * StockQuote.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.modules
+
+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.NoticeMessage
+import net.thauvin.erik.mobibot.msg.PublicMessage
+import org.apache.commons.lang3.StringUtils
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.IOException
+import java.net.URL
+import java.util.*
+
+/**
+ * The StockQuote module.
+ */
+class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
+ /**
+ * Returns the specified stock quote from Alpha Avantage.
+ */
+ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
+ with(bot) {
+ if (StringUtils.isNotBlank(args)) {
+ try {
+ val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP])
+ for (msg in messages) {
+ send(sender, msg)
+ }
+ } catch (e: ModuleException) {
+ logger.warn(e.debugMessage, e)
+ send(e.message)
+ }
+ } else {
+ helpResponse(sender, isPrivate)
+ }
+ }
+ }
+
+ companion object {
+ /**
+ * The Alpha Advantage property key.
+ */
+ const val ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key"
+
+ /**
+ * The Invalid Symbol error string.
+ */
+ const val INVALID_SYMBOL = "Invalid symbol."
+
+ // Alpha Advantage URL
+ private const val ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function="
+
+ // Quote command
+ private const val STOCK_CMD = "stock"
+
+ @Throws(ModuleException::class)
+ private fun getJsonResponse(response: String, debugMessage: String): JSONObject {
+ return if (StringUtils.isNotBlank(response)) {
+ val json = JSONObject(response)
+ try {
+ val info = json.getString("Information")
+ if (info.isNotEmpty()) {
+ throw ModuleException(debugMessage, Utils.unescapeXml(info))
+ }
+ } catch (ignore: JSONException) {
+ // Do nothing
+ }
+ try {
+ var error = json.getString("Note")
+ if (error.isNotEmpty()) {
+ throw ModuleException(debugMessage, Utils.unescapeXml(error))
+ }
+ error = json.getString("Error Message")
+ if (error.isNotEmpty()) {
+ throw ModuleException(debugMessage, Utils.unescapeXml(error))
+ }
+ } catch (ignore: JSONException) {
+ // Do nothing
+ }
+ json
+ } else {
+ throw ModuleException(debugMessage, "Empty Response.")
+ }
+ }
+
+ /**
+ * Retrieves a stock quote.
+ */
+ @JvmStatic
+ @Throws(ModuleException::class)
+ fun getQuote(symbol: String, apiKey: String?): List {
+ if (StringUtils.isBlank(apiKey)) {
+ throw ModuleException("${STOCK_CMD.capitalize()} is disabled. The API key is missing.")
+ }
+ return if (StringUtils.isNotBlank(symbol)) {
+ val debugMessage = "getQuote($symbol)"
+ val messages = ArrayList()
+ var response: String
+ try {
+ with(messages) {
+ // Search for symbol/keywords
+ response = Utils.urlReader(
+ URL(
+ "${ALAPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + "&apikey="
+ + Utils.encodeUrl(apiKey)
+ )
+ )
+ var json = getJsonResponse(response, debugMessage)
+ val symbols = json.getJSONArray("bestMatches")
+ if (symbols.isEmpty) {
+ messages.add(ErrorMessage(INVALID_SYMBOL))
+ } else {
+ val symbolInfo = symbols.getJSONObject(0)
+
+ // Get quote for symbol
+ response = Utils.urlReader(
+ URL(
+ "${ALAPHAVANTAGE_URL}GLOBAL_QUOTE&symbol="
+ + Utils.encodeUrl(symbolInfo.getString("1. symbol"))
+ + "&apikey=" + Utils.encodeUrl(apiKey)
+ )
+ )
+ json = getJsonResponse(response, debugMessage)
+ val quote = json.getJSONObject("Global Quote")
+ if (quote.isEmpty) {
+ add(ErrorMessage(INVALID_SYMBOL))
+ return messages
+ }
+ add(
+ PublicMessage(
+ "Symbol: " + Utils.unescapeXml(quote.getString("01. symbol"))
+ + " [" + Utils.unescapeXml(symbolInfo.getString("2. name")) + ']'
+ )
+ )
+ add(PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price"))))
+ add(
+ PublicMessage(
+ " Previous: " + Utils.unescapeXml(quote.getString("08. previous close"))
+ )
+ )
+ add(NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open"))))
+ add(NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high"))))
+ add(NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low"))))
+ add(
+ NoticeMessage(
+ " Volume: " + Utils.unescapeXml(quote.getString("06. volume"))
+ )
+ )
+ add(
+ NoticeMessage(
+ " Latest: "
+ + Utils.unescapeXml(quote.getString("07. latest trading day"))
+ )
+ )
+ add(
+ NoticeMessage(
+ " Change: " + Utils.unescapeXml(quote.getString("09. change"))
+ + " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']'
+ )
+ )
+ }
+ }
+ } catch (e: IOException) {
+ throw ModuleException(debugMessage, "An IO error has occurred retrieving a stock quote.", e)
+ } catch (e: NullPointerException) {
+ throw ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e)
+ }
+ messages
+ } else {
+ throw ModuleException(INVALID_SYMBOL)
+ }
+ }
+ }
+
+ init {
+ commands.add(STOCK_CMD)
+ help.add("To retrieve a stock quote:")
+ help.add(Utils.helpIndent("%c $STOCK_CMD "))
+ initProperties(ALPHAVANTAGE_API_KEY_PROP)
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt
similarity index 64%
rename from src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java
rename to src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt
index 11d3cec..97a07e5 100644
--- a/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.java
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/ThreadedModule.kt
@@ -29,40 +29,34 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package net.thauvin.erik.mobibot.modules
-package net.thauvin.erik.mobibot.modules;
-
-import net.thauvin.erik.mobibot.Mobibot;
+import net.thauvin.erik.mobibot.Mobibot
/**
- * The ThreadedModule class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-03
- * @since 1.0
+ * The `ThreadedModule` class.
*/
-public abstract class ThreadedModule extends AbstractModule {
- ThreadedModule(final Mobibot bot) {
- super(bot);
- }
-
- @Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate) {
- if (isEnabled() && args.length() > 0) {
- new Thread(() -> run(sender, cmd, args, isPrivate)).start();
+abstract class ThreadedModule(bot: Mobibot) : AbstractModule(bot) {
+ override fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ ) {
+ if (isEnabled && args.isNotEmpty()) {
+ Thread { run(sender, cmd, args, isPrivate) }.start()
} else {
- helpResponse(sender, isPrivate);
+ helpResponse(sender, isPrivate)
}
}
/**
* Runs the thread.
*/
- abstract void run(String sender,
- @SuppressWarnings("unused") String cmd,
- String args,
- boolean isPrivate);
+ abstract fun run(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ )
}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java
deleted file mode 100644
index 87446a5..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Twitter.java
- *
- * 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.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.ReleaseInfo;
-import net.thauvin.erik.mobibot.TwitterTimer;
-import net.thauvin.erik.mobibot.Utils;
-import net.thauvin.erik.mobibot.commands.links.LinksMgr;
-import net.thauvin.erik.mobibot.entries.EntryLink;
-import net.thauvin.erik.mobibot.msg.Message;
-import net.thauvin.erik.mobibot.msg.NoticeMessage;
-import org.apache.commons.lang3.StringUtils;
-import twitter4j.DirectMessage;
-import twitter4j.Status;
-import twitter4j.TwitterFactory;
-import twitter4j.conf.ConfigurationBuilder;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import static org.apache.commons.lang3.StringUtils.isNotBlank;
-
-/**
- * The Twitter module.
- *
- * @author Erik C. Thauvin
- * @created Sept 10, 2008
- * @since 1.0
- */
-public final class Twitter extends ThreadedModule {
- // Property keys
- static final String AUTOPOST_PROP = "twitter-auto-post";
- static final String CONSUMER_KEY_PROP = "twitter-consumerKey";
- static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret";
- static final String HANDLE_PROP = "twitter-handle";
- static final String TOKEN_PROP = "twitter-token";
- static final String TOKEN_SECRET_PROP = "twitter-tokenSecret";
- // Twitter command
- private static final String TWITTER_CMD = "twitter";
-
- // Twitter auto-posts.
- private final Set entries = new HashSet<>();
-
- /**
- * Creates a new {@link Twitter} instance.
- */
- public Twitter(final Mobibot bot) {
- super(bot);
-
- commands.add(TWITTER_CMD);
-
- help.add("To post to Twitter:");
- help.add(Utils.helpIndent("%c " + TWITTER_CMD + " "));
-
- properties.put(AUTOPOST_PROP, "false");
- initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP);
- }
-
- /**
- * Posts on Twitter.
- *
- * @param consumerKey The consumer key.
- * @param consumerSecret The consumer secret.
- * @param token The token.
- * @param tokenSecret The token secret.
- * @param handle The Twitter handle (dm) or nickname.
- * @param message The message to post.
- * @param isDm The direct message flag.
- * @return The confirmation {@link Message}.
- * @throws ModuleException If an error occurs while posting.
- */
- static Message twitterPost(final String consumerKey,
- final String consumerSecret,
- final String token,
- final String tokenSecret,
- final String handle,
- final String message,
- final boolean isDm) throws ModuleException {
- try {
- final ConfigurationBuilder cb = new ConfigurationBuilder();
- cb.setDebugEnabled(true).setOAuthConsumerKey(consumerKey).setOAuthConsumerSecret(consumerSecret)
- .setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret);
-
- final TwitterFactory tf = new TwitterFactory(cb.build());
- final twitter4j.Twitter twitter = tf.getInstance();
-
- if (!isDm) {
- final Status status = twitter.updateStatus(message);
- return new NoticeMessage("You message was posted to https://twitter.com/" + twitter.getScreenName()
- + "/statuses/" + status.getId());
- } else {
- final DirectMessage dm = twitter.sendDirectMessage(handle, message);
- return new NoticeMessage(dm.getText());
- }
- } catch (Exception e) {
- throw new ModuleException("twitterPost(" + message + ")", "An error has occurred: " + e.getMessage(), e);
- }
- }
-
- /**
- * Add an entry to be posted on Twitter.
- *
- * @param index The entry index.
- */
- public final void addEntry(final int index) {
- entries.add(index);
- }
-
- public final int entriesCount() {
- return entries.size();
- }
-
- public String getHandle() {
- return properties.get(HANDLE_PROP);
- }
-
- public final boolean hasEntry(final int index) {
- return entries.contains(index);
- }
-
- public boolean isAutoPost() {
- return isEnabled() && Boolean.parseBoolean(properties.get(AUTOPOST_PROP));
- }
-
- @Override
- boolean isValidProperties() {
- for (final String s : getPropertyKeys()) {
- if (!AUTOPOST_PROP.equals(s) && !HANDLE_PROP.equals(s) && StringUtils.isBlank(properties.get(s))) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Send a notification to the registered Twitter handle.
- *
- * @param msg The twitter message.
- */
- public final void notification(final String msg) {
- if (isEnabled() && isNotBlank(getHandle())) {
- new Thread(() -> {
- try {
- post(String.format(msg, bot.getName(), ReleaseInfo.VERSION, bot.getChannel()), true);
- } catch (ModuleException e) {
- bot.getLogger().warn("Failed to notify @{}: {}", getHandle(), msg, e);
- }
- }).start();
- }
- }
-
- /**
- * Posts on Twitter.
- *
- * @param message The message to post.
- * @param isDm The direct message flag.
- * @throws ModuleException If an error occurs while posting.
- */
- public void post(final String message, final boolean isDm)
- throws ModuleException {
- post(properties.get(HANDLE_PROP), message, isDm);
- }
-
- /**
- * Posts on Twitter.
- *
- * @param handle The Twitter handle (dm) or nickname.
- * @param message The message to post.
- * @param isDm The direct message flag.
- * @return The {@link Message} to send back.
- * @throws ModuleException If an error occurs while posting.
- */
- public Message post(final String handle, final String message, final boolean isDm)
- throws ModuleException {
- return twitterPost(properties.get(CONSUMER_KEY_PROP),
- properties.get(CONSUMER_SECRET_PROP),
- properties.get(TOKEN_PROP),
- properties.get(TOKEN_SECRET_PROP),
- handle,
- message,
- isDm);
- }
-
- /**
- * Post an entry to twitter.
- *
- * @param index The post entry index.
- */
- @SuppressFBWarnings("SUI_CONTAINS_BEFORE_REMOVE")
- public final void postEntry(final int index) {
- if (isAutoPost() && hasEntry(index) && LinksMgr.getEntriesCount() >= index) {
- final EntryLink entry = LinksMgr.getEntry(index);
- final String msg =
- entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + bot.getChannel();
- new Thread(() -> {
- try {
- if (bot.getLogger().isDebugEnabled()) {
- bot.getLogger().debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1);
- }
- post(msg, false);
- } catch (ModuleException e) {
- bot.getLogger().warn("Failed to post entry on Twitter.", e);
- }
- }).start();
- removeEntry(index);
- }
- }
-
- public void queueEntry(final int index) {
- if (isAutoPost()) {
- addEntry(index);
- bot.getLogger().debug("Scheduling ${Constants.LINK_CMD}${index + 1} for posting on Twitter.");
- bot.getTimer().schedule(new TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L);
- }
- }
-
- public final void removeEntry(final int index) {
- entries.remove(index);
- }
-
- /**
- * Posts to twitter.
- */
- @Override
- void run(final String sender, final String cmd, final String message, final boolean isPrivate) {
- try {
- bot.send(sender,
- post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMsg(),
- isPrivate);
- } catch (ModuleException e) {
- bot.getLogger().warn(e.getDebugMessage(), e);
- bot.send(sender, e.getMessage(), isPrivate);
- }
- }
-
- /**
- * Post all the entries to Twitter on shutdown.
- */
- public final void shutdown() {
- if (isAutoPost()) {
- for (final int index : entries) {
- postEntry(index);
- }
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt
new file mode 100644
index 0000000..07de9f9
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt
@@ -0,0 +1,244 @@
+/*
+ * Twitter.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.modules
+
+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.links.LinksMgr.Companion.entriesCount
+import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getEntry
+import net.thauvin.erik.mobibot.msg.Message
+import net.thauvin.erik.mobibot.msg.NoticeMessage
+import org.apache.commons.lang3.StringUtils
+import twitter4j.TwitterException
+import twitter4j.TwitterFactory
+import twitter4j.conf.ConfigurationBuilder
+import java.util.*
+
+/**
+ * The Twitter module.
+ */
+class Twitter(bot: Mobibot) : ThreadedModule(bot) {
+ // Twitter auto-posts.
+ private val entries: MutableSet = HashSet()
+
+ /**
+ * Add an entry to be posted on Twitter.
+ */
+ private fun addEntry(index: Int) {
+ entries.add(index)
+ }
+
+ fun entriesCount(): Int {
+ return entries.size
+ }
+
+ private val handle: String?
+ get() = properties[HANDLE_PROP]
+
+ private fun hasEntry(index: Int): Boolean {
+ return entries.contains(index)
+ }
+
+ val isAutoPost: Boolean
+ get() = isEnabled && properties[AUTOPOST_PROP].toBoolean()
+
+ override val isValidProperties: Boolean
+ get() {
+ for (s in propertyKeys) {
+ if (AUTOPOST_PROP != s && HANDLE_PROP != s && properties[s]!!.isBlank()) {
+ return false
+ }
+ }
+ return true
+ }
+
+ /**
+ * Send a notification to the registered Twitter handle.
+ */
+ fun notification(msg: String) {
+ with(bot) {
+ if (isEnabled && StringUtils.isNotBlank(handle)) {
+ Thread {
+ try {
+ post(message = msg, isDm = true)
+ if (logger.isDebugEnabled) {
+ logger.debug("Notified @{}: {}", handle, msg)
+ }
+ } catch (e: ModuleException) {
+ logger.warn("Failed to notify @{}: {}", handle, msg, e)
+ }
+ }.start()
+ }
+ }
+ }
+
+ /**
+ * Posts on Twitter.
+ */
+ @Throws(ModuleException::class)
+ fun post(handle: String = "${properties[HANDLE_PROP]}", message: String, isDm: Boolean): Message {
+ return twitterPost(
+ properties[CONSUMER_KEY_PROP],
+ properties[CONSUMER_SECRET_PROP],
+ properties[TOKEN_PROP],
+ properties[TOKEN_SECRET_PROP],
+ handle,
+ message,
+ isDm
+ )
+ }
+
+ /**
+ * Post an entry to twitter.
+ */
+ fun postEntry(index: Int) {
+ with(bot) {
+ if (isAutoPost && hasEntry(index) && entriesCount >= index) {
+ val entry = getEntry(index)
+ val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel"
+ Thread {
+ try {
+ if (logger.isDebugEnabled) {
+ logger.debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1)
+ }
+ post(message = msg, isDm = false)
+ } catch (e: ModuleException) {
+ logger.warn("Failed to post entry on Twitter.", e)
+ }
+ }.start()
+ removeEntry(index)
+ }
+ }
+ }
+
+ fun queueEntry(index: Int) {
+ if (isAutoPost) {
+ addEntry(index)
+ if (bot.logger.isDebugEnabled) {
+ bot.logger.debug("Scheduling {}{} for posting on Twitter.", Constants.LINK_CMD, index + 1)
+ }
+ bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L)
+ }
+ }
+
+ fun removeEntry(index: Int) {
+ entries.remove(index)
+ }
+
+ /**
+ * Posts to twitter.
+ */
+ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
+ with(bot) {
+ try {
+ send(
+ sender,
+ post(sender, "$args (by $sender on $channel)", false).msg,
+ isPrivate
+ )
+ } catch (e: ModuleException) {
+ logger.warn(e.debugMessage, e)
+ send(sender, e.message, isPrivate)
+ }
+ }
+ }
+
+ /**
+ * Post all the entries to Twitter on shutdown.
+ */
+ fun shutdown() {
+ if (isAutoPost) {
+ for (index in entries) {
+ postEntry(index)
+ }
+ }
+ }
+
+ companion object {
+ // Property keys
+ const val AUTOPOST_PROP = "twitter-auto-post"
+ const val CONSUMER_KEY_PROP = "twitter-consumerKey"
+ const val CONSUMER_SECRET_PROP = "twitter-consumerSecret"
+ const val HANDLE_PROP = "twitter-handle"
+ const val TOKEN_PROP = "twitter-token"
+ const val TOKEN_SECRET_PROP = "twitter-tokenSecret"
+
+ // Twitter command
+ private const val TWITTER_CMD = "twitter"
+
+ /**
+ * Posts on Twitter.
+ */
+ @JvmStatic
+ @Throws(ModuleException::class)
+ fun twitterPost(
+ consumerKey: String?,
+ consumerSecret: String?,
+ token: String?,
+ tokenSecret: String?,
+ handle: String?,
+ message: String,
+ isDm: Boolean
+ ): Message {
+ return try {
+ val cb = ConfigurationBuilder()
+ cb.setDebugEnabled(true)
+ .setOAuthConsumerKey(consumerKey)
+ .setOAuthConsumerSecret(consumerSecret)
+ .setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret)
+ val tf = TwitterFactory(cb.build())
+ val twitter = tf.instance
+ if (!isDm) {
+ val status = twitter.updateStatus(message)
+ NoticeMessage(
+ "You message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}"
+ )
+ } else {
+ val dm = twitter.sendDirectMessage(handle, message)
+ NoticeMessage(dm.text)
+ }
+ } catch (e: TwitterException) {
+ throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e)
+ }
+ }
+ }
+
+ init {
+ commands.add(TWITTER_CMD)
+ help.add("To post to Twitter:")
+ help.add(Utils.helpIndent("%c $TWITTER_CMD "))
+ properties[AUTOPOST_PROP] = "false"
+ initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP)
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java
index b1be81e..fe43ef5 100644
--- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java
@@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.modules;
import net.thauvin.erik.mobibot.Mobibot;
import net.thauvin.erik.mobibot.Utils;
+import org.jetbrains.annotations.NotNull;
import java.security.SecureRandom;
@@ -71,9 +72,9 @@ public final class War extends AbstractModule {
* {@inheritDoc}
*/
@Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
+ public void commandResponse(@NotNull final String sender,
+ @NotNull final String cmd,
+ @NotNull final String args,
final boolean isPrivate) {
final SecureRandom r = new SecureRandom();
@@ -84,20 +85,21 @@ public final class War extends AbstractModule {
i = r.nextInt(WAR_DECK.length);
y = r.nextInt(WAR_DECK.length);
- bot.send(sender + " drew the " + bold(WAR_DECK[i]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)]));
- bot.action("drew the " + bold(WAR_DECK[y]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)]));
+ getBot().send(sender + " drew the " + bold(WAR_DECK[i]) + " of "
+ + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)]));
+ getBot().action("drew the " + bold(WAR_DECK[y]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)]));
if (i != y) {
break;
}
- bot.send("This means " + bold("WAR") + '!');
+ getBot().send("This means " + bold("WAR") + '!');
}
if (i < y) {
- bot.action("lost.");
+ getBot().action("lost.");
} else {
- bot.action("wins.");
+ getBot().action("wins.");
}
}
}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java
deleted file mode 100644
index e1910ac..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Weather2.java
- *
- * 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.modules;
-
-import net.aksingh.owmjapis.api.APIException;
-import net.aksingh.owmjapis.core.OWM;
-import net.aksingh.owmjapis.model.CurrentWeather;
-import net.aksingh.owmjapis.model.param.Main;
-import net.aksingh.owmjapis.model.param.Weather;
-import net.aksingh.owmjapis.model.param.Wind;
-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.NoticeMessage;
-import net.thauvin.erik.mobibot.msg.PublicMessage;
-import org.apache.commons.lang3.StringUtils;
-import org.jibble.pircbot.Colors;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static net.thauvin.erik.mobibot.Utils.bold;
-
-/**
- * The Weather2 module.
- *
- * @author Erik C. Thauvin
- * @created 2017-04-02
- * @since 1.0
- */
-public class Weather2 extends ThreadedModule {
- /**
- * The OpenWeatherMap API Key property.
- */
- static final String OWM_API_KEY_PROP = "owm-api-key";
-
- // Weather command
- private static final String WEATHER_CMD = "weather";
-
- /**
- * Creates a new {@link Weather2} instance.
- */
- public Weather2(final Mobibot bot) {
- super(bot);
-
- commands.add(WEATHER_CMD);
-
- help.add("To display weather information:");
- help.add(Utils.helpIndent("%c " + WEATHER_CMD + " [, ]"));
- help.add("For example:");
- help.add(Utils.helpIndent("%c " + WEATHER_CMD + " paris, fr"));
- help.add("The default ISO 3166 country code is " + bold("US") + ". Zip codes supported in most countries.");
-
- initProperties(OWM_API_KEY_PROP);
- }
-
- private static OWM.Country getCountry(final String countryCode) {
- for (final OWM.Country c : OWM.Country.values()) {
- if (c.name().equalsIgnoreCase(countryCode)) {
- return c;
- }
- }
-
- return OWM.Country.UNITED_STATES;
- }
-
- @SuppressWarnings("AvoidEscapedUnicodeCharacters")
- private static String getTemps(final Double d) {
- final double c = (d - 32) * 5 / 9;
- return Math.round(d) + " °F, " + Math.round(c) + " °C";
- }
-
- /**
- * Retrieves the weather data.
- *
- *
- *
98204
- *
London, UK
- *
- *
- * @param query The query.
- * @param apiKey The API key.
- * @return The {@link Message} array containing the weather data.
- * @throws ModuleException If an error occurs while retrieving the weather data.
- */
- static List getWeather(final String query, final String apiKey) throws ModuleException {
- if (StringUtils.isBlank(apiKey)) {
- throw new ModuleException(StringUtils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing.");
- }
-
- final OWM owm = new OWM(apiKey);
- final ArrayList messages = new ArrayList<>();
-
- owm.setUnit(OWM.Unit.IMPERIAL);
-
- if (StringUtils.isNotBlank(query)) {
- final String[] argv = query.split(",");
-
- if (argv.length >= 1 && argv.length <= 2) {
- final String country;
- final String city = argv[0].trim();
- if (argv.length > 1 && StringUtils.isNotBlank(argv[1])) {
- country = argv[1].trim();
- } else {
- country = "US";
- }
-
- try {
- final CurrentWeather cwd;
- if (city.matches("\\d+")) {
- cwd = owm.currentWeatherByZipCode(Integer.parseInt(city), getCountry(country));
- } else {
- cwd = owm.currentWeatherByCityName(city, getCountry(country));
- }
- if (cwd.hasCityName()) {
- messages.add(new PublicMessage(
- "City: " + cwd.getCityName() + " [" + StringUtils.upperCase(country) + "]"));
-
- final Main main = cwd.getMainData();
- if (main != null) {
- if (main.hasTemp()) {
- messages.add(new PublicMessage("Temperature: " + getTemps(main.getTemp())));
- }
-
- if (main.hasHumidity() && (main.getHumidity() != null)) {
- messages.add(new NoticeMessage("Humidity: " + Math.round(main.getHumidity()) + "%"));
- }
- }
-
- if (cwd.hasWindData()) {
- final Wind w = cwd.getWindData();
- if (w != null && w.hasSpeed()) {
- messages.add(new NoticeMessage("Wind: " + wind(w.getSpeed())));
- }
- }
-
- if (cwd.hasWeatherList()) {
- final StringBuilder condition = new StringBuilder("Condition:");
- final List list = cwd.getWeatherList();
- if (list != null) {
- for (final Weather w : list) {
- condition.append(' ')
- .append(StringUtils.capitalize(w.getDescription()))
- .append('.');
- }
- messages.add(new NoticeMessage(condition.toString()));
- }
- }
-
- if (cwd.hasCityId() && cwd.getCityId() != null) {
- if (cwd.getCityId() > 0) {
- messages.add(new NoticeMessage("https://openweathermap.org/city/" + cwd.getCityId(),
- Colors.GREEN));
- } else {
- messages.add(new NoticeMessage("https://openweathermap.org/find?q=" + Utils.encodeUrl(
- city + ',' + StringUtils.upperCase(country)), Colors.GREEN));
- }
- }
- }
- } catch (APIException | NullPointerException e) {
- throw new ModuleException("getWeather(" + query + ')', "Unable to perform weather lookup.", e);
- }
- }
- }
-
- if (messages.isEmpty()) {
- messages.add(new ErrorMessage("Invalid syntax."));
- }
-
- 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";
- }
-
- /**
- * Fetches the weather data from a specific city.
- */
- @Override
- void run(final String sender, final String cmd, final String args, final boolean isPrivate) {
- if (StringUtils.isNotBlank(args)) {
- try {
- final List messages = getWeather(args, properties.get(OWM_API_KEY_PROP));
- if (messages.get(0).isError()) {
- helpResponse(sender, isPrivate);
- } else {
- for (final Message msg : messages) {
- bot.send(sender, msg);
- }
- }
- } catch (ModuleException e) {
- bot.getLogger().debug(e.getDebugMessage(), e);
- bot.send(e.getMessage());
- }
- } else {
- helpResponse(sender, isPrivate);
- }
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt
new file mode 100644
index 0000000..2dea6c7
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt
@@ -0,0 +1,200 @@
+/*
+ * Weather2.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.modules
+
+import net.aksingh.owmjapis.api.APIException
+import net.aksingh.owmjapis.core.OWM
+import net.aksingh.owmjapis.core.OWM.Country
+import net.aksingh.owmjapis.model.CurrentWeather
+import net.thauvin.erik.mobibot.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.NoticeMessage
+import net.thauvin.erik.mobibot.msg.PublicMessage
+import org.apache.commons.lang3.StringUtils
+import org.jibble.pircbot.Colors
+import java.util.*
+import kotlin.math.roundToInt
+
+/**
+ * The `Weather2` module.
+ */
+class Weather2(bot: Mobibot) : ThreadedModule(bot) {
+ /**
+ * Fetches the weather data from a specific city.
+ */
+ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
+ if (StringUtils.isNotBlank(args)) {
+ try {
+ val messages = getWeather(args, properties[OWM_API_KEY_PROP])
+ if (messages[0].isError) {
+ helpResponse(sender, isPrivate)
+ } else {
+ for (msg in messages) {
+ bot.send(sender, msg)
+ }
+ }
+ } catch (e: ModuleException) {
+ bot.logger.debug(e.debugMessage, e)
+ bot.send(e.message)
+ }
+ } else {
+ helpResponse(sender, isPrivate)
+ }
+ }
+
+ companion object {
+ /**
+ * The OpenWeatherMap API Key property.
+ */
+ const val OWM_API_KEY_PROP = "owm-api-key"
+
+ // Weather command
+ private const val WEATHER_CMD = "weather"
+ private fun getCountry(countryCode: String): Country {
+ for (c in Country.values()) {
+ if (c.name.equals(countryCode, ignoreCase = true)) {
+ return c
+ }
+ }
+ return Country.UNITED_STATES
+ }
+
+ private fun getTemps(d: Double?): String {
+ val c = (d!! - 32) * 5 / 9
+ return "${d.roundToInt()} °F, ${c.roundToInt()} °C"
+ }
+
+ /**
+ * Retrieves the weather data.
+ */
+ @JvmStatic
+ @Throws(ModuleException::class)
+ fun getWeather(query: String, apiKey: String?): List {
+ if (StringUtils.isBlank(apiKey)) {
+ throw ModuleException("${WEATHER_CMD.capitalize()} is disabled. The API key is missing.")
+ }
+ val owm = OWM(apiKey!!)
+ val messages = ArrayList()
+ owm.unit = OWM.Unit.IMPERIAL
+ if (StringUtils.isNotBlank(query)) {
+ val argv = query.split(",").toTypedArray()
+ if (argv.size in 1..2) {
+ val city = argv[0].trim()
+ val country: String = if (argv.size > 1 && StringUtils.isNotBlank(argv[1])) {
+ argv[1].trim()
+ } else {
+ "US"
+ }
+ try {
+ val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) {
+ owm.currentWeatherByZipCode(city.toInt(), getCountry(country))
+ } else {
+ owm.currentWeatherByCityName(city, getCountry(country))
+ }
+ if (cwd.hasCityName()) {
+ messages.add(
+ PublicMessage("City: ${cwd.cityName} [${country.toUpperCase()}]")
+ )
+ with(cwd.mainData) {
+ if (this != null) {
+ if (hasTemp()) {
+ messages.add(PublicMessage("Temperature: ${getTemps(temp)}"))
+ }
+ if (hasHumidity() && humidity != null) {
+ messages.add(NoticeMessage("Humidity: ${(humidity!!).roundToInt()}%"))
+ }
+ }
+ }
+ if (cwd.hasWindData()) {
+ with(cwd.windData) {
+ if (this != null && hasSpeed()) {
+ messages.add(NoticeMessage("Wind: ${wind(speed)}"))
+ }
+ }
+ }
+ if (cwd.hasWeatherList()) {
+ val condition = StringBuilder("Condition:")
+ val list = cwd.weatherList
+ if (list != null) {
+ for (w in list) {
+ condition.append(' ').append(w!!.getDescription().capitalize()).append('.')
+ }
+ messages.add(NoticeMessage(condition.toString()))
+ }
+ }
+ if (cwd.hasCityId() && cwd.cityId != null) {
+ if (cwd.cityId!! > 0) {
+ messages.add(
+ NoticeMessage("https://openweathermap.org/city/${cwd.cityId}", Colors.GREEN)
+ )
+ } else {
+ messages.add(
+ NoticeMessage(
+ "https://openweathermap.org/find?q="
+ + Utils.encodeUrl("$city,${country.toUpperCase()}"),
+ Colors.GREEN
+ )
+ )
+ }
+ }
+ }
+ } catch (e: APIException) {
+ throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e)
+ } catch (e: NullPointerException) {
+ throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e)
+ }
+ }
+ }
+ if (messages.isEmpty()) {
+ messages.add(ErrorMessage("Invalid syntax."))
+ }
+ return messages
+ }
+
+ private fun wind(w: Double?): String {
+ val kmh = w!! * 1.60934
+ return "${Math.round(w)} mph, ${kmh.roundToInt()} km/h"
+ }
+ }
+
+ init {
+ commands.add(WEATHER_CMD)
+ help.add("To display weather information:")
+ help.add(Utils.helpIndent("%c $WEATHER_CMD [, ]"))
+ help.add("For example:")
+ help.add(Utils.helpIndent("%c $WEATHER_CMD paris, fr"))
+ help.add("The default ISO 3166 country code is ${Utils.bold("US")}. Zip codes supported in most countries.")
+ initProperties(OWM_API_KEY_PROP)
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java
deleted file mode 100644
index 05bcc22..0000000
--- a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * WorldTime.java
- *
- * 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.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-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;
-import org.apache.commons.lang3.StringUtils;
-
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.temporal.ChronoField;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * The WorldTime module.
- *
- * @author Erik C. Thauvin
- * @created 2014-04-27
- * @since 1.0
- */
-@SuppressWarnings("PMD.UseConcurrentHashMap")
-public final class WorldTime extends AbstractModule {
- // Beats (Internet Time) keyword
- private static final String BEATS_KEYWORD = ".beats";
- // Supported countries
- private static final Map COUNTRIES_MAP;
-
- // The Time command
- private static final String TIME_CMD = "time";
-
- static {
- // Initialize the countries map
- final Map countries = new TreeMap<>();
- countries.put("AE", "Asia/Dubai");
- countries.put("AF", "Asia/Kabul");
- countries.put("AQ", "Antarctica/South_Pole");
- countries.put("AT", "Europe/Vienna");
- countries.put("AU", "Australia/Sydney");
- countries.put("AKST", "America/Anchorage");
- countries.put("AKDT", "America/Anchorage");
- countries.put("BE", "Europe/Brussels");
- countries.put("BR", "America/Sao_Paulo");
- countries.put("CA", "America/Montreal");
- countries.put("CDT", "America/Chicago");
- countries.put("CET", "CET");
- countries.put("CH", "Europe/Zurich");
- countries.put("CN", "Asia/Shanghai");
- countries.put("CST", "America/Chicago");
- countries.put("CU", "Cuba");
- countries.put("DE", "Europe/Berlin");
- countries.put("DK", "Europe/Copenhagen");
- countries.put("EDT", "America/New_York");
- countries.put("EG", "Africa/Cairo");
- countries.put("ER", "Africa/Asmara");
- countries.put("ES", "Europe/Madrid");
- countries.put("EST", "America/New_York");
- countries.put("FI", "Europe/Helsinki");
- countries.put("FR", "Europe/Paris");
- countries.put("GB", "Europe/London");
- countries.put("GMT", "GMT");
- countries.put("GR", "Europe/Athens");
- countries.put("HK", "Asia/Hong_Kong");
- countries.put("HST", "Pacific/Honolulu");
- countries.put("IE", "Europe/Dublin");
- countries.put("IL", "Asia/Tel_Aviv");
- countries.put("IN", "Asia/Kolkata");
- countries.put("IQ", "Asia/Baghdad");
- countries.put("IR", "Asia/Tehran");
- countries.put("IS", "Atlantic/Reykjavik");
- countries.put("IT", "Europe/Rome");
- countries.put("JM", "Jamaica");
- countries.put("JP", "Asia/Tokyo");
- countries.put("LY", "Africa/Tripoli");
- countries.put("MA", "Africa/Casablanca");
- countries.put("MDT", "America/Denver");
- countries.put("MH", "Kwajalein");
- countries.put("MQ", "America/Martinique");
- countries.put("MST", "America/Denver");
- countries.put("MX", "America/Mexico_City");
- countries.put("NL", "Europe/Amsterdam");
- countries.put("NO", "Europe/Oslo");
- countries.put("NP", "Asia/Katmandu");
- countries.put("NZ", "Pacific/Auckland");
- countries.put("PDT", "America/Los_Angeles");
- countries.put("PH", "Asia/Manila");
- countries.put("PK", "Asia/Karachi");
- countries.put("PL", "Europe/Warsaw");
- countries.put("PST", "America/Los_Angeles");
- countries.put("PT", "Europe/Lisbon");
- countries.put("PR", "America/Puerto_Rico");
- countries.put("RU", "Europe/Moscow");
- countries.put("SE", "Europe/Stockholm");
- countries.put("SG", "Asia/Singapore");
- countries.put("TH", "Asia/Bangkok");
- countries.put("TM", "Asia/Ashgabat");
- countries.put("TN", "Africa/Tunis");
- countries.put("TR", "Europe/Istanbul");
- countries.put("TW", "Asia/Taipei");
- countries.put("UK", "Europe/London");
- countries.put("US", "America/New_York");
- countries.put("UTC", "UTC");
- countries.put("VA", "Europe/Vatican");
- countries.put("VE", "America/Caracas");
- countries.put("VN", "Asia/Ho_Chi_Minh");
- countries.put("ZA", "Africa/Johannesburg");
- countries.put("ZULU", "Zulu");
- countries.put("INTERNET", BEATS_KEYWORD);
- countries.put("BEATS", BEATS_KEYWORD);
-
- ZoneId.getAvailableZoneIds().stream()
- .filter(tz -> !tz.contains("/") && tz.length() == 3 && !countries.containsKey(tz))
- .forEach(tz -> countries.put(tz, tz));
-
- COUNTRIES_MAP = Collections.unmodifiableMap(countries);
- }
-
- /**
- * Creates a new {@link WorldTime} instance.
- */
- public WorldTime(final Mobibot bot) {
- super(bot);
-
- help.add("To display a country's current date/time:");
- help.add(Utils.helpIndent("%c " + TIME_CMD) + " []");
- help.add("For a listing of the supported countries:");
- help.add(Utils.helpIndent("%c " + TIME_CMD));
-
- commands.add(TIME_CMD);
- }
-
-
- /**
- * Returns the current Internet (beat) Time.
- *
- * @return The Internet Time string.
- */
- private static String internetTime() {
- final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00"));
- final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60)
- + (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4);
- return String.format("%c%03d", '@', beats);
- }
-
- /**
- * Returns the world time.
- *
- *
- *
PST
- *
BEATS
- *
- *
- * @param query The query.
- * @return The {@link Message} containing the world time.
- */
- @SuppressFBWarnings("STT_STRING_PARSING_A_FIELD")
- static Message worldTime(final String query) {
- final String tz = (COUNTRIES_MAP.get((StringUtils.upperCase(query.substring(query.indexOf(' ') + 1).trim()))));
- final String response;
-
- if (tz != null) {
- if (BEATS_KEYWORD.equals(tz)) {
- response = ("The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD));
- } else {
- response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(
- DateTimeFormatter
- .ofPattern("'The time is " + Utils.bold("'HH:mm'") + " on " + Utils.bold(
- "'EEEE, d MMMM yyyy'") + " in '"))
- + Utils.bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' '));
- }
- } else {
- return new ErrorMessage("Unsupported country/zone. Please try again.");
- }
-
- return new PublicMessage(response);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void commandResponse(final String sender,
- final String cmd,
- final String args,
- final boolean isPrivate) {
- if (args.length() == 0) {
- bot.send(sender, "The supported countries/zones are: ", isPrivate);
- bot.sendList(sender, new ArrayList<>(COUNTRIES_MAP.keySet()), 17, false, false);
- } else {
- final Message msg = worldTime(args);
- if (isPrivate) {
- bot.send(sender, msg.getMsg(), true);
- } else {
- if (msg.isError()) {
- bot.send(sender, msg.getMsg(), false);
- } else {
- bot.send(msg.getMsg());
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isPrivateMsgEnabled() {
- return true;
- }
-}
diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt
new file mode 100644
index 0000000..8ecc469
--- /dev/null
+++ b/src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt
@@ -0,0 +1,219 @@
+/*
+ * WorldTime.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.modules
+
+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
+import org.apache.commons.lang3.StringUtils
+import java.time.ZoneId
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
+import java.time.temporal.ChronoField
+import java.util.*
+
+/**
+ * The WorldTime module.
+ */
+class WorldTime(bot: Mobibot) : AbstractModule(bot) {
+ companion object {
+ // Beats (Internet Time) keyword
+ private const val BEATS_KEYWORD = ".beats"
+
+ // Supported countries
+ private var COUNTRIES_MAP: Map
+
+ // The Time command
+ private const val TIME_CMD = "time"
+
+ /**
+ * Returns the current Internet (beat) Time.
+ */
+ private fun internetTime(): String {
+ val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00"))
+ val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60
+ + zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt()
+ return String.format("%c%03d", '@', beats)
+ }
+
+ /**
+ * Returns the world time.
+ */
+ @JvmStatic
+ fun worldTime(query: String): Message {
+ val tz = COUNTRIES_MAP[StringUtils.upperCase(query.substring(query.indexOf(' ') + 1).trim())]
+ val response: String = if (tz != null) {
+ if (BEATS_KEYWORD == tz) {
+ "The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD)
+ } else {
+ (ZonedDateTime.now()
+ .withZoneSameInstant(ZoneId.of(tz))
+ .format(
+ DateTimeFormatter.ofPattern(
+ "'The time is ${Utils.bold("'HH:mm'")} on ${Utils.bold("'EEEE, d MMMM yyyy'")} in '"
+ )
+ )
+ + Utils.bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' '))
+ )
+ }
+ } else {
+ return ErrorMessage("Unsupported country/zone. Please try again.")
+ }
+ return PublicMessage(response)
+ }
+
+ init {
+ // Initialize the countries map
+ val countries = TreeMap()
+ countries["AE"] = "Asia/Dubai"
+ countries["AF"] = "Asia/Kabul"
+ countries["AQ"] = "Antarctica/South_Pole"
+ countries["AT"] = "Europe/Vienna"
+ countries["AU"] = "Australia/Sydney"
+ countries["AKST"] = "America/Anchorage"
+ countries["AKDT"] = "America/Anchorage"
+ countries["BE"] = "Europe/Brussels"
+ countries["BR"] = "America/Sao_Paulo"
+ countries["CA"] = "America/Montreal"
+ countries["CDT"] = "America/Chicago"
+ countries["CET"] = "CET"
+ countries["CH"] = "Europe/Zurich"
+ countries["CN"] = "Asia/Shanghai"
+ countries["CST"] = "America/Chicago"
+ countries["CU"] = "Cuba"
+ countries["DE"] = "Europe/Berlin"
+ countries["DK"] = "Europe/Copenhagen"
+ countries["EDT"] = "America/New_York"
+ countries["EG"] = "Africa/Cairo"
+ countries["ER"] = "Africa/Asmara"
+ countries["ES"] = "Europe/Madrid"
+ countries["EST"] = "America/New_York"
+ countries["FI"] = "Europe/Helsinki"
+ countries["FR"] = "Europe/Paris"
+ countries["GB"] = "Europe/London"
+ countries["GMT"] = "GMT"
+ countries["GR"] = "Europe/Athens"
+ countries["HK"] = "Asia/Hong_Kong"
+ countries["HST"] = "Pacific/Honolulu"
+ countries["IE"] = "Europe/Dublin"
+ countries["IL"] = "Asia/Tel_Aviv"
+ countries["IN"] = "Asia/Kolkata"
+ countries["IQ"] = "Asia/Baghdad"
+ countries["IR"] = "Asia/Tehran"
+ countries["IS"] = "Atlantic/Reykjavik"
+ countries["IT"] = "Europe/Rome"
+ countries["JM"] = "Jamaica"
+ countries["JP"] = "Asia/Tokyo"
+ countries["LY"] = "Africa/Tripoli"
+ countries["MA"] = "Africa/Casablanca"
+ countries["MDT"] = "America/Denver"
+ countries["MH"] = "Kwajalein"
+ countries["MQ"] = "America/Martinique"
+ countries["MST"] = "America/Denver"
+ countries["MX"] = "America/Mexico_City"
+ countries["NL"] = "Europe/Amsterdam"
+ countries["NO"] = "Europe/Oslo"
+ countries["NP"] = "Asia/Katmandu"
+ countries["NZ"] = "Pacific/Auckland"
+ countries["PDT"] = "America/Los_Angeles"
+ countries["PH"] = "Asia/Manila"
+ countries["PK"] = "Asia/Karachi"
+ countries["PL"] = "Europe/Warsaw"
+ countries["PST"] = "America/Los_Angeles"
+ countries["PT"] = "Europe/Lisbon"
+ countries["PR"] = "America/Puerto_Rico"
+ countries["RU"] = "Europe/Moscow"
+ countries["SE"] = "Europe/Stockholm"
+ countries["SG"] = "Asia/Singapore"
+ countries["TH"] = "Asia/Bangkok"
+ countries["TM"] = "Asia/Ashgabat"
+ countries["TN"] = "Africa/Tunis"
+ countries["TR"] = "Europe/Istanbul"
+ countries["TW"] = "Asia/Taipei"
+ countries["UK"] = "Europe/London"
+ countries["US"] = "America/New_York"
+ countries["UTC"] = "UTC"
+ countries["VA"] = "Europe/Vatican"
+ countries["VE"] = "America/Caracas"
+ countries["VN"] = "Asia/Ho_Chi_Minh"
+ countries["ZA"] = "Africa/Johannesburg"
+ countries["ZULU"] = "Zulu"
+ countries["INTERNET"] = BEATS_KEYWORD
+ countries["BEATS"] = BEATS_KEYWORD
+ ZoneId.getAvailableZoneIds().stream()
+ .filter { tz: String ->
+ !tz.contains("/") && tz.length == 3 && !countries.containsKey(tz)
+ }
+ .forEach { tz: String ->
+ countries[tz] = tz
+ }
+ COUNTRIES_MAP = Collections.unmodifiableMap(countries)
+ }
+ }
+
+ override fun commandResponse(
+ sender: String,
+ cmd: String,
+ args: String,
+ isPrivate: Boolean
+ ) {
+ with(bot) {
+ if (args.isEmpty()) {
+ send(sender, "The supported countries/zones are: ", isPrivate)
+ sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, false, false)
+ } else {
+ val msg = worldTime(args)
+ if (isPrivate) {
+ send(sender, msg.msg, true)
+ } else {
+ if (msg.isError) {
+ send(sender, msg.msg, false)
+ } else {
+ send(msg.msg)
+ }
+ }
+ }
+ }
+ }
+
+ override val isPrivateMsgEnabled = true
+
+ init {
+ help.add("To display a country's current date/time:")
+ help.add(Utils.helpIndent("%c $TIME_CMD") + " []")
+ help.add("For a listing of the supported countries:")
+ help.add(Utils.helpIndent("%c $TIME_CMD"))
+ commands.add(TIME_CMD)
+ }
+}
diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt
index b353fe8..96ef549 100644
--- a/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt
+++ b/src/main/java/net/thauvin/erik/mobibot/msg/ErrorMessage.kt
@@ -1,5 +1,5 @@
/*
- * ErrorMessage.java
+ * ErrorMessage.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg
/**
* The `ErrorMessage` class.
- *
- * @author [Erik C. Thauvin](https://erik.thauvin.net/)
- * @created 2019-04-07
- * @since 1.0
*/
class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() {
init {
diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt
index d3fe758..a6f66f7 100644
--- a/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt
+++ b/src/main/java/net/thauvin/erik/mobibot/msg/Message.kt
@@ -35,10 +35,6 @@ import org.jibble.pircbot.Colors
/**
* The `Message` class.
- *
- * @author [Erik C. Thauvin](https://erik.thauvin.net/)
- * @created 2019-04-07
- * @since 1.0
*/
open class Message {
companion object {
@@ -67,12 +63,6 @@ open class Message {
/**
* Creates a new message.
- *
- * @param msg The message.
- * @param color The color.
- * @param isNotice The notice flag.
- * @param isError The error flag.
- * @param isPrivate The private flag.
*/
@JvmOverloads
constructor(msg: String, color: String = DEFAULT_COLOR, isNotice: Boolean, isError: Boolean, isPrivate: Boolean) {
diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt
index 3286d60..126fd91 100644
--- a/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt
+++ b/src/main/java/net/thauvin/erik/mobibot/msg/NoticeMessage.kt
@@ -1,5 +1,5 @@
/*
- * NoticeMessage.java
+ * NoticeMessage.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg
/**
* The `NoticeMessage` class.
- *
- * @author [Erik C. Thauvin](https://erik.thauvin.net/)
- * @created 2019-04-07
- * @since 1.0
*/
class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() {
init {
diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt
index 8eb4281..6bbade8 100644
--- a/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt
+++ b/src/main/java/net/thauvin/erik/mobibot/msg/PrivateMessage.kt
@@ -1,5 +1,5 @@
/*
- * PrivateMessage.java
+ * PrivateMessage.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg
/**
* The `PrivateMessage` class.
- *
- * @author [Erik C. Thauvin](https://erik.thauvin.net/)
- * @created 2019-04-09
- * @since 1.0
*/
@Suppress("unused")
class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() {
diff --git a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt
index 3d2ac0f..384bc05 100644
--- a/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt
+++ b/src/main/java/net/thauvin/erik/mobibot/msg/PublicMessage.kt
@@ -1,5 +1,5 @@
/*
- * PublicMessage.java
+ * PublicMessage.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg
/**
* The `PublicMessage` class.
- *
- * @author [Erik C. Thauvin](https://erik.thauvin.net/)
- * @created 2019-04-07
- * @since 1.0
*/
class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() {
init {
diff --git a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java
index b754fbf..bde9d77 100644
--- a/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java
+++ b/src/test/java/net/thauvin/erik/mobibot/UtilsTest.java
@@ -66,7 +66,7 @@ public class UtilsTest {
@Test
public void testBold() {
- assertThat(Utils.bold(1)).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD);
+ assertThat(Utils.bold(Integer.toString(1))).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD);
assertThat(Utils.bold(ASCII)).as("bold(ascii").isEqualTo(Colors.BOLD + ASCII + Colors.BOLD);
}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt
similarity index 63%
rename from src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java
rename to src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt
index 2f9b21e..643dbd6 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.java
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/CalcTest.kt
@@ -1,5 +1,5 @@
/*
- * CalcTest.java
+ * CalcTest.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -29,32 +29,26 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package net.thauvin.erik.mobibot.modules
-package net.thauvin.erik.mobibot.modules;
-
-import net.thauvin.erik.mobibot.Utils;
-import org.testng.annotations.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import net.thauvin.erik.mobibot.Utils
+import net.thauvin.erik.mobibot.modules.Calc.Companion.calc
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
/**
- * The CalcTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-07
- * @since 1.0
+ * The `CalcTest` class.
*/
-public class CalcTest {
+class CalcTest {
@Test
- public void testCalc() {
- assertThat(Calc.calc("1 + 1")).as("calc(1+1)").isEqualTo("1+1 = %s", Utils.bold(2));
- assertThat(Calc.calc("1 -3")).as("calc(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2));
- assertThat(Calc.calc("pi+π+e+φ")).as("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62"));
- assertThat(Calc.calc("one + one")).as("calc(one + one)").startsWith("No idea.");
- }
-
- @Test
- public void testCalcImpl() {
- AbstractModuleTest.testAbstractModule(new Calc(null));
+ fun testCalc() {
+ Assertions.assertThat(calc("1 + 1")).`as`("calc(1+1)")
+ .isEqualTo("1+1 = %s", Utils.bold(2))
+ Assertions.assertThat(calc("1 -3")).`as`("calc(1 -3)")
+ .isEqualTo("1-3 = %s", Utils.bold(-2))
+ Assertions.assertThat(calc("pi+π+e+φ")).`as`("calc(pi+π+e+φ)")
+ .isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62"))
+ Assertions.assertThat(calc("one + one")).`as`("calc(one + one)")
+ .startsWith("No idea.")
}
}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java
deleted file mode 100644
index b67fd6f..0000000
--- a/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * CurrencyConverterTest.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.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * The CurrencyConvertTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-07
- * @since 1.0
- */
-public class CurrencyConverterTest {
- @BeforeClass
- public void before() throws ModuleException {
- CurrencyConverter.loadRates();
- }
-
- @Test
- @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS")
- public void testConvertCurrency() {
- assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMsg())
- .as("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR");
- assertThat(CurrencyConverter.convertCurrency("100 USD to USD").getMsg())
- .as("100 USD to USD").contains("You're kidding, right?");
- assertThat(CurrencyConverter.convertCurrency("100 USD").getMsg())
- .as("100 USD").contains("Invalid query.");
- assertThat(CurrencyConverter.currencyRates().size())
- .as("currencyRates().size() == 33").isEqualTo(33);
- assertThat(CurrencyConverter.currencyRates())
- .as("currencyRates().get(EUR)").contains(" EUR: 1");
- }
-
- @Test
- public void testCurrencyConvertererImpl() {
- AbstractModuleTest.testAbstractModule(new CurrencyConverter(null));
- }
-}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt
similarity index 56%
rename from src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java
rename to src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt
index 6b1b7c4..edcbb65 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.java
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt
@@ -1,5 +1,5 @@
/*
- * LookupTest.java
+ * CurrencyConverterTest.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -29,40 +29,36 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package net.thauvin.erik.mobibot.modules
-package net.thauvin.erik.mobibot.modules;
-
-import org.testng.annotations.Test;
-
-import java.util.Arrays;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency
+import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.currencyRates
+import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadRates
+import org.assertj.core.api.Assertions
+import org.testng.annotations.BeforeClass
+import org.testng.annotations.Test
/**
- * The Lookup Test class.
- *
- * @author Erik C. Thauvin
- * @created 2017-05-30
- * @since 1.0
+ * The `CurrencyConvertTest` class.
*/
-@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
-public class LookupTest {
- @Test
- public void testLookupImpl() {
- AbstractModuleTest.testAbstractModule(new Lookup(null));
+class CurrencyConverterTest {
+ @BeforeClass
+ @Throws(ModuleException::class)
+ fun before() {
+ loadRates()
}
@Test
- public void testLookup() throws Exception {
- final String result = Lookup.lookup("erik.thauvin.net");
- assertThat(result).as("lookup(erik.thauvin.net/104.31.77.12)").contains("104.31.77.12");
- }
-
- @Test
- public void testWhois() throws Exception {
- final String[] result = Lookup.whois("17.178.96.59", Lookup.WHOIS_HOST);
-
- assertThat(Arrays.stream(result).anyMatch(m -> m.contains("Apple Inc.")))
- .as("whois(17.178.96.59/Apple Inc.").isTrue();
+ fun testConvertCurrency() {
+ Assertions.assertThat(convertCurrency("100 USD to EUR").msg)
+ .`as`("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR")
+ Assertions.assertThat(convertCurrency("100 USD to USD").msg)
+ .`as`("100 USD to USD").contains("You're kidding, right?")
+ Assertions.assertThat(convertCurrency("100 USD").msg)
+ .`as`("100 USD").contains("Invalid query.")
+ Assertions.assertThat(currencyRates().size)
+ .`as`("currencyRates().size() == 33").isEqualTo(33)
+ Assertions.assertThat(currencyRates())
+ .`as`("currencyRates().get(EUR)").contains(" EUR: 1")
}
}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt
new file mode 100644
index 0000000..6cc59a5
--- /dev/null
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt
@@ -0,0 +1,52 @@
+/*
+ * DiceTest.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.modules
+
+
+import org.assertj.core.api.Assertions.assertThat
+import org.testng.annotations.Test
+
+class DiceTest {
+ @Test
+ fun testWinLoseOrTie() {
+ assertThat(
+ Dice.winLoseOrTie(6, 6)
+ ).`as`("6 vs. 6").isEqualTo(Dice.Result.TIE)
+ assertThat(
+ Dice.winLoseOrTie(6, 5)
+ ).`as`("6 vs. 5").isEqualTo(Dice.Result.WIN)
+ assertThat(
+ Dice.winLoseOrTie(5, 6)
+ ).`as`("5 vs. 6").isEqualTo(Dice.Result.LOSE)
+ }
+}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java
deleted file mode 100644
index ecf0f0f..0000000
--- a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * GoogleSearchTest.java
- *
- * 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.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import net.thauvin.erik.mobibot.LocalProperties;
-import net.thauvin.erik.mobibot.msg.Message;
-import org.testng.annotations.Test;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-/**
- * The GoogleSearchTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-08
- * @since 1.0
- */
-public class GoogleSearchTest extends LocalProperties {
- @Test
- public void testGoogleSearchImpl() {
- AbstractModuleTest.testAbstractModule(new GoogleSearch(null));
- }
-
- @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE")
- @SuppressWarnings("PMD.PreserveStackTrace")
- @Test
- public void testSearchGoogle() throws ModuleException {
- final String apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP);
- final String cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP);
- try {
- List messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey);
- assertThat(messages).as("mobibot results not empty").isNotEmpty();
- assertThat(messages.get(0).getMsg()).as("found mobitopia").contains("mobibot");
-
- messages = GoogleSearch.searchGoogle("aapl", apiKey, cseKey);
- assertThat(messages).as("aapl results not empty").isNotEmpty();
- assertThat(messages.get(0).getMsg()).as("found apple").containsIgnoringCase("apple");
-
- assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "", "apiKey")).as("no API key").isInstanceOf(
- ModuleException.class).hasNoCause();
-
- assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "apiKey", "")).as("no CSE API key").isInstanceOf(
- ModuleException.class).hasNoCause();
-
- assertThatThrownBy(() -> GoogleSearch.searchGoogle("", "apikey", "apiKey")).as("no query").isInstanceOf(
- ModuleException.class).hasNoCause();
- } catch (ModuleException e) {
- // Avoid displaying api keys in CI logs
- if ("true".equals(System.getenv("CI"))) {
- throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey, cseKey));
- } else {
- throw e;
- }
- }
- }
-}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt
new file mode 100644
index 0000000..b934b02
--- /dev/null
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt
@@ -0,0 +1,72 @@
+/*
+ * GoogleSearchTest.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.modules
+
+import net.thauvin.erik.mobibot.LocalProperties
+import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
+
+/**
+ * The `GoogleSearchTest` class.
+ */
+class GoogleSearchTest : LocalProperties() {
+ @Test
+ @Throws(ModuleException::class)
+ fun testSearchGoogle() {
+ val apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP)
+ val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP)
+ try {
+ var messages = searchGoogle("mobibot site:github.com", apiKey, cseKey)
+ Assertions.assertThat(messages).`as`("mobibot results not empty").isNotEmpty
+ Assertions.assertThat(messages[0].msg).`as`("found mobitopia").contains("mobibot")
+ messages = searchGoogle("aapl", apiKey, cseKey)
+ Assertions.assertThat(messages).`as`("aapl results not empty").isNotEmpty
+ Assertions.assertThat(messages[0].msg).`as`("found apple").containsIgnoringCase("apple")
+ Assertions.assertThatThrownBy { searchGoogle("test", "", "apiKey") }
+ .`as`("no API key")
+ .isInstanceOf(ModuleException::class.java).hasNoCause()
+ Assertions.assertThatThrownBy { searchGoogle("test", "apiKey", "") }
+ .`as`("no CSE API key")
+ .isInstanceOf(ModuleException::class.java).hasNoCause()
+ Assertions.assertThatThrownBy { searchGoogle("", "apikey", "apiKey") }
+ .`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause()
+ } catch (e: ModuleException) {
+ // Avoid displaying api keys in CI logs
+ if ("true" == System.getenv("CI")) {
+ throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey))
+ } else {
+ throw e
+ }
+ }
+ }
+}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt
similarity index 69%
rename from src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java
rename to src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt
index 9880241..5a2f221 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.java
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/JokeTest.kt
@@ -1,5 +1,5 @@
/*
- * JokeTest.java
+ * JokeTest.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -29,29 +29,20 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package net.thauvin.erik.mobibot.modules
-package net.thauvin.erik.mobibot.modules;
-
-import org.testng.annotations.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
/**
- * The JokeTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-07
- * @since 1.0
+ * The `JokeTest` class.
*/
-public class JokeTest {
+class JokeTest {
@Test
- public void testJokeImpl() {
- AbstractModuleTest.testAbstractModule(new Joke(null));
- }
-
- @Test
- public void testRamdomJoke() throws ModuleException {
- assertThat(Joke.randomJoke().getMsg().length() > 0).as("randomJoke() > 0").isTrue();
- assertThat(Joke.randomJoke().getMsg()).as("randomJoke()").containsIgnoringCase("chuck");
+ @Throws(ModuleException::class)
+ fun testRamdomJoke() {
+ Assertions.assertThat(randomJoke().msg.isNotEmpty()).`as`("randomJoke() > 0").isTrue
+ Assertions.assertThat(randomJoke().msg).`as`("randomJoke()").containsIgnoringCase("chuck")
}
}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt
similarity index 66%
rename from src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java
rename to src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt
index 5f862ca..303a377 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.java
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/LookupTest.kt
@@ -1,5 +1,5 @@
/*
- * WordTimeTest.java
+ * LookupTest.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -29,31 +29,30 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package net.thauvin.erik.mobibot.modules
-package net.thauvin.erik.mobibot.modules;
-
-import net.thauvin.erik.mobibot.Utils;
-import org.testng.annotations.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import net.thauvin.erik.mobibot.modules.Lookup.Companion.lookup
+import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
+import java.util.*
/**
- * The WordTimeTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-07
- * @since 1.0
+ * The `Lookup Test` class.
*/
-public class WordTimeTest {
+class LookupTest {
@Test
- public void testWorldTime() {
- assertThat(WorldTime.worldTime("PST").getMsg()).as("PST").endsWith(Utils.bold("Los Angeles"));
- assertThat(WorldTime.worldTime("BLAH").isError()).as("BLAH").isTrue();
- assertThat(WorldTime.worldTime("BEATS").getMsg()).as("BEATS").contains("@");
+ @Throws(Exception::class)
+ fun testLookup() {
+ val result = lookup("erik.thauvin.net")
+ Assertions.assertThat(result).`as`("lookup(erik.thauvin.net/104.31.77.12)").contains("104.31.77.12")
}
@Test
- public void testWorldTimeImpl() {
- AbstractModuleTest.testAbstractModule(new Lookup(null));
+ @Throws(Exception::class)
+ fun testWhois() {
+ val result = whois("17.178.96.59", Lookup.WHOIS_HOST)
+ Assertions.assertThat(Arrays.stream(result).anyMatch { m: String -> m.contains("Apple Inc.") })
+ .`as`("whois(17.178.96.59/Apple Inc.").isTrue
}
}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java
index 46199c9..b7feb89 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.java
@@ -42,10 +42,6 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* The ModuleExceptionTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-09
- * @since 1.0
*/
public class ModuleExceptionTest {
static final String debugMessage = "debugMessage";
@@ -53,13 +49,13 @@ public class ModuleExceptionTest {
@DataProvider(name = "dp")
Object[][] createData(final Method m) {
- return new Object[][]{new Object[]{new ModuleException(debugMessage,
- message,
- new IOException("URL http://foobar.com"))},
- new Object[]{new ModuleException(debugMessage,
- message,
- new IOException("URL http://foobar.com?"))},
- new Object[]{new ModuleException(debugMessage, message)}};
+ return new Object[][]{ new Object[]{ new ModuleException(debugMessage,
+ message,
+ new IOException("URL http://foobar.com")) },
+ new Object[]{ new ModuleException(debugMessage,
+ message,
+ new IOException("URL http://foobar.com?")) },
+ new Object[]{ new ModuleException(debugMessage, message) } };
}
@Test(dataProvider = "dp")
@@ -78,7 +74,7 @@ public class ModuleExceptionTest {
final ModuleException e = new ModuleException(debugMessage,
message,
new IOException(
- "URL http://foo.com?apiKey=" + apiKey + "&userID=me"));
+ "URL http://foo.com?apiKey=" + apiKey + "&userID=me"));
assertThat(e.getSanitizedMessage(apiKey)).as("sanitized url").contains("xxxxxxxxxx").doesNotContain(apiKey);
}
}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt
similarity index 75%
rename from src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java
rename to src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt
index 564f111..1b0c460 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.java
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/PingTest.kt
@@ -1,5 +1,5 @@
/*
- * PingTest.java
+ * PingTest.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -29,28 +29,25 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package net.thauvin.erik.mobibot.modules
-package net.thauvin.erik.mobibot.modules;
-
-import org.testng.annotations.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
/**
- * The PingTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-07
- * @since 1.0
+ * The `PingTest` class.
*/
-public class PingTest {
+class PingTest {
@Test
- public void testPingImpl() {
- AbstractModuleTest.testAbstractModule(new Ping(null));
+ fun testPingsArray() {
+ Assertions.assertThat(Ping.PINGS).`as`("Pings array is not empty.").isNotEmpty
}
@Test
- public void testPingsArray() {
- assertThat(Ping.PINGS).as("Pings array is not empty.").isNotEmpty();
+ fun testRandomPing() {
+ for (i in 0..9) {
+ Assertions.assertThat(randomPing()).`as`("Random ping $i").isIn(Ping.PINGS)
+ }
}
}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt
index 0429945..1c71cb0 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt
@@ -32,7 +32,6 @@
package net.thauvin.erik.mobibot.modules
-
import org.assertj.core.api.Assertions.assertThat
import org.testng.annotations.Test
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java
deleted file mode 100644
index a8961c6..0000000
--- a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * StockQuoteTest.java
- *
- * 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.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import net.thauvin.erik.mobibot.LocalProperties;
-import net.thauvin.erik.mobibot.msg.Message;
-import org.testng.annotations.Test;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-/**
- * The StockQuoteTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-07
- * @since 1.0
- */
-
-public class StockQuoteTest extends LocalProperties {
- @SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE")
- @SuppressWarnings("PMD.PreserveStackTrace")
- @Test
- public void testGetQuote() throws ModuleException {
- final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP);
- try {
- final List messages = StockQuote.getQuote("apple inc", apiKey);
- assertThat(messages).as("response not empty").isNotEmpty();
- assertThat(messages.get(0).getMsg()).as("same stock symbol").contains("AAPL").contains("Apple Inc.");
-
- try {
- StockQuote.getQuote("blahfoo", apiKey);
- } catch (ModuleException e) {
- assertThat(e.getMessage()).as("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL);
- }
-
- assertThatThrownBy(() -> StockQuote.getQuote("test", "")).as("no API key").isInstanceOf(
- ModuleException.class).hasNoCause();
-
- assertThatThrownBy(() -> StockQuote.getQuote("", "apikey")).as("no symbol").isInstanceOf(
- ModuleException.class).hasNoCause();
-
- } catch (ModuleException e) {
- // Avoid displaying api keys in CI logs
- if ("true".equals(System.getenv("CI"))) {
- throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey));
- } else {
- throw e;
- }
- }
- }
-
- @Test
- public void testStockQuoteImpl() {
- AbstractModuleTest.testAbstractModule(new StockQuote(null));
- }
-}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt
new file mode 100644
index 0000000..96cc1c1
--- /dev/null
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt
@@ -0,0 +1,73 @@
+/*
+ * StockQuoteTest.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.modules
+
+import net.thauvin.erik.mobibot.LocalProperties
+import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
+
+/**
+ * The `StockQuoteTest` class.
+ */
+class StockQuoteTest : LocalProperties() {
+ @Test
+ @Throws(ModuleException::class)
+ fun testGetQuote() {
+ val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP)
+ try {
+ val messages = getQuote("apple inc", apiKey)
+ Assertions.assertThat(messages).`as`("response not empty").isNotEmpty
+ Assertions.assertThat(messages[0].msg).`as`("same stock symbol")
+ .isEqualTo("Symbol: AAPL [Apple Inc.]")
+ Assertions.assertThat(messages[1].msg).`as`("price label")
+ .startsWith(" Price: ")
+ try {
+ getQuote("blahfoo", apiKey)
+ } catch (e: ModuleException) {
+ Assertions.assertThat(e.message).`as`("invalid symbol")
+ .containsIgnoringCase(StockQuote.INVALID_SYMBOL)
+ }
+ Assertions.assertThatThrownBy { getQuote("test", "") }.`as`("no API key")
+ .isInstanceOf(ModuleException::class.java).hasNoCause()
+ Assertions.assertThatThrownBy { getQuote("", "apikey") }.`as`("no symbol")
+ .isInstanceOf(ModuleException::class.java).hasNoCause()
+ } catch (e: ModuleException) {
+ // Avoid displaying api keys in CI logs
+ if ("true" == System.getenv("CI")) {
+ throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey))
+ } else {
+ throw e
+ }
+ }
+ }
+}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt
similarity index 62%
rename from src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java
rename to src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt
index 2c14d2b..da9b26a 100644
--- a/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.java
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/TwitterTest.kt
@@ -1,5 +1,5 @@
/*
- * TwitterTest.java
+ * TwitterTest.kt
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
@@ -29,50 +29,43 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package net.thauvin.erik.mobibot.modules
-package net.thauvin.erik.mobibot.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import net.thauvin.erik.mobibot.LocalProperties;
-import org.testng.annotations.Test;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import net.thauvin.erik.mobibot.LocalProperties
+import net.thauvin.erik.mobibot.modules.Twitter.Companion.twitterPost
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
+import java.net.InetAddress
+import java.net.UnknownHostException
/**
- * The TwitterTest class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-19
- * @since 1.0
+ * The `TwitterTest` class.
*/
-public class TwitterTest extends LocalProperties {
- @SuppressFBWarnings("MDM")
- private String getCi() {
- final String ciName = System.getenv("CI_NAME");
- if (ciName != null) {
- return ciName;
- } else {
- try {
- return InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e) {
- return "Unknown Host";
+class TwitterTest : LocalProperties() {
+ private val ci: String
+ get() {
+ val ciName = System.getenv("CI_NAME")
+ return ciName ?: try {
+ InetAddress.getLocalHost().hostName
+ } catch (e: UnknownHostException) {
+ "Unknown Host"
}
}
- }
@Test
- public void testPostTwitter() throws ModuleException {
- final String msg = "Testing Twitter API from " + getCi();
- assertThat(Twitter.twitterPost(
+ @Throws(ModuleException::class)
+ fun testPostTwitter() {
+ val msg = "Testing Twitter API from $ci"
+ Assertions.assertThat(
+ twitterPost(
getProperty(Twitter.CONSUMER_KEY_PROP),
getProperty(Twitter.CONSUMER_SECRET_PROP),
getProperty(Twitter.TOKEN_PROP),
getProperty(Twitter.TOKEN_SECRET_PROP),
getProperty(Twitter.HANDLE_PROP),
msg,
- true).getMsg()).as("twitterPost(" + msg + ')').isEqualTo(msg);
+ true
+ ).msg
+ ).`as`("twitterPost($msg)").isEqualTo(msg)
}
}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java
deleted file mode 100644
index 6d8c15c..0000000
--- a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Weather2Test.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.modules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import net.aksingh.owmjapis.api.APIException;
-import net.thauvin.erik.mobibot.LocalProperties;
-import net.thauvin.erik.mobibot.msg.Message;
-import org.testng.annotations.Test;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-/**
- * The Weather2Test class.
- *
- * @author Erik C. Thauvin
- * @created 2019-04-09
- * @since 1.0
- */
-public class Weather2Test extends LocalProperties {
- @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS")
- @Test
- public void testWeather() throws ModuleException {
- List messages = Weather2.getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP));
- assertThat(messages.get(0).getMsg()).as("is Everett").contains("Everett").contains("US");
- assertThat(messages.get(messages.size() - 1).getMsg()).as("is City Search").endsWith("98204%2CUS");
-
- messages = Weather2.getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP));
- assertThat(messages.get(0).getMsg()).as("is UK").contains("London").contains("UK");
- assertThat(messages.get(messages.size() - 1).getMsg()).as("is City Code").endsWith("4517009");
-
- assertThatThrownBy(() -> Weather2.getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)))
- .as("Montpellier not found").hasCauseInstanceOf(APIException.class);
-
- assertThatThrownBy(() -> Weather2.getWeather("test", ""))
- .as("no API key").isInstanceOf(ModuleException.class).hasNoCause();
-
- messages = Weather2.getWeather("", "apikey");
- assertThat(messages.get(0).isError()).as("no query").isTrue();
-
- }
-
- @Test
- public void testWeather2Impl() {
- AbstractModuleTest.testAbstractModule(new Weather2(null));
- }
-}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt
new file mode 100644
index 0000000..4364585
--- /dev/null
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/Weather2Test.kt
@@ -0,0 +1,60 @@
+/*
+ * Weather2Test.kt
+ *
+ * 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.modules
+
+import net.aksingh.owmjapis.api.APIException
+import net.thauvin.erik.mobibot.LocalProperties
+import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
+
+/**
+ * The `Weather2Test` class.
+ */
+class Weather2Test : LocalProperties() {
+ @Test
+ @Throws(ModuleException::class)
+ fun testWeather() {
+ var messages = getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP))
+ Assertions.assertThat(messages[0].msg).`as`("is Everett").contains("Everett").contains("US")
+ Assertions.assertThat(messages[messages.size - 1].msg).`as`("is City Search").endsWith("98204%2CUS")
+ messages = getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP))
+ Assertions.assertThat(messages[0].msg).`as`("is UK").contains("London").contains("UK")
+ Assertions.assertThat(messages[messages.size - 1].msg).`as`("is City Code").endsWith("4517009")
+ Assertions.assertThatThrownBy { getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)) }
+ .`as`("Montpellier not found").hasCauseInstanceOf(APIException::class.java)
+ Assertions.assertThatThrownBy { getWeather("test", "") }
+ .`as`("no API key").isInstanceOf(ModuleException::class.java).hasNoCause()
+ messages = getWeather("", "apikey")
+ Assertions.assertThat(messages[0].isError).`as`("no query").isTrue
+ }
+}
diff --git a/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt
new file mode 100644
index 0000000..b6a40aa
--- /dev/null
+++ b/src/test/java/net/thauvin/erik/mobibot/modules/WordTimeTest.kt
@@ -0,0 +1,49 @@
+/*
+ * WordTimeTest.kt
+ *
+ * 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.modules
+
+import net.thauvin.erik.mobibot.Utils
+import net.thauvin.erik.mobibot.modules.WorldTime.Companion.worldTime
+import org.assertj.core.api.Assertions
+import org.testng.annotations.Test
+
+/**
+ * The `WordTimeTest` class.
+ */
+class WordTimeTest {
+ @Test
+ fun testWorldTime() {
+ Assertions.assertThat(worldTime("PST").msg).`as`("PST").endsWith(Utils.bold("Los Angeles"))
+ Assertions.assertThat(worldTime("BLAH").isError).`as`("BLAH").isTrue
+ Assertions.assertThat(worldTime("BEATS").msg).`as`("BEATS").contains("@")
+ }
+}