Moved tell methods to their own class.
This commit is contained in:
parent
015a368bd1
commit
3e11fd9ee6
9 changed files with 527 additions and 432 deletions
15
mobibot.ipr
15
mobibot.ipr
|
@ -58,9 +58,22 @@
|
|||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
<component name="CopyrightManager" default="" />
|
||||
<component name="CopyrightManager" default="">
|
||||
<copyright>
|
||||
<option name="myName" value="Copyright" />
|
||||
<option name="notice" value="&#36;file.fileName Copyright (c) 2004-&#36;today.year, 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." />
|
||||
</copyright>
|
||||
<module2copyright>
|
||||
<element module="Copyright" copyright="Copyright" />
|
||||
</module2copyright>
|
||||
<LanguageOptions name="JAVA">
|
||||
<option name="fileTypeOverride" value="3" />
|
||||
<option name="addBlankAfter" value="false" />
|
||||
</LanguageOptions>
|
||||
</component>
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="Source" pattern="file[mobibot]:src/generated/java//*||file[mobibot]:src/main/java//*" />
|
||||
<scope name="Copyright" pattern="file[mobibot]:src/main/java//*&&!file:src/main/java/net/thauvin/erik/mobibot/SwingWorker.java&&!file:src/main/java/net/thauvin/erik/mobibot/TwitterOAuth.java" />
|
||||
</component>
|
||||
<component name="Encoding">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
|
|
|
@ -13,11 +13,11 @@ import java.util.Date;
|
|||
* Annotation Processor</a>
|
||||
*/
|
||||
public final class ReleaseInfo {
|
||||
private final static String buildmeta = "001";
|
||||
private final static Date date = new Date(1467451308717L);
|
||||
private final static String buildmeta = "002";
|
||||
private final static Date date = new Date(1467486806272L);
|
||||
private final static int major = 0;
|
||||
private final static int minor = 6;
|
||||
private final static int patch = 5;
|
||||
private final static int minor = 7;
|
||||
private final static int patch = 0;
|
||||
private final static String prerelease = "beta";
|
||||
private final static String project = "mobibot";
|
||||
|
||||
|
|
|
@ -135,20 +135,6 @@ final class Commands
|
|||
*/
|
||||
public static final String SAY_CMD = "say";
|
||||
|
||||
/**
|
||||
* The {@link #TELL_CMD} all command.
|
||||
*/
|
||||
public static final String TELL_ALL_CMD = "all";
|
||||
|
||||
/**
|
||||
* The tell command.
|
||||
*/
|
||||
public static final String TELL_CMD = "tell";
|
||||
|
||||
/**
|
||||
* The {@link #TELL_CMD} delete command.
|
||||
*/
|
||||
public static final String TELL_DEL_CMD = "del";
|
||||
|
||||
/**
|
||||
* The users command.
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.jsoup.nodes.Document;
|
|||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Implements the #mobitopia bot.
|
||||
|
@ -71,17 +70,6 @@ public class Mobibot extends PircBot
|
|||
*/
|
||||
private static final int DEFAULT_PORT = 6667;
|
||||
|
||||
/**
|
||||
* The default maximum number of days to keep {@link Commands#TELL_CMD} messages.
|
||||
*/
|
||||
|
||||
private static final int DEFAULT_TELL_MAX_DAYS = 7;
|
||||
|
||||
/**
|
||||
* The default {@link Commands#TELL_CMD) message max queue size.
|
||||
*/
|
||||
private static final int DEFAULT_TELL_MAX_SIZE = 50;
|
||||
|
||||
/**
|
||||
* The info strings.
|
||||
*/
|
||||
|
@ -120,11 +108,6 @@ public class Mobibot extends PircBot
|
|||
*/
|
||||
private static final List<AbstractModule> MODULES = new ArrayList<>(0);
|
||||
|
||||
/**
|
||||
* The serialized object file extension.
|
||||
*/
|
||||
private static final String SER_EXT = ".ser";
|
||||
|
||||
/**
|
||||
* The start time.
|
||||
*/
|
||||
|
@ -148,6 +131,11 @@ public class Mobibot extends PircBot
|
|||
+ System.getProperty("java.vm.info") + ')'
|
||||
};
|
||||
|
||||
/**
|
||||
* The tell object.
|
||||
*/
|
||||
private static Tell tell;
|
||||
|
||||
/**
|
||||
* The main channel.
|
||||
*/
|
||||
|
@ -203,16 +191,6 @@ public class Mobibot extends PircBot
|
|||
*/
|
||||
private final List<String> recap = new ArrayList<>(0);
|
||||
|
||||
/**
|
||||
* The serialized object file.
|
||||
*/
|
||||
private final String serializedObject;
|
||||
|
||||
/**
|
||||
* Processes the {@link Commands#TELL_CMD} messages queue.
|
||||
*/
|
||||
private final List<TellMessage> tellMessages = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* The backlogs URL.
|
||||
*/
|
||||
|
@ -248,16 +226,6 @@ public class Mobibot extends PircBot
|
|||
*/
|
||||
private String identNick = "";
|
||||
|
||||
/**
|
||||
* The number of days message are kept.
|
||||
*/
|
||||
private int tellMaxDays = DEFAULT_TELL_MAX_DAYS;
|
||||
|
||||
/**
|
||||
* The maximum number of {@link Commands#TELL_CMD} messages allowed.
|
||||
*/
|
||||
private int tellMaxSize = DEFAULT_TELL_MAX_SIZE;
|
||||
|
||||
/**
|
||||
* Today's date.
|
||||
*/
|
||||
|
@ -290,7 +258,6 @@ public class Mobibot extends PircBot
|
|||
ircPort = port;
|
||||
this.channel = channel;
|
||||
this.logsDir = logsDir;
|
||||
this.serializedObject = logsDir + getName() + SER_EXT;
|
||||
|
||||
// Set the logger level
|
||||
loggerLevel = logger.getLogger().getLevel();
|
||||
|
@ -493,13 +460,12 @@ public class Mobibot extends PircBot
|
|||
final String dname = p.getProperty("delicious-user");
|
||||
final String dpwd = p.getProperty("delicious-pwd");
|
||||
|
||||
// Get the tell command settings
|
||||
final int tellMaxDays = Utils.getIntProperty(p.getProperty("tell-max-days"), DEFAULT_TELL_MAX_DAYS);
|
||||
final int tellMaxSize = Utils.getIntProperty(p.getProperty("tell-max-size"), DEFAULT_TELL_MAX_SIZE);
|
||||
|
||||
// Create the bot
|
||||
final Mobibot bot = new Mobibot(server, port, nickname, channel, logsDir);
|
||||
|
||||
// Get the tell command settings
|
||||
tell = new Tell(bot, p.getProperty("tell-max-days"), p.getProperty("tell-max-size"));
|
||||
|
||||
// Initialize the bot
|
||||
bot.setVerbose(true);
|
||||
bot.setAutoNickChange(true);
|
||||
|
@ -539,9 +505,6 @@ public class Mobibot extends PircBot
|
|||
// Set the ignored nicks
|
||||
bot.setIgnoredNicks(ignoredNicks);
|
||||
|
||||
// Set the tell command
|
||||
bot.setTell(tellMaxDays, tellMaxSize);
|
||||
|
||||
// Save the entries
|
||||
bot.saveEntries(true);
|
||||
|
||||
|
@ -590,13 +553,6 @@ public class Mobibot extends PircBot
|
|||
}
|
||||
|
||||
bot.joinChannel(channel);
|
||||
|
||||
// Load the messages queue
|
||||
bot.tellMessages.addAll(TellMessagesMgr.load(bot.getSerializedObject(), bot.getLogger()));
|
||||
if (bot.cleanTellMessages())
|
||||
{
|
||||
bot.saveTellMessages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -679,18 +635,6 @@ public class Mobibot extends PircBot
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Commands#TELL_CMD} parameters
|
||||
*
|
||||
* @param tellMaxDays The max number of days to hold messages for.
|
||||
* @param tellMaxSize The maximum number of messages to hold
|
||||
*/
|
||||
private void setTell(final int tellMaxDays, final int tellMaxSize)
|
||||
{
|
||||
this.tellMaxDays = tellMaxDays;
|
||||
this.tellMaxSize = tellMaxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the entries.
|
||||
*
|
||||
|
@ -718,49 +662,6 @@ public class Mobibot extends PircBot
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reruns the serialized object file.
|
||||
*
|
||||
* @return The file location.
|
||||
*/
|
||||
private String getSerializedObject()
|
||||
{
|
||||
return serializedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bot's logger.
|
||||
*
|
||||
* @return The bot's logger.
|
||||
*/
|
||||
public final Log4JLogger getLogger()
|
||||
{
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans the {@link #tellMessages} messages queue.
|
||||
*
|
||||
* @return <code>True</code> if the queue was cleaned.
|
||||
*/
|
||||
private boolean cleanTellMessages()
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Cleaning the messages.");
|
||||
}
|
||||
|
||||
return TellMessagesMgr.cleanTellMessages(tellMessages, tellMaxDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the {@link #tellMessages} messages queue.
|
||||
*/
|
||||
private void saveTellMessages()
|
||||
{
|
||||
TellMessagesMgr.save(getSerializedObject(), tellMessages, logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an action to the current channel.
|
||||
*
|
||||
|
@ -869,6 +770,16 @@ public class Mobibot extends PircBot
|
|||
return this.ircServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bot's logger.
|
||||
*
|
||||
* @return The bot's logger.
|
||||
*/
|
||||
public final Log4JLogger getLogger()
|
||||
{
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the log directory.
|
||||
*
|
||||
|
@ -958,7 +869,7 @@ public class Mobibot extends PircBot
|
|||
*
|
||||
* @return The indented help string.
|
||||
*/
|
||||
private String helpIndent(final String help, final boolean isBold)
|
||||
public String helpIndent(final String help, final boolean isBold)
|
||||
{
|
||||
return " " + (isBold ? Utils.bold(help) : help);
|
||||
}
|
||||
|
@ -1054,15 +965,9 @@ public class Mobibot extends PircBot
|
|||
send(sender, helpIndent(getNick() + ": " + Commands.IGNORE_CMD + ' ' + Commands.IGNORE_ME_KEYWORD));
|
||||
|
||||
}
|
||||
else if (lcTopic.equals(Commands.TELL_CMD))
|
||||
else if (lcTopic.equals(Tell.TELL_CMD) && tell.isEnabled())
|
||||
{
|
||||
send(sender, "To send a message to someone when they join the channel:");
|
||||
send(sender, helpIndent(getNick() + ": " + Commands.TELL_CMD + " <nick> <message>"));
|
||||
|
||||
send(sender, "To view queued and sent messages:");
|
||||
send(sender, helpIndent(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.VIEW_CMD));
|
||||
|
||||
send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days.");
|
||||
tell.helpResponse(sender);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1098,9 +1003,9 @@ public class Mobibot extends PircBot
|
|||
MODULES.stream().filter(AbstractModule::isEnabled)
|
||||
.forEach(module -> commandsList.addAll(module.getCommands()));
|
||||
|
||||
if (isTellEnabled())
|
||||
if (tell.isEnabled())
|
||||
{
|
||||
commandsList.add(Commands.TELL_CMD);
|
||||
commandsList.add(Tell.TELL_CMD);
|
||||
}
|
||||
|
||||
Collections.sort(commandsList);
|
||||
|
@ -1230,7 +1135,7 @@ public class Mobibot extends PircBot
|
|||
|
||||
send(sender,
|
||||
"Uptime: " + days + " day(s) " + hours + " hour(s) " + minutes + " minute(s) [Entries: " + entries.size()
|
||||
+ (isTellEnabled() && isOp(sender) ? ", Messages: " + tellMessages.size() : "") + ']',
|
||||
+ (tell.isEnabled() && isOp(sender) ? ", Messages: " + tell.size() : "") + ']',
|
||||
isPrivate);
|
||||
}
|
||||
|
||||
|
@ -1254,7 +1159,7 @@ public class Mobibot extends PircBot
|
|||
*
|
||||
* @return true, if the sender is an Op.
|
||||
*/
|
||||
private boolean isOp(final String sender)
|
||||
public boolean isOp(final String sender)
|
||||
{
|
||||
final User[] users = getUsers(channel);
|
||||
|
||||
|
@ -1482,9 +1387,9 @@ public class Mobibot extends PircBot
|
|||
viewResponse(sender, args, false);
|
||||
}
|
||||
// mobibot: tell
|
||||
else if (cmd.startsWith(Commands.TELL_CMD) && isTellEnabled())
|
||||
else if (cmd.startsWith(Tell.TELL_CMD) && tell.isEnabled())
|
||||
{
|
||||
tellResponse(sender, args);
|
||||
tell.response(sender, args);
|
||||
}
|
||||
// mobibot: ignore
|
||||
else if (cmd.startsWith(Commands.IGNORE_CMD))
|
||||
|
@ -1749,7 +1654,7 @@ public class Mobibot extends PircBot
|
|||
recap(sender, message, false);
|
||||
}
|
||||
|
||||
tellSendMessages(sender, true);
|
||||
tell.send(sender, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1870,9 +1775,9 @@ public class Mobibot extends PircBot
|
|||
{
|
||||
viewResponse(sender, args, true);
|
||||
}
|
||||
else if (cmd.equals(Commands.TELL_CMD) && isTellEnabled())
|
||||
else if (cmd.equals(Tell.TELL_CMD) && tell.isEnabled())
|
||||
{
|
||||
tellResponse(sender, args);
|
||||
tell.response(sender, args);
|
||||
}
|
||||
else if (cmd.equals(Commands.INFO_CMD))
|
||||
{
|
||||
|
@ -1882,21 +1787,18 @@ public class Mobibot extends PircBot
|
|||
{
|
||||
versionResponse(sender, true);
|
||||
}
|
||||
else if (cmd.equals(Commands.DEBUG_CMD))
|
||||
else if (cmd.equals(Commands.DEBUG_CMD) && isOp(sender))
|
||||
{
|
||||
if (isOp(sender))
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.getLogger().setLevel(loggerLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.getLogger().setLevel(Level.DEBUG);
|
||||
}
|
||||
|
||||
send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true);
|
||||
logger.getLogger().setLevel(loggerLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.getLogger().setLevel(Level.DEBUG);
|
||||
}
|
||||
|
||||
send(sender, "Debug logging is " + (logger.isDebugEnabled() ? "enabled." : "disabled."), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1950,91 +1852,29 @@ public class Mobibot extends PircBot
|
|||
@Override
|
||||
protected void onJoin(final String channel, final String sender, final String login, final String hostname)
|
||||
{
|
||||
tellSendMessages(sender);
|
||||
tell.send(sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick)
|
||||
{
|
||||
tellSendMessages(newNick);
|
||||
tell.send(newNick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends {@link Commands#TELL_CMD} messages.
|
||||
* Responds with the last 10 public messages.
|
||||
*
|
||||
* @param nickname The user's nickname.
|
||||
* @param sender The nick of the person who sent the private message.
|
||||
* @param isPrivate Set to true is the response should be send as a private message.
|
||||
*/
|
||||
private void tellSendMessages(final String nickname)
|
||||
private void recapResponse(final String sender, final boolean isPrivate)
|
||||
{
|
||||
tellSendMessages(nickname, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends {@link Commands#TELL_CMD} messages.
|
||||
*
|
||||
* @param nickname The user's nickname.
|
||||
* @param isMessage The message flag.
|
||||
*/
|
||||
private void tellSendMessages(final String nickname, final boolean isMessage)
|
||||
{
|
||||
if (!nickname.equals(getNick()) && isTellEnabled())
|
||||
for (final String recap : this.recap)
|
||||
{
|
||||
tellMessages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> {
|
||||
if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived())
|
||||
{
|
||||
if (message.getSender().equals(nickname))
|
||||
{
|
||||
if (!isMessage)
|
||||
{
|
||||
send(nickname,
|
||||
Utils.bold("You") + " wanted me to remind you: " + Utils
|
||||
.reverseColor(message.getMessage()), true);
|
||||
|
||||
message.setIsReceived();
|
||||
message.setIsNotified();
|
||||
|
||||
saveTellMessages();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
send(nickname,
|
||||
message.getSender() + " wanted me to tell you: " + Utils
|
||||
.reverseColor(message.getMessage()),
|
||||
true);
|
||||
|
||||
message.setIsReceived();
|
||||
|
||||
saveTellMessages();
|
||||
}
|
||||
}
|
||||
else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message
|
||||
.isNotified())
|
||||
{
|
||||
send(nickname,
|
||||
"Your message " + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to " + Utils
|
||||
.bold(message.getRecipient()) + " on " + Utils.UTC_SDF.format(message.getReceived()),
|
||||
true);
|
||||
|
||||
message.setIsNotified();
|
||||
|
||||
saveTellMessages();
|
||||
}
|
||||
});
|
||||
send(sender, recap, isPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if {@link Commands#TELL_CMD} is enabled.
|
||||
*
|
||||
* @return <code>true</code> or <code>false</code>
|
||||
*/
|
||||
|
||||
private boolean isTellEnabled()
|
||||
{
|
||||
return tellMaxSize > 0 && tellMaxDays > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a private message or notice.
|
||||
*
|
||||
|
@ -2067,20 +1907,6 @@ public class Mobibot extends PircBot
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the last 10 public messages.
|
||||
*
|
||||
* @param sender The nick of the person who sent the private message.
|
||||
* @param isPrivate Set to true is the response should be send as a private message.
|
||||
*/
|
||||
private void recapResponse(final String sender, final boolean isPrivate)
|
||||
{
|
||||
for (final String recap : this.recap)
|
||||
{
|
||||
send(sender, recap, isPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a private notice.
|
||||
*
|
||||
|
@ -2092,189 +1918,6 @@ public class Mobibot extends PircBot
|
|||
send(sender, message, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the {@link Commands#TELL_CMD} commands.
|
||||
*
|
||||
* @param sender The sender's nick.
|
||||
* @param cmds The commands string.
|
||||
*/
|
||||
private void tellResponse(final String sender, final String cmds)
|
||||
{
|
||||
if (!Utils.isValidString(cmds))
|
||||
{
|
||||
helpResponse(sender, Commands.TELL_CMD);
|
||||
}
|
||||
else if (cmds.startsWith(Commands.VIEW_CMD))
|
||||
{
|
||||
if (isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + Commands.TELL_ALL_CMD))
|
||||
{
|
||||
if (tellMessages.size() > 0)
|
||||
{
|
||||
for (final TellMessage message : tellMessages)
|
||||
{
|
||||
send(sender,
|
||||
Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " [ID: "
|
||||
+ message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED") + ']',
|
||||
true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
send(sender, "There are no messages in the queue.", true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean hasMessage = false;
|
||||
|
||||
for (final TellMessage message : tellMessages)
|
||||
{
|
||||
if (message.isMatch(sender))
|
||||
{
|
||||
if (!hasMessage)
|
||||
{
|
||||
hasMessage = true;
|
||||
send(sender, "Here are your messages: ", true);
|
||||
}
|
||||
|
||||
if (message.isReceived())
|
||||
{
|
||||
send(sender,
|
||||
Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " ["
|
||||
+ Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId()
|
||||
+ ", DELIVERED]",
|
||||
true);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
send(sender,
|
||||
Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient()) + " ["
|
||||
+ Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId() + ", QUEUED]",
|
||||
true);
|
||||
}
|
||||
|
||||
send(sender, helpIndent(message.getMessage(), false), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasMessage)
|
||||
{
|
||||
send(sender, "You have no messages in the queue.", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
send(sender, "To delete one or all delivered messages:");
|
||||
send(sender,
|
||||
helpIndent(getNick() + ": " + Commands.TELL_CMD + ' ' + Commands.TELL_DEL_CMD + " <id|"
|
||||
+ Commands.TELL_ALL_CMD + '>'));
|
||||
send(sender, "Messages are kept for " + Utils.bold(tellMaxDays) + " days.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cmds.startsWith(Commands.TELL_DEL_CMD + ' '))
|
||||
{
|
||||
final String[] split = cmds.split(" ");
|
||||
|
||||
if (split.length == 2)
|
||||
{
|
||||
final String id = split[1];
|
||||
boolean deleted = false;
|
||||
|
||||
if (id.equalsIgnoreCase(Commands.TELL_ALL_CMD))
|
||||
{
|
||||
for (final TellMessage message : tellMessages)
|
||||
{
|
||||
if (message.getSender().equalsIgnoreCase(sender) && message.isReceived())
|
||||
{
|
||||
tellMessages.remove(message);
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
saveTellMessages();
|
||||
send(sender, "Delivered messages have been deleted.", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
send(sender, "No delivered messages were found.", true);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean found = false;
|
||||
|
||||
for (final TellMessage message : tellMessages)
|
||||
{
|
||||
found = message.isMatchId(id);
|
||||
|
||||
if (found && (message.getSender().equalsIgnoreCase(sender) || isOp(sender)))
|
||||
{
|
||||
tellMessages.remove(message);
|
||||
|
||||
saveTellMessages();
|
||||
send(sender, "Your message was deleted from the queue.", true);
|
||||
deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!deleted)
|
||||
{
|
||||
if (found)
|
||||
{
|
||||
send(sender, "Only messages that you sent can be deleted.", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
send(sender, "The specified message [ID " + id + "] could not be found.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
helpResponse(sender, Commands.TELL_CMD);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
final String[] split = cmds.split(" ", 2);
|
||||
|
||||
if (split.length == 2 && (Utils.isValidString(split[1]) && split[1].contains(" ")))
|
||||
{
|
||||
if (tellMessages.size() < tellMaxSize)
|
||||
{
|
||||
final TellMessage message = new TellMessage(sender, split[0], split[1].trim());
|
||||
|
||||
tellMessages.add(message);
|
||||
|
||||
saveTellMessages();
|
||||
|
||||
send(sender,
|
||||
"Message [ID " + message.getId() + "] was queued for " + Utils.bold(message.getRecipient()),
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
send(sender, "Sorry, the messages queue is currently full.", true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
helpResponse(sender, Commands.TELL_CMD);
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanTellMessages())
|
||||
{
|
||||
saveTellMessages();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the users on a channel.
|
||||
*
|
||||
|
|
428
src/main/java/net/thauvin/erik/mobibot/Tell.java
Normal file
428
src/main/java/net/thauvin/erik/mobibot/Tell.java
Normal file
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* Tell.java
|
||||
*
|
||||
* Copyright (c) 2004-2016, 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 java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* The <code>Tell</code> command.
|
||||
*
|
||||
* @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a>
|
||||
* @created 2016-07-02
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Tell
|
||||
{
|
||||
/**
|
||||
* The all keyword.
|
||||
*/
|
||||
public static final String TELL_ALL_KEYWORD = "all";
|
||||
|
||||
/**
|
||||
* The tell command.
|
||||
*/
|
||||
public static final String TELL_CMD = "tell";
|
||||
|
||||
/**
|
||||
* The delete command.
|
||||
*/
|
||||
public static final String TELL_DEL_KEYWORD = "del";
|
||||
|
||||
/**
|
||||
* The default maximum number of days to keep messages.
|
||||
*/
|
||||
|
||||
private static final int DEFAULT_TELL_MAX_DAYS = 7;
|
||||
|
||||
/**
|
||||
* The default message max queue size.
|
||||
*/
|
||||
private static final int DEFAULT_TELL_MAX_SIZE = 50;
|
||||
|
||||
/**
|
||||
* The serialized object file extension.
|
||||
*/
|
||||
private static final String SER_EXT = ".ser";
|
||||
|
||||
/**
|
||||
* The bot instance.
|
||||
*/
|
||||
final private Mobibot bot;
|
||||
|
||||
/**
|
||||
* The maximum number of days to keep messages.
|
||||
*/
|
||||
final private int maxDays;
|
||||
|
||||
/**
|
||||
* The message maximum queue size.
|
||||
*/
|
||||
final private int maxSize;
|
||||
|
||||
/**
|
||||
* The messages queue.
|
||||
*/
|
||||
private final List<TellMessage> messages = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* The serialized object file.
|
||||
*/
|
||||
private final String serializedObject;
|
||||
|
||||
public Tell(final Mobibot bot, final String maxDays, final String maxSize)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS);
|
||||
this.maxSize = Utils.getIntProperty(maxSize, DEFAULT_TELL_MAX_SIZE);
|
||||
|
||||
// Load the message queue.
|
||||
serializedObject = bot.getLogsDir() + bot.getName() + SER_EXT;
|
||||
messages.addAll(TellMessagesMgr.load(serializedObject, bot.getLogger()));
|
||||
|
||||
if (clean())
|
||||
{
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans the messages queue.
|
||||
*
|
||||
* @return <code>True</code> if the queue was cleaned.
|
||||
*/
|
||||
private boolean clean()
|
||||
{
|
||||
if (bot.getLogger().isDebugEnabled())
|
||||
{
|
||||
bot.getLogger().debug("Cleaning the messages.");
|
||||
}
|
||||
|
||||
return TellMessagesMgr.clean(messages, maxDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the messages queue.
|
||||
*/
|
||||
private void save()
|
||||
{
|
||||
TellMessagesMgr.save(serializedObject, messages, bot.getLogger());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the commands.
|
||||
*
|
||||
* @param sender The sender's nick.
|
||||
* @param cmds The commands string.
|
||||
*/
|
||||
public void response(final String sender, final String cmds)
|
||||
{
|
||||
if (!Utils.isValidString(cmds))
|
||||
{
|
||||
helpResponse(sender);
|
||||
}
|
||||
else if (cmds.startsWith(Commands.VIEW_CMD))
|
||||
{
|
||||
if (bot.isOp(sender) && cmds.equals(Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD))
|
||||
{
|
||||
if (messages.size() > 0)
|
||||
{
|
||||
for (final TellMessage message : messages)
|
||||
{
|
||||
bot.send(sender,
|
||||
Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient())
|
||||
+ " [ID: " + message.getId() + ", " + (message.isReceived() ? "DELIVERED" : "QUEUED")
|
||||
+ ']',
|
||||
true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.send(sender, "There are no messages in the queue.", true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean hasMessage = false;
|
||||
|
||||
for (final TellMessage message : messages)
|
||||
{
|
||||
if (message.isMatch(sender))
|
||||
{
|
||||
if (!hasMessage)
|
||||
{
|
||||
hasMessage = true;
|
||||
bot.send(sender, "Here are your messages: ", true);
|
||||
}
|
||||
|
||||
if (message.isReceived())
|
||||
{
|
||||
bot.send(sender,
|
||||
Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient())
|
||||
+ " [" + Utils.UTC_SDF.format(message.getReceived()) + ", ID: " + message.getId()
|
||||
+ ", DELIVERED]",
|
||||
true);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.send(sender,
|
||||
Utils.bold(message.getSender()) + " --> " + Utils.bold(message.getRecipient())
|
||||
+ " [" + Utils.UTC_SDF.format(message.getQueued()) + ", ID: " + message.getId()
|
||||
+ ", QUEUED]",
|
||||
true);
|
||||
}
|
||||
|
||||
bot.send(sender, bot.helpIndent(message.getMessage(), false), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasMessage)
|
||||
{
|
||||
bot.send(sender, "You have no messages in the queue.", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.send(sender, "To delete one or all delivered messages:");
|
||||
bot.send(sender,
|
||||
bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|"
|
||||
+ TELL_ALL_KEYWORD + '>'));
|
||||
bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + " days.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cmds.startsWith(TELL_DEL_KEYWORD + ' '))
|
||||
{
|
||||
final String[] split = cmds.split(" ");
|
||||
|
||||
if (split.length == 2)
|
||||
{
|
||||
final String id = split[1];
|
||||
boolean deleted = false;
|
||||
|
||||
if (id.equalsIgnoreCase(TELL_ALL_KEYWORD))
|
||||
{
|
||||
for (final TellMessage message : messages)
|
||||
{
|
||||
if (message.getSender().equalsIgnoreCase(sender) && message.isReceived())
|
||||
{
|
||||
messages.remove(message);
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
save();
|
||||
bot.send(sender, "Delivered messages have been deleted.", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.send(sender, "No delivered messages were found.", true);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean found = false;
|
||||
|
||||
for (final TellMessage message : messages)
|
||||
{
|
||||
found = message.isMatchId(id);
|
||||
|
||||
if (found && (message.getSender().equalsIgnoreCase(sender) || bot.isOp(sender)))
|
||||
{
|
||||
messages.remove(message);
|
||||
|
||||
save();
|
||||
bot.send(sender, "Your message was deleted from the queue.", true);
|
||||
deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!deleted)
|
||||
{
|
||||
if (found)
|
||||
{
|
||||
bot.send(sender, "Only messages that you sent can be deleted.", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.send(sender, "The specified message [ID " + id + "] could not be found.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
helpResponse(sender);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
final String[] split = cmds.split(" ", 2);
|
||||
|
||||
if (split.length == 2 && (Utils.isValidString(split[1]) && split[1].contains(" ")))
|
||||
{
|
||||
if (messages.size() < maxSize)
|
||||
{
|
||||
final TellMessage message = new TellMessage(sender, split[0], split[1].trim());
|
||||
|
||||
messages.add(message);
|
||||
|
||||
save();
|
||||
|
||||
bot.send(sender,
|
||||
"Message [ID " + message.getId() + "] was queued for " + Utils
|
||||
.bold(message.getRecipient()), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.send(sender, "Sorry, the messages queue is currently full.", true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
helpResponse(sender);
|
||||
}
|
||||
}
|
||||
|
||||
if (clean())
|
||||
{
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with help.
|
||||
*
|
||||
* @param sender The sender.
|
||||
*/
|
||||
public void helpResponse(final String sender)
|
||||
{
|
||||
bot.send(sender, "To send a message to someone when they join the channel:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>"));
|
||||
|
||||
bot.send(sender, "To view queued and sent messages:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + Commands.VIEW_CMD));
|
||||
|
||||
bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + " days.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends messages.
|
||||
*
|
||||
* @param nickname The user's nickname.
|
||||
*/
|
||||
public void send(final String nickname)
|
||||
{
|
||||
send(nickname, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends messages.
|
||||
*
|
||||
* @param nickname The user's nickname.
|
||||
* @param isMessage The message flag.
|
||||
*/
|
||||
public void send(final String nickname, final boolean isMessage)
|
||||
{
|
||||
if (!nickname.equals(bot.getNick()) && isEnabled())
|
||||
{
|
||||
messages.stream().filter(message -> message.isMatch(nickname)).forEach(message -> {
|
||||
if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived())
|
||||
{
|
||||
if (message.getSender().equals(nickname))
|
||||
{
|
||||
if (!isMessage)
|
||||
{
|
||||
bot.send(nickname,
|
||||
Utils.bold("You") + " wanted me to remind you: " + Utils
|
||||
.reverseColor(message.getMessage()),
|
||||
true);
|
||||
|
||||
message.setIsReceived();
|
||||
message.setIsNotified();
|
||||
|
||||
save();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.send(nickname,
|
||||
message.getSender() + " wanted me to tell you: " + Utils
|
||||
.reverseColor(message.getMessage()),
|
||||
true);
|
||||
|
||||
message.setIsReceived();
|
||||
|
||||
save();
|
||||
}
|
||||
}
|
||||
else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived() && !message
|
||||
.isNotified())
|
||||
{
|
||||
bot.send(nickname,
|
||||
"Your message " + Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to "
|
||||
+ Utils.bold(message.getRecipient()) + " on " + Utils.UTC_SDF
|
||||
.format(message.getReceived()),
|
||||
true);
|
||||
|
||||
message.setIsNotified();
|
||||
|
||||
save();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if enabled.
|
||||
*
|
||||
* @return <code>true</code> or <code>false</code>
|
||||
*/
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return maxSize > 0 && maxDays > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the messages queue size.
|
||||
*
|
||||
* @return The size.
|
||||
*/
|
||||
public int size()
|
||||
{
|
||||
return messages.size();
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Managers the {@link Commands#TELL_CMD} messages.
|
||||
* The Tell Messages Manager.
|
||||
*
|
||||
* @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a>
|
||||
* @created 2014-04-26
|
||||
|
@ -67,7 +67,7 @@ final class TellMessagesMgr
|
|||
*
|
||||
* @return <code>True</code> if the queue was cleaned.
|
||||
*/
|
||||
public static boolean cleanTellMessages(final List<TellMessage> tellMessages, final int tellMaxDays)
|
||||
public static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays)
|
||||
{
|
||||
final Calendar maxDate = Calendar.getInstance();
|
||||
final Date today = new Date();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Module.java
|
||||
* AbstractModule.java
|
||||
*
|
||||
* Copyright (c) 2004-2016, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -37,7 +37,7 @@ import net.thauvin.erik.mobibot.Utils;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The <code>Module</code> class.
|
||||
* The <code>Module</code> abstract class.
|
||||
*
|
||||
* @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a>
|
||||
* @created 2016-07-01
|
||||
|
|
|
@ -1,8 +1,33 @@
|
|||
/*
|
||||
* Ping.java
|
||||
*
|
||||
* Copyright (c) 2016 Erik C. Thauvin (http://erik.thauvin.net/)
|
||||
* Copyright (c) 2004-2016, 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;
|
||||
|
||||
|
@ -13,7 +38,7 @@ import java.util.List;
|
|||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* The <code>Ping</code> class.
|
||||
* The Ping module.
|
||||
*
|
||||
* @author <a href="mailto:erik@thauvin.net">Erik C. Thauvin</a>
|
||||
* @created 2016-07-02
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#Mon Dec 07 01:31:00 PST 2015
|
||||
version.project=mobibot
|
||||
version.major=0
|
||||
version.minor=6
|
||||
version.patch=5
|
||||
version.minor=7
|
||||
version.patch=0
|
||||
version.prerelease=beta
|
||||
version.buildmeta=001
|
||||
version.buildmeta=002
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue