Implemented Rock Paper Scissors module in Kotlin.

This commit is contained in:
Erik C. Thauvin 2020-03-23 05:53:11 -07:00
parent d8da21b0ef
commit 92576f9496
26 changed files with 501 additions and 248 deletions

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
All rights reserved.
Redistribution and use in source and binary forms, with or without

View file

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

View file

@ -1,7 +1,7 @@
/*
* FeedReader.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -88,12 +88,15 @@ class FeedReader implements Runnable {
SyndEntry item;
final List<SyndEntry> items = feed.getEntries();
if (items.isEmpty()) {
bot.send(sender, "There is currently nothing to view. Why don't you post something?");
} else {
for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) {
item = items.get(i);
bot.send(sender, item.getTitle());
bot.send(sender, TAB_INDENT + Utils.green(item.getLink()));
}
}
} catch (MalformedURLException e) {
bot.getLogger().debug("Invalid feed URL.", e);
bot.send(sender, "The feed URL is invalid.");

View file

@ -1,7 +1,7 @@
/*
* Mobibot.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -47,6 +47,7 @@ import net.thauvin.erik.mobibot.modules.Joke;
import net.thauvin.erik.mobibot.modules.Lookup;
import net.thauvin.erik.mobibot.modules.ModuleException;
import net.thauvin.erik.mobibot.modules.Ping;
import net.thauvin.erik.mobibot.modules.RockPaperScissors;
import net.thauvin.erik.mobibot.modules.StockQuote;
import net.thauvin.erik.mobibot.modules.Twitter;
import net.thauvin.erik.mobibot.modules.War;
@ -62,7 +63,6 @@ import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -93,6 +93,9 @@ import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import static net.thauvin.erik.mobibot.Utils.bold;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* Implements the #mobitopia bot.
*
@ -101,7 +104,8 @@ import java.util.StringTokenizer;
* @since 1.0
*/
@SuppressWarnings("WeakerAccess")
@Version(properties = "version.properties", className = "ReleaseInfo")
@Version(properties = "version.properties",
className = "ReleaseInfo")
public class Mobibot extends PircBot {
// The default port.
@ -111,8 +115,9 @@ public class Mobibot extends PircBot {
private static final String DEFAULT_SERVER = "irc.freenode.net";
// The info strings.
private static final String[] INFO_STRS =
{ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)",
@SuppressWarnings("indentation")
private static final String[] INFO_STRS = {
ReleaseInfo.PROJECT + " v" + ReleaseInfo.VERSION + " by Erik C. Thauvin (erik@thauvin.net)",
"https://www.mobitopia.org/mobibot/" };
// The link match string.
@ -137,12 +142,13 @@ public class Mobibot extends PircBot {
private static final String TAGS_MARKER = "tags:";
/* The version strings.*/
@SuppressWarnings("indentation")
private static final String[] VERSION_STRS =
{ "Version: " + ReleaseInfo.VERSION + " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')',
"Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", " + System
.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')',
"Runtime: " + System.getProperty("java.runtime.name") + " (build " + System
.getProperty("java.runtime.version") + ')',
"Platform: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ", "
+ System.getProperty("os.arch") + ", " + System.getProperty("user.country") + ')',
"Runtime: " + System.getProperty("java.runtime.name") + " (build " + System.getProperty(
"java.runtime.version") + ')',
"VM: " + System.getProperty("java.vm.name") + " (build " + System.getProperty("java.vm.version") + ", "
+ System.getProperty("java.vm.info") + ')' };
// The logger.
@ -272,6 +278,7 @@ public class Mobibot extends PircBot {
MODULES.add(new Joke());
MODULES.add(new Lookup());
MODULES.add(new Ping());
MODULES.add(new RockPaperScissors());
MODULES.add(new StockQuote());
twitterModule = new Twitter();
@ -317,12 +324,20 @@ public class Mobibot extends PircBot {
public static void main(final String[] args) {
// Setup the command line options
final Options options = new Options()
.addOption(Commands.HELP_ARG.substring(0, 1), Commands.HELP_ARG, false, "print this help message")
.addOption(Commands.HELP_ARG.substring(0, 1),
Commands.HELP_ARG,
false,
"print this help message")
.addOption(Commands.DEBUG_ARG.substring(0, 1), Commands.DEBUG_ARG, false,
"print debug & logging data directly to the console")
.addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg().argName("file")
.desc("use " + "alternate properties file").longOpt(Commands.PROPS_ARG).build())
.addOption(Commands.VERSION_ARG.substring(0, 1), Commands.VERSION_ARG, false, "print version info");
.addOption(Option.builder(Commands.PROPS_ARG.substring(0, 1)).hasArg()
.argName("file")
.desc("use " + "alternate properties file")
.longOpt(Commands.PROPS_ARG).build())
.addOption(Commands.VERSION_ARG.substring(0, 1),
Commands.VERSION_ARG,
false,
"print version info");
// Parse the command line
final CommandLineParser parser = new DefaultParser();
@ -423,7 +438,7 @@ public class Mobibot extends PircBot {
* @param action The action.
*/
private void action(final String channel, final String action) {
if (StringUtils.isNotBlank(channel) && StringUtils.isNotBlank(action)) {
if (isNotBlank(channel) && isNotBlank(action)) {
sendAction(channel, action);
}
}
@ -470,7 +485,7 @@ public class Mobibot extends PircBot {
* @param sender The nick of the person who sent the private message.
*/
private void feedResponse(final String sender) {
if (StringUtils.isNotBlank(feedUrl)) {
if (isNotBlank(feedUrl)) {
new Thread(new FeedReader(this, sender, feedUrl)).start();
} else {
send(sender, "There is no weblog setup for this channel.");
@ -606,7 +621,7 @@ public class Mobibot extends PircBot {
* @return The indented help string.
*/
public String helpIndent(final String help, final boolean isBold) {
return " " + (isBold ? Utils.bold(help) : help);
return " " + (isBold ? bold(help) : help);
}
/**
@ -619,21 +634,21 @@ public class Mobibot extends PircBot {
final String lcTopic = topic.toLowerCase(Constants.LOCALE).trim();
if (Commands.HELP_POSTING_KEYWORD.equals(lcTopic)) {
send(sender, Utils.bold("Post a URL, by saying it on a line on its own:"));
send(sender, bold("Post a URL, by saying it on a line on its own:"));
send(sender, helpIndent("<url> [<title>] [" + TAGS_MARKER + "<+tag> [...]]"));
send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + '1'));
send(sender, "I will reply with a label, for example: " + bold(Commands.LINK_CMD + '1'));
send(sender, "To add a title, use a its label and a pipe:");
send(sender, helpIndent(Commands.LINK_CMD + "1:|This is the title"));
send(sender, "To add a comment: ");
send(sender, helpIndent(Commands.LINK_CMD + "1:This is a comment"));
send(sender, "I will reply with a label, for example: " + Utils.bold(Commands.LINK_CMD + "1.1"));
send(sender, "I will reply with a label, for example: " + bold(Commands.LINK_CMD + "1.1"));
send(sender, "To edit a comment, use its label: ");
send(sender, helpIndent(Commands.LINK_CMD + "1.1:This is an edited comment"));
send(sender, "To delete a comment, use its label and a minus sign: ");
send(sender, helpIndent(Commands.LINK_CMD + "1.1:-"));
send(sender, "You can also view a posting by saying its label.");
} else if (Commands.HELP_TAGS_KEYWORD.equals(lcTopic)) {
send(sender, Utils.bold("To categorize or tag a URL, use its label and a T:"));
send(sender, bold("To categorize or tag a URL, use its label and a T:"));
send(sender, helpIndent(Commands.LINK_CMD + "1T:<+tag|-tag> [...]"));
} else if (Commands.VIEW_CMD.equals(lcTopic)) {
send(sender, "To list or search the current URL posts:");
@ -685,7 +700,7 @@ public class Mobibot extends PircBot {
}
}
send(sender, Utils.bold("Type a URL on " + ircChannel + " to post it."));
send(sender, bold("Type a URL on " + ircChannel + " to post it."));
send(sender, "For more information on a specific command, type:");
send(sender, helpIndent(getNick() + ": " + Commands.HELP_CMD + " <command>"));
send(sender, "The commands are:");
@ -743,12 +758,12 @@ public class Mobibot extends PircBot {
*/
private void identify() {
// Identify with NickServ
if (StringUtils.isNotBlank(identPwd)) {
if (isNotBlank(identPwd)) {
identify(identPwd);
}
// Identify with a specified nick
if (StringUtils.isNotBlank(identNick) && StringUtils.isNotBlank(identMsg)) {
if (isNotBlank(identNick) && isNotBlank(identMsg)) {
sendMessage(identNick, identMsg);
}
}
@ -836,7 +851,7 @@ public class Mobibot extends PircBot {
* @return <code>true</code> if the nick should be ignored, <code>false</code> otherwise.
*/
private boolean isIgnoredNick(final String nick) {
return StringUtils.isNotBlank(nick) && ignoredNicks.contains(nick.toLowerCase(Constants.LOCALE));
return isNotBlank(nick) && ignoredNicks.contains(nick.toLowerCase(Constants.LOCALE));
}
/**
@ -871,7 +886,7 @@ public class Mobibot extends PircBot {
*/
@Override
protected final void onDisconnect() {
if (StringUtils.isNotBlank(weblogUrl)) {
if (isNotBlank(weblogUrl)) {
setVersion(weblogUrl);
}
@ -883,7 +898,8 @@ public class Mobibot extends PircBot {
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.")
@SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY",
justification = "Working on it.")
@Override
protected final void onMessage(final String channel, final String sender, final String login, final String hostname,
final String message) {
@ -923,7 +939,7 @@ public class Mobibot extends PircBot {
if (data.length == 1) {
title = data[0].trim();
} else {
if (StringUtils.isNotBlank(data[0])) {
if (isNotBlank(data[0])) {
title = data[0].trim();
}
@ -936,7 +952,7 @@ public class Mobibot extends PircBot {
final Document html = Jsoup.connect(link).userAgent("Mozilla").get();
final String htmlTitle = html.title();
if (StringUtils.isNotBlank(htmlTitle)) {
if (isNotBlank(htmlTitle)) {
title = htmlTitle;
}
} catch (IOException ignore) {
@ -962,7 +978,7 @@ public class Mobibot extends PircBot {
}
} else {
final EntryLink entry = entries.get(dupIndex);
send(sender, Utils.bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry));
send(sender, bold("Duplicate") + " >> " + EntriesUtils.buildLink(dupIndex, entry));
}
}
} else if (message.matches(getNickPattern() + ":.*")) { // mobibot: <command>
@ -1000,7 +1016,7 @@ public class Mobibot extends PircBot {
for (final AbstractModule module : MODULES) { // modules
for (final String c : module.getCommands()) {
if (cmd.startsWith(c)) {
module.commandResponse(this, sender, args, false);
module.commandResponse(this, sender, cmd, args, false);
}
}
}
@ -1185,7 +1201,8 @@ public class Mobibot extends PircBot {
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = {"DM_EXIT", "CC_CYCLOMATIC_COMPLEXITY"}, justification = "Yes, we want to bail out.")
@SuppressFBWarnings(value = { "DM_EXIT", "CC_CYCLOMATIC_COMPLEXITY" },
justification = "Yes, we want to bail out.")
@Override
protected final void onPrivateMessage(final String sender, final String login, final String hostname,
final String message) {
@ -1280,7 +1297,7 @@ public class Mobibot extends PircBot {
if (module.isPrivateMsgEnabled()) {
for (final String c : module.getCommands()) {
if (cmd.equals(c)) {
module.commandResponse(this, sender, args, true);
module.commandResponse(this, sender, cmd, args, true);
return;
}
}
@ -1352,7 +1369,7 @@ public class Mobibot extends PircBot {
* sent.
*/
public final void send(final String sender, final String message, final boolean isPrivate) {
if (StringUtils.isNotBlank(message) && StringUtils.isNotBlank(sender)) {
if (isNotBlank(message) && isNotBlank(sender)) {
if (isPrivate) {
if (logger.isDebugEnabled()) {
logger.debug("Sending message to {} : {}", sender, message);
@ -1460,7 +1477,7 @@ public class Mobibot extends PircBot {
* @param nicks The nicks to ignore
*/
final void setIgnoredNicks(final String nicks) {
if (StringUtils.isNotBlank(nicks)) {
if (isNotBlank(nicks)) {
final StringTokenizer st = new StringTokenizer(nicks, ",");
while (st.hasMoreTokens()) {
@ -1475,7 +1492,7 @@ public class Mobibot extends PircBot {
* @param apiToken The API token
*/
final void setPinboardAuth(final String apiToken) {
if (StringUtils.isNotBlank(apiToken)) {
if (isNotBlank(apiToken)) {
pinboard = new Pinboard(this, apiToken, ircServer);
}
}
@ -1520,7 +1537,7 @@ public class Mobibot extends PircBot {
* @param msg The twitter message.
*/
final void twitterNotification(final String msg) {
if (twitterModule.isEnabled() && StringUtils.isNotBlank(twitterHandle)) {
if (twitterModule.isEnabled() && isNotBlank(twitterHandle)) {
new Thread(() -> {
try {
twitterModule.post(twitterHandle, getName() + ' ' + ReleaseInfo.VERSION + " " + msg, true);
@ -1629,8 +1646,8 @@ public class Mobibot extends PircBot {
|| (entry.getNick().toLowerCase(Constants.LOCALE).contains(lcArgs))) {
if (sent > MAX_ENTRIES) {
send(sender,
"To view more, try: " + Utils
.bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs),
"To view more, try: "
+ bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1) + ' ' + lcArgs),
isPrivate);
break;
@ -1642,7 +1659,7 @@ public class Mobibot extends PircBot {
} else {
if (sent > MAX_ENTRIES) {
send(sender,
"To view more, try: " + Utils.bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)),
"To view more, try: " + bold(getNick() + ": " + Commands.VIEW_CMD + ' ' + (i + 1)),
isPrivate);
break;

View file

@ -1,3 +1,35 @@
/*
* TwitterOAuth.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;

View file

@ -1,7 +1,7 @@
/*
* Utils.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot;
import org.apache.commons.lang3.StringUtils;
import org.jibble.pircbot.Colors;
import org.jsoup.Jsoup;
import java.io.File;
import java.time.LocalDateTime;
@ -233,8 +234,7 @@ public final class Utils {
* @return The unescaped string.
*/
public static String unescapeXml(final String str) {
return str.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", "\"").replace(
"&apos;", "'").replace("&#39;", "'");
return Jsoup.parse(str).text();
}
/**

View file

@ -1,7 +1,7 @@
/*
* AbstractModule.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -57,11 +57,13 @@ public abstract class AbstractModule {
*
* @param bot The bot's instance.
* @param sender The sender.
* @param cmd The command.
* @param args The command arguments.
* @param isPrivate Set to <code>true</code> if the response should be sent as a private message.
*/
public abstract void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate);

View file

@ -1,7 +1,7 @@
/*
* Calc.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -81,7 +81,11 @@ public class Calc extends AbstractModule {
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
public void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate) {
if (StringUtils.isNotBlank(args)) {
bot.send(calc(args));

View file

@ -1,7 +1,7 @@
/*
* CurrencyConverter.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -87,44 +87,6 @@ public final class CurrencyConverter extends ThreadedModule {
commands.add(CURRENCY_CMD);
}
/**
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
synchronized (this) {
if (!pubDate.equals(Utils.today())) {
EXCHANGE_RATES.clear();
}
}
super.commandResponse(bot, sender, args, isPrivate);
}
/**
* Converts the specified currencies.
*/
@SuppressFBWarnings("REDOS")
@Override
void run(final Mobibot bot, final String sender, final String query) {
if (StringUtils.isNotBlank(sender) && StringUtils.isNotBlank(query)) {
if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) {
try {
final Message msg = convertCurrency(query);
if (msg.isError()) {
helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false);
}
bot.send(sender, msg);
} catch (ModuleException e) {
bot.getLogger().warn(e.getDebugMessage(), e);
bot.send(sender, e.getMessage());
}
} else {
helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true);
}
}
}
/**
* Converts from a currency to another.
*
@ -181,8 +143,8 @@ public final class CurrencyConverter extends ThreadedModule {
} else {
try {
final double amt = Double.parseDouble(cmds[0].replace(",", ""));
final double from = Double.parseDouble(EXCHANGE_RATES.get(cmds[1]
.toUpperCase(Constants.LOCALE)));
final double from =
Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase(Constants.LOCALE)));
final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase(Constants.LOCALE)));
return new PublicMessage(
@ -219,6 +181,48 @@ public final class CurrencyConverter extends ThreadedModule {
return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet());
}
/**
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate) {
synchronized (this) {
if (!pubDate.equals(Utils.today())) {
EXCHANGE_RATES.clear();
}
}
super.commandResponse(bot, sender, cmd, args, isPrivate);
}
/**
* Converts the specified currencies.
*/
@SuppressFBWarnings("REDOS")
@Override
void run(final Mobibot bot, final String sender, final String cmd, final String query) {
if (StringUtils.isNotBlank(sender) && StringUtils.isNotBlank(query)) {
if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) {
try {
final Message msg = convertCurrency(query);
if (msg.isError()) {
helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false);
}
bot.send(sender, msg);
} catch (ModuleException e) {
bot.getLogger().warn(e.getDebugMessage(), e);
bot.send(sender, e.getMessage());
}
} else {
helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true);
}
}
}
/**
* {@inheritDoc}
*/

View file

@ -1,7 +1,7 @@
/*
* Dice.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -57,15 +57,14 @@ public final class Dice extends AbstractModule {
}
/**
* Rolls the dice.
*
* @param bot The bot's instance.
* @param sender The sender.
* @param args The command arguments.
* @param isPrivate Set to <code>true</code> if the response should be sent as a private message.
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
public void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate) {
final SecureRandom r = new SecureRandom();
int i = r.nextInt(6) + 1;

View file

@ -1,7 +1,7 @@
/*
* GoogleSearch.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -96,7 +96,7 @@ public final class GoogleSearch extends ThreadedModule {
* Searches Google.
*/
@Override
void run(final Mobibot bot, final String sender, final String query) {
void run(final Mobibot bot, final String sender, final String cmd, final String query) {
if (StringUtils.isNotBlank(query)) {
try {
final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP),

View file

@ -1,7 +1,7 @@
/*
* Joke.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -100,15 +100,19 @@ public final class Joke extends ThreadedModule {
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
new Thread(() -> run(bot, sender, args)).start();
public void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate) {
new Thread(() -> run(bot, sender, cmd, args)).start();
}
/**
* Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>.
*/
@Override
void run(final Mobibot bot, final String sender, final String arg) {
void run(final Mobibot bot, final String sender, final String cmd, final String arg) {
try {
bot.send(Utils.cyan(randomJoke().getMessage()));
} catch (ModuleException e) {

View file

@ -1,7 +1,7 @@
/*
* Lookup.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -143,15 +143,14 @@ public final class Lookup extends AbstractModule {
}
/**
* Process a command.
*
* @param bot The bot's instance.
* @param sender The sender.
* @param args The command arguments.
* @param isPrivate Set to <code>true</code> if the response should be sent as a private message.
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
public void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate) {
if (args.matches("(\\S.)+(\\S)+")) {
try {
bot.send(Lookup.lookup(args));

View file

@ -1,7 +1,7 @@
/*
* Ping.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -80,7 +80,11 @@ public class Ping extends AbstractModule {
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
public void commandResponse(final Mobibot bot,
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())));
}

View file

@ -0,0 +1,116 @@
/*
* RockPaperScissors.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
/**
* Simple module example in Kotlin.
*/
class RockPaperScissors : AbstractModule() {
init {
with(commands) {
add(Shapes.ROCK.value)
add(Shapes.SCISSORS.value)
add(Shapes.PAPER.value)
}
}
enum class Shapes(val value: String) {
ROCK("rock"), PAPER("paper"), SCISSORS("scissors")
}
enum class Results {
WIN, LOSE, DRAW
}
companion object {
/**
* Returns the the randomly picked shape and result.
*/
fun winLoseOrDraw(hand: Shapes): Pair<Shapes, Results> {
val botHand = Shapes.values()[Random.nextInt(0, Shapes.values().size)]
val result: Results
if (botHand == hand) {
result = Results.DRAW
} else {
when (botHand) {
Shapes.ROCK -> {
result = if (hand == Shapes.PAPER) {
Results.WIN
} else {
Results.LOSE
}
}
Shapes.PAPER -> {
result = if (hand == Shapes.ROCK) {
Results.LOSE
} else {
Results.WIN
}
}
Shapes.SCISSORS -> {
result = if (hand == Shapes.ROCK) {
Results.WIN
} else {
Results.LOSE
}
}
}
}
return Pair(botHand, result)
}
}
override fun commandResponse(bot: Mobibot?, sender: String?, cmd: String?, args: String?, isPrivate: Boolean) {
val result = winLoseOrDraw(Shapes.valueOf(cmd!!.toUpperCase()))
val picked = "picked ${Utils.bold(result.first.value)}."
when (result.second) {
Results.WIN -> bot!!.action("$picked You win.")
Results.LOSE -> bot!!.action("$picked You lose.")
else -> bot!!.action("$picked We have a draw.")
}
}
override fun helpResponse(bot: Mobibot?, sender: String?, args: String?, isPrivate: Boolean) {
bot!!.send(sender, "To play Rock Paper Scissors:")
bot.send(
sender,
bot.helpIndent("${bot.nick}: ${Shapes.ROCK.value} or ${Shapes.PAPER.value} or ${Shapes.SCISSORS.value}")
)
}
}

View file

@ -1,7 +1,7 @@
/*
* StockQuote.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -211,7 +211,7 @@ public final class StockQuote extends ThreadedModule {
* Returns the specified stock quote from Alpha Advantage.
*/
@Override
void run(final Mobibot bot, final String sender, final String symbol) {
void run(final Mobibot bot, final String sender, final String cmd, final String symbol) {
if (StringUtils.isNotBlank(symbol)) {
try {
final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP));

View file

@ -1,7 +1,7 @@
/*
* ThreadedModule.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -46,9 +46,13 @@ public abstract class ThreadedModule extends AbstractModule {
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
public void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate) {
if (isEnabled() && args.length() > 0) {
new Thread(() -> run(bot, sender, args)).start();
new Thread(() -> run(bot, sender, cmd, args)).start();
} else {
helpResponse(bot, sender, args, isPrivate);
}
@ -57,5 +61,5 @@ public abstract class ThreadedModule extends AbstractModule {
/**
* Runs the thread.
*/
abstract void run(Mobibot bot, String sender, String args);
abstract void run(Mobibot bot, String sender, @SuppressWarnings("unused") String cmd, String args);
}

View file

@ -1,7 +1,7 @@
/*
* Twitter.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -148,7 +148,7 @@ public final class Twitter extends ThreadedModule {
* Posts to twitter.
*/
@Override
void run(final Mobibot bot, final String sender, final String message) {
void run(final Mobibot bot, final String sender, final String cmd, final String message) {
try {
bot.send(sender, post(sender, message, false).getMessage());
} catch (ModuleException e) {

View file

@ -1,7 +1,7 @@
/*
* War.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -62,15 +62,14 @@ public final class War extends AbstractModule {
}
/**
* Plays war.
*
* @param bot The bot's instance.
* @param sender The sender.
* @param args The command arguments.
* @param isPrivate Set to <code>true</code> if the response should be sent as a private message.
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
public void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate) {
final SecureRandom r = new SecureRandom();
int i;

View file

@ -1,7 +1,7 @@
/*
* Weather2.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -217,7 +217,7 @@ public class Weather2 extends ThreadedModule {
* Fetches the weather data from a specific city.
*/
@Override
void run(final Mobibot bot, final String sender, final String args) {
void run(final Mobibot bot, final String sender, final String cmd, final String args) {
if (StringUtils.isNotBlank(args)) {
try {
final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP));

View file

@ -1,7 +1,7 @@
/*
* WorldTime.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -146,9 +146,9 @@ public final class WorldTime extends AbstractModule {
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));
ZoneId.getAvailableZoneIds().stream()
.filter(tz -> !tz.contains("/") && tz.length() == 3 && !countries.containsKey(tz))
.forEach(tz -> countries.put(tz, tz));
COUNTRIES_MAP = Collections.unmodifiableMap(countries);
}
@ -161,49 +161,6 @@ public final class WorldTime extends AbstractModule {
commands.add(TIME_CMD);
}
/**
* Responds with the current time in the specified timezone/country.
*
* @param bot The bot's instance.
* @param sender The sender.
* @param args The command arguments.
* @param isPrivate Set to <code>true</code> if the response should be sent as a private message.
*/
@Override
public void commandResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
final Message msg = worldTime(args);
if (isPrivate) {
bot.send(sender, msg.getMessage(), true);
} else {
if (msg.isError()) {
bot.send(sender, msg.getMessage());
} else {
bot.send(msg.getMessage());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To display a country's current date/time:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]");
bot.send(sender, "For a listing of the supported countries:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD));
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPrivateMsgEnabled() {
return true;
}
/**
* Returns the current Internet (beat) Time.
*
@ -247,4 +204,46 @@ public final class WorldTime extends AbstractModule {
return new PublicMessage(response);
}
/**
* {@inheritDoc}
*/
@Override
public void commandResponse(final Mobibot bot,
final String sender,
final String cmd,
final String args,
final boolean isPrivate) {
final Message msg = worldTime(args);
if (isPrivate) {
bot.send(sender, msg.getMessage(), true);
} else {
if (msg.isError()) {
bot.send(sender, msg.getMessage());
} else {
bot.send(msg.getMessage());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
bot.send(sender, "To display a country's current date/time:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]");
bot.send(sender, "For a listing of the supported countries:");
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD));
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPrivateMsgEnabled() {
return true;
}
}

View file

@ -1,7 +1,7 @@
/*
* EntryLinkTest.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -1,7 +1,7 @@
/*
* AbstractModuleTest.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -0,0 +1,67 @@
/*
* RockPaperScissorsTest.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.modules.RockPaperScissors.Results
import net.thauvin.erik.mobibot.modules.RockPaperScissors.Shapes
import org.assertj.core.api.Assertions.assertThat
import org.testng.annotations.Test
class RockPaperScissorsTest {
@Test(invocationCount = 5)
fun testWinLoseOrDraw() {
var play = RockPaperScissors.winLoseOrDraw(Shapes.SCISSORS)
// println("SCISSORS vs ${play.first}: ${play.second}")
when (play.first) {
Shapes.SCISSORS -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.DRAW)
Shapes.ROCK -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.LOSE)
else -> assertThat(play.second).`as`("SCISSORS vs ${play.first}").isEqualTo(Results.WIN)
}
play = RockPaperScissors.winLoseOrDraw(Shapes.ROCK)
// println("ROCK vs ${play.first}: ${play.second}")
when (play.first) {
Shapes.SCISSORS -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.WIN)
Shapes.ROCK -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.DRAW)
else -> assertThat(play.second).`as`("ROCK vs ${play.first}").isEqualTo(Results.LOSE)
}
play = RockPaperScissors.winLoseOrDraw(Shapes.PAPER)
// println("PAPER vs ${play.first}: ${play.second}")
when (play.first) {
Shapes.SCISSORS -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.LOSE)
Shapes.ROCK -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.WIN)
else -> assertThat(play.second).`as`("PAPER vs ${play.first}").isEqualTo(Results.DRAW)
}
}
}

View file

@ -1,7 +1,7 @@
/*
* TellMessageTest.java
*
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -1,9 +1,9 @@
#Generated by the Semver Plugin for Gradle
#Wed Mar 18 16:00:11 PDT 2020
version.buildmeta=581
#Mon Mar 23 05:21:18 PDT 2020
version.buildmeta=682
version.major=0
version.minor=7
version.patch=3
version.prerelease=beta
version.project=mobibot
version.semver=0.7.3-beta+581
version.semver=0.7.3-beta+682