Converter Entry Links manipulation classes to Kotlin.
This commit is contained in:
parent
310ffb91da
commit
7759e278e8
22 changed files with 818 additions and 1213 deletions
|
@ -44,17 +44,10 @@ import java.util.*
|
|||
|
||||
/**
|
||||
* The class to handle posts to pinboard.in.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net)
|
||||
* @created 2017-05-17
|
||||
* @since 1.0
|
||||
*/
|
||||
object PinboardUtils {
|
||||
/**
|
||||
* Adds a pin.
|
||||
*
|
||||
* @param poster The PinboardPoster instance.
|
||||
* @param entry The entry to add.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) = runBlocking {
|
||||
|
@ -72,9 +65,6 @@ object PinboardUtils {
|
|||
|
||||
/**
|
||||
* Deletes a pin.
|
||||
*
|
||||
* @param poster The PinboardPoster instance.
|
||||
* @param entry The entry to delete.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun deletePin(poster: PinboardPoster, entry: EntryLink) = runBlocking {
|
||||
|
@ -86,33 +76,31 @@ object PinboardUtils {
|
|||
|
||||
/**
|
||||
* Updates a pin.
|
||||
*
|
||||
* @param poster The PinboardPoster instance.
|
||||
* @param oldUrl The old post URL.
|
||||
* @param entry The entry to add.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) = runBlocking {
|
||||
val update = GlobalScope.async {
|
||||
if (oldUrl != entry.link) {
|
||||
poster.deletePin(oldUrl)
|
||||
poster.addPin(
|
||||
entry.link,
|
||||
entry.title,
|
||||
postedBy(entry, ircServer),
|
||||
entry.pinboardTags,
|
||||
formatDate(entry.date)
|
||||
)
|
||||
} else {
|
||||
poster.addPin(
|
||||
entry.link,
|
||||
entry.title,
|
||||
postedBy(entry, ircServer),
|
||||
entry.pinboardTags,
|
||||
formatDate(entry.date),
|
||||
replace = true,
|
||||
shared = true
|
||||
)
|
||||
with(entry) {
|
||||
if (oldUrl != link) {
|
||||
poster.deletePin(oldUrl)
|
||||
poster.addPin(
|
||||
link,
|
||||
title,
|
||||
postedBy(entry, ircServer),
|
||||
pinboardTags,
|
||||
formatDate(date)
|
||||
)
|
||||
} else {
|
||||
poster.addPin(
|
||||
link,
|
||||
title,
|
||||
postedBy(entry, ircServer),
|
||||
pinboardTags,
|
||||
formatDate(date),
|
||||
replace = true,
|
||||
shared = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
update.await()
|
||||
|
@ -120,19 +108,13 @@ object PinboardUtils {
|
|||
|
||||
/**
|
||||
* Format a date to a UTC timestamp.
|
||||
*
|
||||
* @param date The date.
|
||||
* @return The date in [DateTimeFormatter.ISO_INSTANT] format.
|
||||
*/
|
||||
private fun formatDate(date: Date): String {
|
||||
return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns he pinboard.in extended attribution line.
|
||||
*
|
||||
* @param entry The entry.
|
||||
* @return The extended attribution line.
|
||||
* Returns the pinboard.in extended attribution line.
|
||||
*/
|
||||
private fun postedBy(entry: EntryLink, ircServer: String): String {
|
||||
return "Posted by ${entry.nick} on ${entry.channel} ( $ircServer )"
|
||||
|
|
|
@ -51,14 +51,16 @@ class Cycle(bot: Mobibot) : AbstractCommand(bot) {
|
|||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
bot.send("$sender has just asked me to leave. I'll be back!")
|
||||
bot.sleep(wait)
|
||||
bot.partChannel(bot.channel)
|
||||
bot.sleep(wait)
|
||||
bot.joinChannel(bot.channel)
|
||||
} else {
|
||||
bot.helpDefault(sender, isOp, isPrivate)
|
||||
with(bot) {
|
||||
if (isOp) {
|
||||
send("$sender has just asked me to leave. I'll be back!")
|
||||
sleep(wait)
|
||||
partChannel(channel)
|
||||
sleep(wait)
|
||||
joinChannel(channel)
|
||||
} else {
|
||||
helpDefault(sender, isOp, isPrivate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,17 +52,17 @@ class Modules(bot: Mobibot) : AbstractCommand(bot) {
|
|||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
with(bot.modulesNames) {
|
||||
if (isEmpty()) {
|
||||
bot.send(sender, "There are no enabled modules.", isPrivate)
|
||||
with(bot) {
|
||||
if (isOp) {
|
||||
if (modulesNames.isEmpty()) {
|
||||
send(sender, "There are no enabled modules.", isPrivate)
|
||||
} else {
|
||||
bot.send(sender, "The enabled modules are: ", isPrivate)
|
||||
bot.sendList(sender, this, 7, isPrivate, false)
|
||||
send(sender, "The enabled modules are: ", isPrivate)
|
||||
sendList(sender, modulesNames, 7, isPrivate, false)
|
||||
}
|
||||
} else {
|
||||
helpDefault(sender, isOp, isPrivate)
|
||||
}
|
||||
} else {
|
||||
bot.helpDefault(sender, isOp, isPrivate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
|||
if (index < LinksMgr.entriesCount) {
|
||||
val entry: EntryLink = LinksMgr.getEntry(index)
|
||||
val commentIndex = cmds[1].toInt() - 1
|
||||
if (commentIndex < entry.commentsCount) {
|
||||
if (commentIndex < entry.comments.size) {
|
||||
when (val cmd = cmds[2].trim()) {
|
||||
"" -> showComment(bot, entry, index, commentIndex) // L1.1:
|
||||
"-" -> deleteComment(bot, sender, isOp, entry, index, commentIndex) // L11:-
|
||||
|
@ -140,7 +140,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
|||
) {
|
||||
if (isOp || sender == entry.getComment(commentIndex).nick) {
|
||||
entry.deleteComment(commentIndex)
|
||||
bot.send("Comment ${Constants.LINK_CMD}${index + 1}.${commentIndex + 1} removed.")
|
||||
bot.send("Comment ${EntriesUtils.buildLinkCmd(index)}.${commentIndex + 1} removed.")
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
} else {
|
||||
bot.send(sender, "Please ask a channel op to delete this comment for you.", false)
|
||||
|
|
|
@ -40,6 +40,7 @@ import net.thauvin.erik.mobibot.commands.Ignore
|
|||
import net.thauvin.erik.mobibot.entries.EntriesMgr
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.jsoup.Jsoup
|
||||
import java.io.IOException
|
||||
|
||||
|
@ -166,7 +167,7 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
|||
bot.send(sender, "Please specify a title, by typing:", isPrivate)
|
||||
bot.send(
|
||||
sender,
|
||||
Utils.helpIndent(Constants.LINK_CMD + (index + 1) + ":|This is the title"),
|
||||
Utils.helpIndent("${EntriesUtils.buildLinkCmd(index)}:|This is the title"),
|
||||
isPrivate
|
||||
)
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) {
|
|||
if (entry.login == login || isOp) {
|
||||
bot.deletePin(index, entry)
|
||||
LinksMgr.removeEntry(index)
|
||||
bot.send("Entry ${Constants.LINK_CMD}${index + 1} removed.")
|
||||
bot.send("Entry ${EntriesUtils.buildLinkCmd(index)} removed.")
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
} else {
|
||||
bot.send(sender, "Please ask a channel op to remove this entry for you.", false)
|
||||
|
|
|
@ -104,7 +104,9 @@ public class Tell extends AbstractCommand {
|
|||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
final boolean clean() {
|
||||
getBot().getLogger().debug("Cleaning the messages.");
|
||||
if (getBot().getLogger().isDebugEnabled()) {
|
||||
getBot().getLogger().debug("Cleaning the messages.");
|
||||
}
|
||||
return TellMessagesMgr.clean(messages, maxDays);
|
||||
}
|
||||
|
||||
|
|
|
@ -94,8 +94,9 @@ final class TellMessagesMgr {
|
|||
try {
|
||||
try (final ObjectInput input = new ObjectInputStream(
|
||||
new BufferedInputStream(Files.newInputStream(Paths.get(file))))) {
|
||||
logger.debug("Loading the messages.");
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loading the messages.");
|
||||
}
|
||||
return ((List<TellMessage>) input.readObject());
|
||||
}
|
||||
} catch (FileNotFoundException ignore) {
|
||||
|
@ -118,7 +119,9 @@ final class TellMessagesMgr {
|
|||
try {
|
||||
try (final BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get(file)))) {
|
||||
try (final ObjectOutput output = new ObjectOutputStream(bos)) {
|
||||
logger.debug("Saving the messages.");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Saving the messages.");
|
||||
}
|
||||
output.writeObject(messages);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,331 +0,0 @@
|
|||
/*
|
||||
* EntriesMgr.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.entries;
|
||||
|
||||
import com.rometools.rome.feed.synd.SyndContent;
|
||||
import com.rometools.rome.feed.synd.SyndContentImpl;
|
||||
import com.rometools.rome.feed.synd.SyndEntry;
|
||||
import com.rometools.rome.feed.synd.SyndEntryImpl;
|
||||
import com.rometools.rome.feed.synd.SyndFeed;
|
||||
import com.rometools.rome.feed.synd.SyndFeedImpl;
|
||||
import com.rometools.rome.io.FeedException;
|
||||
import com.rometools.rome.io.SyndFeedInput;
|
||||
import com.rometools.rome.io.SyndFeedOutput;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages the feed entries.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-28
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class EntriesMgr {
|
||||
/**
|
||||
* The name of the file containing the current entries.
|
||||
*/
|
||||
public static final String CURRENT_XML = "current.xml";
|
||||
|
||||
/**
|
||||
* The name of the file containing the backlog entries.
|
||||
*/
|
||||
public static final String NAV_XML = "nav.xml";
|
||||
|
||||
/**
|
||||
* The .xml extension
|
||||
*/
|
||||
public static final String XML_EXT = ".xml";
|
||||
|
||||
// Maximum number of backlogs to keep
|
||||
private static final int MAX_BACKLOGS = 10;
|
||||
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*
|
||||
* @throws UnsupportedOperationException If the constructor is called.
|
||||
*/
|
||||
private EntriesMgr() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the backlogs.
|
||||
*
|
||||
* @param file The file containing the backlogs.
|
||||
* @param history The history list.
|
||||
* @throws IOException If the file was not found or could not be read.
|
||||
* @throws FeedException If an error occurred while reading the feed.
|
||||
*/
|
||||
public static void loadBacklogs(final String file, final Collection<String> history)
|
||||
throws IOException, FeedException {
|
||||
history.clear();
|
||||
|
||||
final SyndFeedInput input = new SyndFeedInput();
|
||||
|
||||
try (final InputStreamReader reader =
|
||||
new InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) {
|
||||
|
||||
final SyndFeed feed = input.build(reader);
|
||||
|
||||
final List<SyndEntry> items = feed.getEntries();
|
||||
SyndEntry item;
|
||||
|
||||
for (int i = items.size() - 1; i >= 0; i--) {
|
||||
item = items.get(i);
|
||||
history.add(item.getTitle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the current entries.
|
||||
*
|
||||
* @param file The file containing the current entries.
|
||||
* @param channel The channel
|
||||
* @param entries The entries.
|
||||
* @return The feed's last published date.
|
||||
* @throws IOException If the file was not found or could not be read.
|
||||
* @throws FeedException If an error occurred while reading the feed.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public static String loadEntries(final String file, final String channel, final Collection<EntryLink> entries)
|
||||
throws IOException, FeedException {
|
||||
entries.clear();
|
||||
|
||||
final SyndFeedInput input = new SyndFeedInput();
|
||||
|
||||
final String today;
|
||||
|
||||
try (final InputStreamReader reader = new InputStreamReader(
|
||||
Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) {
|
||||
final SyndFeed feed = input.build(reader);
|
||||
|
||||
today = Utils.isoLocalDate(feed.getPublishedDate());
|
||||
|
||||
final List<SyndEntry> items = feed.getEntries();
|
||||
SyndEntry item;
|
||||
SyndContent description;
|
||||
String[] comments;
|
||||
String author;
|
||||
EntryLink entry;
|
||||
|
||||
for (int i = items.size() - 1; i >= 0; i--) {
|
||||
item = items.get(i);
|
||||
author = item.getAuthor()
|
||||
.substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1);
|
||||
entry = new EntryLink(item.getLink(),
|
||||
item.getTitle(),
|
||||
author,
|
||||
channel,
|
||||
item.getPublishedDate(),
|
||||
item.getCategories());
|
||||
description = item.getDescription();
|
||||
comments = description.getValue().split("<br/>");
|
||||
|
||||
int split;
|
||||
for (final String comment : comments) {
|
||||
split = comment.indexOf(": ");
|
||||
|
||||
if (split != -1) {
|
||||
entry.addComment(comment.substring(split + 2).trim(), comment.substring(0, split).trim());
|
||||
}
|
||||
}
|
||||
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return today;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the entries.
|
||||
*
|
||||
* @param bot The bot object.
|
||||
* @param entries The entries array.
|
||||
* @param history The history array.
|
||||
* @param isDayBackup Set the true if the daily backup file should also be created.
|
||||
*/
|
||||
@SuppressFBWarnings(value = { "CE_CLASS_ENVY", "CC_CYCLOMATIC_COMPLEXITY" },
|
||||
justification = "Yes, it does.")
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public static void saveEntries(final Mobibot bot,
|
||||
final List<EntryLink> entries,
|
||||
final List<String> history,
|
||||
final boolean isDayBackup) {
|
||||
bot.getLogger().debug("Saving the feeds...");
|
||||
|
||||
if (StringUtils.isNotBlank(bot.getLogsDir()) && StringUtils.isNotBlank(bot.getWeblogUrl())) {
|
||||
try {
|
||||
final SyndFeedOutput output = new SyndFeedOutput();
|
||||
SyndFeed rss = new SyndFeedImpl();
|
||||
final List<SyndEntry> items = new ArrayList<>(0);
|
||||
SyndEntry item;
|
||||
SyndContent description;
|
||||
try (final Writer fw = new OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get(bot.getLogsDir() + CURRENT_XML)), StandardCharsets.UTF_8)) {
|
||||
rss.setFeedType("rss_2.0");
|
||||
rss.setTitle(bot.getChannel() + " IRC Links");
|
||||
rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel());
|
||||
rss.setLink(bot.getWeblogUrl());
|
||||
rss.setPublishedDate(Calendar.getInstance().getTime());
|
||||
rss.setLanguage("en");
|
||||
|
||||
EntryLink entry;
|
||||
StringBuilder buff;
|
||||
EntryComment comment;
|
||||
|
||||
for (int i = (entries.size() - 1); i >= 0; --i) {
|
||||
entry = entries.get(i);
|
||||
|
||||
buff = new StringBuilder()
|
||||
.append("Posted by <b>")
|
||||
.append(entry.getNick())
|
||||
.append("</b> on <a href=\"irc://")
|
||||
.append(bot.getIrcServer()).append('/')
|
||||
.append(entry.getChannel())
|
||||
.append("\"><b>")
|
||||
.append(entry.getChannel())
|
||||
.append("</b></a>");
|
||||
|
||||
if (entry.getCommentsCount() > 0) {
|
||||
buff.append(" <br/><br/>");
|
||||
|
||||
final EntryComment[] comments = entry.getComments();
|
||||
|
||||
for (int j = 0; j < comments.length; j++) {
|
||||
comment = comments[j];
|
||||
|
||||
if (j > 0) {
|
||||
buff.append(" <br/>");
|
||||
}
|
||||
|
||||
buff.append(comment.getNick()).append(": ").append(comment.getComment());
|
||||
}
|
||||
}
|
||||
|
||||
item = new SyndEntryImpl();
|
||||
item.setLink(entry.getLink());
|
||||
description = new SyndContentImpl();
|
||||
description.setValue(buff.toString());
|
||||
item.setDescription(description);
|
||||
item.setTitle(entry.getTitle());
|
||||
item.setPublishedDate(entry.getDate());
|
||||
item.setAuthor(
|
||||
bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick()
|
||||
+ ')');
|
||||
item.setCategories(entry.getTags());
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
rss.setEntries(items);
|
||||
|
||||
bot.getLogger().debug("Writing the entries feed.");
|
||||
output.output(rss, fw);
|
||||
}
|
||||
|
||||
try (final Writer fw = new OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get(
|
||||
bot.getLogsDir() + bot.getToday() + XML_EXT)), StandardCharsets.UTF_8)) {
|
||||
output.output(rss, fw);
|
||||
}
|
||||
|
||||
if (isDayBackup) {
|
||||
if (StringUtils.isNotBlank(bot.getBacklogsUrl())) {
|
||||
if (!history.contains(bot.getToday())) {
|
||||
history.add(bot.getToday());
|
||||
|
||||
while (history.size() > MAX_BACKLOGS) {
|
||||
history.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
try (final Writer fw = new OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get(bot.getLogsDir() + NAV_XML)), StandardCharsets.UTF_8)) {
|
||||
rss = new SyndFeedImpl();
|
||||
rss.setFeedType("rss_2.0");
|
||||
rss.setTitle(bot.getChannel() + " IRC Links Backlogs");
|
||||
rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on "
|
||||
+ bot.getChannel());
|
||||
rss.setLink(bot.getBacklogsUrl());
|
||||
rss.setPublishedDate(Calendar.getInstance().getTime());
|
||||
|
||||
String date;
|
||||
items.clear();
|
||||
|
||||
for (int i = (history.size() - 1); i >= 0; --i) {
|
||||
date = history.get(i);
|
||||
|
||||
item = new SyndEntryImpl();
|
||||
item.setLink(bot.getBacklogsUrl() + date + ".xml");
|
||||
item.setTitle(date);
|
||||
description = new SyndContentImpl();
|
||||
description.setValue("Links for " + date);
|
||||
item.setDescription(description);
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
rss.setEntries(items);
|
||||
|
||||
bot.getLogger().debug("Writing the backlog feed.");
|
||||
output.output(rss, fw);
|
||||
}
|
||||
} else {
|
||||
bot.getLogger().warn("Unable to generate the backlogs feed. No property configured.");
|
||||
}
|
||||
}
|
||||
} catch (FeedException | IOException e) {
|
||||
bot.getLogger().warn("Unable to generate the entries feed.", e);
|
||||
}
|
||||
} else {
|
||||
bot.getLogger().warn("Unable to generate the entries feed. A required property is missing.");
|
||||
}
|
||||
}
|
||||
}
|
261
src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt
Normal file
261
src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* EntriesMgr.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.entries
|
||||
|
||||
import com.rometools.rome.feed.synd.SyndContentImpl
|
||||
import com.rometools.rome.feed.synd.SyndEntry
|
||||
import com.rometools.rome.feed.synd.SyndEntryImpl
|
||||
import com.rometools.rome.feed.synd.SyndFeed
|
||||
import com.rometools.rome.feed.synd.SyndFeedImpl
|
||||
import com.rometools.rome.io.FeedException
|
||||
import com.rometools.rome.io.SyndFeedInput
|
||||
import com.rometools.rome.io.SyndFeedOutput
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils.Companion.isoLocalDate
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStreamWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* Manages the feed entries.
|
||||
*/
|
||||
class EntriesMgr private constructor() {
|
||||
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The name of the file containing the current entries.
|
||||
*/
|
||||
const val CURRENT_XML = "current.xml"
|
||||
|
||||
/**
|
||||
* The name of the file containing the backlog entries.
|
||||
*/
|
||||
const val NAV_XML = "nav.xml"
|
||||
|
||||
/**
|
||||
* The .xml extension
|
||||
*/
|
||||
const val XML_EXT = ".xml"
|
||||
|
||||
// Maximum number of backlogs to keep
|
||||
private const val MAX_BACKLOGS = 10
|
||||
|
||||
/**
|
||||
* Loads the backlogs.
|
||||
*/
|
||||
@Throws(IOException::class, FeedException::class)
|
||||
fun loadBacklogs(file: String, history: ArrayList<String>) {
|
||||
history.clear()
|
||||
val input = SyndFeedInput()
|
||||
InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8).use { reader ->
|
||||
val feed = input.build(reader)
|
||||
val items = feed.entries
|
||||
for (i in items.indices.reversed()) {
|
||||
history.add(items[i].title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the current entries.
|
||||
*/
|
||||
@Throws(IOException::class, FeedException::class)
|
||||
fun loadEntries(file: String, channel: String, entries: ArrayList<EntryLink>): String {
|
||||
entries.clear()
|
||||
val input = SyndFeedInput()
|
||||
var today: String
|
||||
InputStreamReader(
|
||||
Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8
|
||||
).use { reader ->
|
||||
val feed = input.build(reader)
|
||||
today = isoLocalDate(feed.publishedDate)
|
||||
val items = feed.entries
|
||||
var entry: EntryLink
|
||||
for (i in items.indices.reversed()) {
|
||||
with(items[i]) {
|
||||
entry = EntryLink(
|
||||
link,
|
||||
title,
|
||||
author.substring(author.lastIndexOf('(') + 1, author.length - 1),
|
||||
channel,
|
||||
publishedDate,
|
||||
categories
|
||||
)
|
||||
var split: List<String>
|
||||
for (comment in description.value.split("<br/>")) {
|
||||
split = comment.split(": ".toRegex(), 2)
|
||||
if (split.size == 2) {
|
||||
entry.addComment(comment = split[1].trim(), nick = split[0].trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
entries.add(entry)
|
||||
}
|
||||
}
|
||||
return today
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the entries.
|
||||
*/
|
||||
fun saveEntries(
|
||||
bot: Mobibot,
|
||||
entries: List<EntryLink>,
|
||||
history: MutableList<String>,
|
||||
isDayBackup: Boolean
|
||||
) {
|
||||
if (bot.logger.isDebugEnabled) {
|
||||
bot.logger.debug("Saving the feeds...")
|
||||
}
|
||||
if (StringUtils.isNotBlank(bot.logsDir) && StringUtils.isNotBlank(bot.weblogUrl)) {
|
||||
try {
|
||||
val output = SyndFeedOutput()
|
||||
var rss: SyndFeed = SyndFeedImpl()
|
||||
val items: MutableList<SyndEntry> = ArrayList(0)
|
||||
var item: SyndEntry
|
||||
OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8
|
||||
).use { fw ->
|
||||
rss.apply {
|
||||
feedType = "rss_2.0"
|
||||
title = bot.channel + " IRC Links"
|
||||
description = "Links from ${bot.ircServer} on ${bot.channel}"
|
||||
link = bot.weblogUrl
|
||||
publishedDate = Calendar.getInstance().time
|
||||
language = "en"
|
||||
}
|
||||
var buff: StringBuilder
|
||||
var comment: EntryComment
|
||||
for (i in entries.size - 1 downTo 0) {
|
||||
with(entries[i]) {
|
||||
buff = StringBuilder()
|
||||
.append("Posted by <b>")
|
||||
.append(nick)
|
||||
.append("</b> on <a href=\"irc://")
|
||||
.append(bot.ircServer).append('/')
|
||||
.append(channel)
|
||||
.append("\"><b>")
|
||||
.append(channel)
|
||||
.append("</b></a>")
|
||||
if (comments.size > 0) {
|
||||
buff.append(" <br/><br/>")
|
||||
val comments = comments
|
||||
for (j in comments.indices) {
|
||||
comment = comments[j]
|
||||
if (j > 0) {
|
||||
buff.append(" <br/>")
|
||||
}
|
||||
buff.append(comment.nick).append(": ").append(comment.comment)
|
||||
}
|
||||
}
|
||||
item = SyndEntryImpl()
|
||||
item.link = link
|
||||
item.description = SyndContentImpl().apply { value = buff.toString() }
|
||||
item.title = title
|
||||
item.publishedDate = date
|
||||
item.author = "${bot.channel.substring(1)}@${bot.ircServer} ($nick)"
|
||||
item.categories = tags
|
||||
items.add(item)
|
||||
}
|
||||
}
|
||||
rss.entries = items
|
||||
if (bot.logger.isDebugEnabled) {
|
||||
bot.logger.debug("Writing the entries feed.")
|
||||
}
|
||||
output.output(rss, fw)
|
||||
}
|
||||
OutputStreamWriter(
|
||||
Files.newOutputStream(
|
||||
Paths.get(
|
||||
bot.logsDir + bot.today + XML_EXT
|
||||
)
|
||||
), StandardCharsets.UTF_8
|
||||
).use { fw -> output.output(rss, fw) }
|
||||
if (isDayBackup) {
|
||||
if (StringUtils.isNotBlank(bot.backlogsUrl)) {
|
||||
if (!history.contains(bot.today)) {
|
||||
history.add(bot.today)
|
||||
while (history.size > MAX_BACKLOGS) {
|
||||
history.removeAt(0)
|
||||
}
|
||||
}
|
||||
OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8
|
||||
).use { fw ->
|
||||
rss = SyndFeedImpl()
|
||||
rss.apply {
|
||||
feedType = "rss_2.0"
|
||||
title = "${bot.channel} IRC Links Backlogs"
|
||||
description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}"
|
||||
link = bot.backlogsUrl
|
||||
publishedDate = Calendar.getInstance().time
|
||||
}
|
||||
var date: String
|
||||
items.clear()
|
||||
for (i in history.size - 1 downTo 0) {
|
||||
date = history[i]
|
||||
item = SyndEntryImpl()
|
||||
item.apply {
|
||||
link = bot.backlogsUrl + date + ".xml"
|
||||
title = date
|
||||
description = SyndContentImpl().apply { value = "Links for $date" }
|
||||
}
|
||||
items.add(item)
|
||||
}
|
||||
rss.entries = items
|
||||
if (bot.logger.isDebugEnabled) {
|
||||
bot.logger.debug("Writing the backlog feed.")
|
||||
}
|
||||
output.output(rss, fw)
|
||||
}
|
||||
} else {
|
||||
bot.logger.warn("Unable to generate the backlogs feed. No property configured.")
|
||||
}
|
||||
}
|
||||
} catch (e: FeedException) {
|
||||
bot.logger.warn("Unable to generate the entries feed.", e)
|
||||
} catch (e: IOException) {
|
||||
bot.logger.warn("Unable to generate the entries feed.", e)
|
||||
}
|
||||
} else {
|
||||
bot.logger.warn("Unable to generate the entries feed. A required property is missing.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* EntriesUtils.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.entries;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Constants;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
|
||||
/**
|
||||
* The <code>Utils</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-19
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class EntriesUtils {
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*/
|
||||
private EntriesUtils() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an entry's comment for display on the channel.
|
||||
*
|
||||
* @param entryIndex The entry's index.
|
||||
* @param commentIndex The comment's index.
|
||||
* @param comment The {@link EntryComment comment} object.
|
||||
* @return The entry's comment.
|
||||
*/
|
||||
public static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) {
|
||||
return (Constants.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] "
|
||||
+ comment.getComment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an entry's link for display on the channel.
|
||||
*
|
||||
* @param index The entry's index.
|
||||
* @param entry The {@link EntryLink entry} object.
|
||||
* @return The entry's link.
|
||||
* @see #buildLink(int, EntryLink, boolean)
|
||||
*/
|
||||
public static String buildLink(final int index, final EntryLink entry) {
|
||||
return buildLink(index, entry, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an entry's link for display on the channel.
|
||||
*
|
||||
* @param index The entry's index.
|
||||
* @param entry The {@link EntryLink entry} object.
|
||||
* @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.")
|
||||
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(']');
|
||||
|
||||
if (isView && entry.hasComments()) {
|
||||
buff.append("[+").append(entry.getCommentsCount()).append(']');
|
||||
}
|
||||
|
||||
buff.append(' ');
|
||||
|
||||
if (Constants.NO_TITLE.equals(entry.getTitle())) {
|
||||
buff.append(entry.getTitle());
|
||||
} else {
|
||||
buff.append(Utils.bold(entry.getTitle()));
|
||||
}
|
||||
|
||||
buff.append(" ( ").append(Utils.green(entry.getLink())).append(" )");
|
||||
|
||||
return buff.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an entry's tags/categories for display on the channel.
|
||||
*
|
||||
* @param entryIndex The entry's index.
|
||||
* @param entry The {@link EntryLink entry} object.
|
||||
* @return The entry's tags.
|
||||
*/
|
||||
public static String buildTags(final int entryIndex, final EntryLink entry) {
|
||||
return (Constants.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replace(",", ", "));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* EntriesUtils.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.entries
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.Companion.bold
|
||||
import net.thauvin.erik.mobibot.Utils.Companion.green
|
||||
|
||||
/**
|
||||
* The `Utils` class.
|
||||
*/
|
||||
class EntriesUtils private constructor() {
|
||||
companion object {
|
||||
/**
|
||||
* Build link cmd based on its index. e.g: L1
|
||||
*/
|
||||
fun buildLinkCmd(index: Int): String = Constants.LINK_CMD + (index + 1)
|
||||
|
||||
/**
|
||||
* Builds an entry's comment for display on the channel.
|
||||
*/
|
||||
fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String =
|
||||
(buildLinkCmd(entryIndex) + '.' + (commentIndex + 1) + ": [" + comment.nick + "] "
|
||||
+ comment.comment)
|
||||
|
||||
/**
|
||||
* Builds an entry's link for display on the channel.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String {
|
||||
val buff = StringBuilder().append(buildLinkCmd(entryIndex)).append(": ")
|
||||
.append('[').append(entry.nick).append(']')
|
||||
if (isView && entry.hasComments()) {
|
||||
buff.append("[+").append(entry.comments.size).append(']')
|
||||
}
|
||||
buff.append(' ')
|
||||
with(entry) {
|
||||
if (Constants.NO_TITLE == title) {
|
||||
buff.append(title)
|
||||
} else {
|
||||
buff.append(bold(title))
|
||||
}
|
||||
buff.append(" ( ").append(green(link)).append(" )")
|
||||
}
|
||||
return buff.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an entry's tags/categories for display on the channel.
|
||||
*/
|
||||
fun buildTags(entryIndex: Int, entry: EntryLink): String =
|
||||
buildLinkCmd(entryIndex) + "T: " + entry.pinboardTags.replace(",", ", ")
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* EntryComment.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.entries;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* The class used to store comments associated to a specific entry.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Jan 31, 2004
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.DataClass" })
|
||||
public class EntryComment implements Serializable {
|
||||
// Serial version UID
|
||||
static final long serialVersionUID = 1L;
|
||||
|
||||
// Creation date
|
||||
private final LocalDateTime date = LocalDateTime.now();
|
||||
|
||||
private String comment = "";
|
||||
private String nick = "";
|
||||
|
||||
/**
|
||||
* Creates a new comment.
|
||||
*
|
||||
* @param comment The new comment.
|
||||
* @param nick The nickname of the comment's author.
|
||||
*/
|
||||
public EntryComment(final String comment, final String nick) {
|
||||
this.comment = comment;
|
||||
this.nick = nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new comment.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
protected EntryComment() {
|
||||
// Required for serialization
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment.
|
||||
*
|
||||
* @return The comment.
|
||||
*/
|
||||
public final String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's creation date.
|
||||
*
|
||||
* @return The date.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public final LocalDateTime getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nickname of the author of the comment.
|
||||
*
|
||||
* @return The nickname.
|
||||
*/
|
||||
public final String getNick() {
|
||||
return nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment.
|
||||
*
|
||||
* @param comment The actual comment.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public final void setComment(final String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the nickname of the author of the comment.
|
||||
*
|
||||
* @param nick The new nickname.
|
||||
*/
|
||||
public final void setNick(final String nick) {
|
||||
this.nick = nick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EntryComment{"
|
||||
+ "comment='" + comment + '\''
|
||||
+ ", date=" + date
|
||||
+ ", nick='" + nick + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* EntryComment.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.entries
|
||||
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Entry comments data class.
|
||||
*/
|
||||
data class EntryComment(var comment: String, var nick: String) : Serializable {
|
||||
/**
|
||||
* Creation date.
|
||||
*/
|
||||
val date: LocalDateTime = LocalDateTime.now()
|
||||
|
||||
override fun toString(): String {
|
||||
return ("EntryComment{comment='$comment', date=$date, nick='$nick'}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Serial version UID
|
||||
const val serialVersionUID = 1L
|
||||
}
|
||||
}
|
|
@ -1,418 +0,0 @@
|
|||
/*
|
||||
* EntryLink.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.entries;
|
||||
|
||||
import com.rometools.rome.feed.synd.SyndCategory;
|
||||
import com.rometools.rome.feed.synd.SyndCategoryImpl;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* The class used to store link entries.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Jan 31, 2004
|
||||
* @since 1.0
|
||||
*/
|
||||
public class EntryLink implements Serializable {
|
||||
// Serial version UID
|
||||
static final long serialVersionUID = 1L;
|
||||
|
||||
// Link's comments
|
||||
private final List<EntryComment> comments = new CopyOnWriteArrayList<>();
|
||||
|
||||
// Tags/categories
|
||||
private final List<SyndCategory> tags = new CopyOnWriteArrayList<>();
|
||||
|
||||
// Channel
|
||||
private String channel;
|
||||
|
||||
// Creation date
|
||||
private Date date = Calendar.getInstance().getTime();
|
||||
|
||||
// Link's URL
|
||||
private String link;
|
||||
|
||||
// Author's login
|
||||
private String login = "";
|
||||
|
||||
// Author's nickname
|
||||
private String nick;
|
||||
|
||||
// Link's title
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* Creates a new entry.
|
||||
*
|
||||
* @param link The new entry's link.
|
||||
* @param title The new entry's title.
|
||||
* @param nick The nickname of the author of the link.
|
||||
* @param login The login of the author of the link.
|
||||
* @param channel The channel.
|
||||
* @param tags The entry's tags/categories.
|
||||
*/
|
||||
public EntryLink(final String link,
|
||||
final String title,
|
||||
final String nick,
|
||||
final String login,
|
||||
final String channel,
|
||||
final List<String> tags) {
|
||||
this.link = link;
|
||||
this.title = title;
|
||||
this.nick = nick;
|
||||
this.login = login;
|
||||
this.channel = channel;
|
||||
|
||||
setTags(tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry.
|
||||
*
|
||||
* @param link The new entry's link.
|
||||
* @param title The new entry's title.
|
||||
* @param nick The nickname of the author of the link.
|
||||
* @param channel The channel.
|
||||
* @param date The entry date.
|
||||
* @param tags The entry's tags/categories.
|
||||
*/
|
||||
public EntryLink(final String link,
|
||||
final String title,
|
||||
final String nick,
|
||||
final String channel,
|
||||
final Date date,
|
||||
final List<SyndCategory> tags) {
|
||||
this.link = link;
|
||||
this.title = title;
|
||||
this.nick = nick;
|
||||
this.channel = channel;
|
||||
this.date = new Date(date.getTime());
|
||||
this.tags.addAll(tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new comment.
|
||||
*
|
||||
* @param comment The actual comment.
|
||||
* @param nick The nickname of the author of the comment.
|
||||
* @return The total number of comments for this entry.
|
||||
*/
|
||||
public final int addComment(final String comment, final String nick) {
|
||||
comments.add(new EntryComment(comment, nick));
|
||||
|
||||
return (comments.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a specific comment.
|
||||
*
|
||||
* @param index The index of the comment to delete.
|
||||
*/
|
||||
public final void deleteComment(final int index) {
|
||||
if (index < comments.size()) {
|
||||
comments.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel the link was posted on.
|
||||
*
|
||||
* @return The channel
|
||||
*/
|
||||
public final String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a comment.
|
||||
*
|
||||
* @param index The comment's index.
|
||||
* @return The specific comment.
|
||||
*/
|
||||
public final EntryComment getComment(final int index) {
|
||||
return (comments.get(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the comments.
|
||||
*
|
||||
* @return The comments.
|
||||
*/
|
||||
public final EntryComment[] getComments() {
|
||||
return (comments.toArray(new EntryComment[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of comments.
|
||||
*
|
||||
* @return The count of comments.
|
||||
*/
|
||||
public final int getCommentsCount() {
|
||||
return comments.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's creation date.
|
||||
*
|
||||
* @return The date.
|
||||
*/
|
||||
public final Date getDate() {
|
||||
return new Date(date.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's link.
|
||||
*
|
||||
* @return The link.
|
||||
*/
|
||||
public final String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's author login.
|
||||
*
|
||||
* @return The login;
|
||||
*/
|
||||
public final String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's author nickname.
|
||||
*
|
||||
* @return The nickname.
|
||||
*/
|
||||
public final String getNick() {
|
||||
return nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tags formatted for pinboard.in
|
||||
*
|
||||
* @return The tags as a comma-delimited string.
|
||||
*/
|
||||
public final String getPinboardTags() {
|
||||
final StringBuilder pinboardTags = new StringBuilder(nick);
|
||||
|
||||
for (final SyndCategory tag : tags) {
|
||||
pinboardTags.append(',');
|
||||
pinboardTags.append(tag.getName());
|
||||
}
|
||||
|
||||
return pinboardTags.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tags.
|
||||
*
|
||||
* @return The tags.
|
||||
*/
|
||||
public final List<SyndCategory> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's title.
|
||||
*
|
||||
* @return The title.
|
||||
*/
|
||||
public final String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entry has comments.
|
||||
*
|
||||
* @return true if there are comments, false otherwise.
|
||||
*/
|
||||
public final boolean hasComments() {
|
||||
return !comments.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entry has tags.
|
||||
*
|
||||
* @return true if there are tags, false otherwise.
|
||||
*/
|
||||
public final boolean hasTags() {
|
||||
return !tags.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a string is contained in the link, title, or nick.
|
||||
*
|
||||
* @param match The string to match.
|
||||
* @return {@code true} if matched, {@code false} otherwise.
|
||||
*/
|
||||
public Boolean matches(final String match) {
|
||||
return (StringUtils.containsIgnoreCase(link, match)
|
||||
|| StringUtils.containsIgnoreCase(title, match)
|
||||
|| StringUtils.containsIgnoreCase(nick, match));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel.
|
||||
*
|
||||
* @param channel The channel.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public final void setChannel(final String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* /** Sets a comment.
|
||||
*
|
||||
* @param index The comment's index.
|
||||
* @param comment The actual comment.
|
||||
* @param nick The nickname of the author of the comment.
|
||||
*/
|
||||
public final void setComment(final int index, final String comment, final String nick) {
|
||||
if (index < comments.size()) {
|
||||
comments.set(index, new EntryComment(comment, nick));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment's link.
|
||||
*
|
||||
* @param link The new link.
|
||||
*/
|
||||
public final void setLink(final String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment's author login.
|
||||
*
|
||||
* @param login The new login.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public final void setLogin(final String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment's author nickname.
|
||||
*
|
||||
* @param nick The new nickname.
|
||||
*/
|
||||
public final void setNick(final String nick) {
|
||||
this.nick = nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags.
|
||||
*
|
||||
* @param tags The space-delimited tags.
|
||||
*/
|
||||
public final void setTags(final String tags) {
|
||||
setTags(Arrays.asList(tags.split(LinksMgr.TAG_MATCH)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags.
|
||||
*
|
||||
* @param tags The tags list.
|
||||
*/
|
||||
@SuppressFBWarnings("STT_STRING_PARSING_A_FIELD")
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public final void setTags(final List<String> tags) {
|
||||
if (!tags.isEmpty()) {
|
||||
SyndCategoryImpl category;
|
||||
|
||||
for (final String tag : tags) {
|
||||
if (StringUtils.isNoneBlank(tag)) {
|
||||
final String t = StringUtils.lowerCase(tag);
|
||||
final char mod = t.charAt(0);
|
||||
if (mod == '-') {
|
||||
// Don't remove the channel tag
|
||||
if (!channel.substring(1).equals(t.substring(1))) {
|
||||
category = new SyndCategoryImpl();
|
||||
category.setName(t.substring(1));
|
||||
this.tags.remove(category);
|
||||
}
|
||||
} else {
|
||||
category = new SyndCategoryImpl();
|
||||
if (mod == '+') {
|
||||
category.setName(t.substring(1));
|
||||
} else {
|
||||
category.setName(t);
|
||||
}
|
||||
if (!this.tags.contains(category)) {
|
||||
this.tags.add(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment's title.
|
||||
*
|
||||
* @param title The new title.
|
||||
*/
|
||||
public final void setTitle(final String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object.
|
||||
*
|
||||
* @return A string representation of the object.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EntryLink{"
|
||||
+ "channel='" + channel + '\''
|
||||
+ ", comments=" + comments
|
||||
+ ", date=" + date
|
||||
+ ", link='" + link + '\''
|
||||
+ ", login='" + login + '\''
|
||||
+ ", nick='" + nick + '\''
|
||||
+ ", tags=" + tags
|
||||
+ ", title='" + title + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
223
src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt
Normal file
223
src/main/java/net/thauvin/erik/mobibot/entries/EntryLink.kt
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* EntryLink.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.entries
|
||||
|
||||
import com.rometools.rome.feed.synd.SyndCategory
|
||||
import com.rometools.rome.feed.synd.SyndCategoryImpl
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
/**
|
||||
* The class used to store link entries.
|
||||
*/
|
||||
class EntryLink : Serializable {
|
||||
// Link's comments
|
||||
val comments: MutableList<EntryComment> = CopyOnWriteArrayList()
|
||||
|
||||
// Tags/categories
|
||||
val tags: MutableList<SyndCategory> = CopyOnWriteArrayList()
|
||||
|
||||
// Channel
|
||||
var channel: String
|
||||
|
||||
// Creation date
|
||||
var date = Calendar.getInstance().time
|
||||
|
||||
// Link's URL
|
||||
var link: String
|
||||
|
||||
// Author's login
|
||||
var login = ""
|
||||
|
||||
// Author's nickname
|
||||
var nick: String
|
||||
|
||||
// Link's title
|
||||
var title: String
|
||||
|
||||
/**
|
||||
* Creates a new entry.
|
||||
*/
|
||||
constructor(
|
||||
link: String,
|
||||
title: String,
|
||||
nick: String,
|
||||
login: String,
|
||||
channel: String,
|
||||
tags: List<String?>
|
||||
) {
|
||||
this.link = link
|
||||
this.title = title
|
||||
this.nick = nick
|
||||
this.login = login
|
||||
this.channel = channel
|
||||
setTags(tags)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry.
|
||||
*/
|
||||
constructor(
|
||||
link: String,
|
||||
title: String,
|
||||
nick: String,
|
||||
channel: String,
|
||||
date: Date,
|
||||
tags: List<SyndCategory>
|
||||
) {
|
||||
this.link = link
|
||||
this.title = title
|
||||
this.nick = nick
|
||||
this.channel = channel
|
||||
this.date = Date(date.time)
|
||||
this.tags.addAll(tags)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new comment.
|
||||
*/
|
||||
fun addComment(comment: String?, nick: String?): Int {
|
||||
comments.add(EntryComment(comment!!, nick!!))
|
||||
return comments.size - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a specific comment.
|
||||
*/
|
||||
fun deleteComment(index: Int) {
|
||||
if (index < comments.size) {
|
||||
comments.removeAt(index)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a comment.
|
||||
*/
|
||||
fun getComment(index: Int): EntryComment = comments[index]
|
||||
|
||||
/**
|
||||
* Returns the tags formatted for pinboard.in
|
||||
*/
|
||||
val pinboardTags: String
|
||||
get() {
|
||||
val pinboardTags = StringBuilder(nick)
|
||||
for (tag in tags) {
|
||||
pinboardTags.append(',')
|
||||
pinboardTags.append(tag.name)
|
||||
}
|
||||
return pinboardTags.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entry has comments.
|
||||
*/
|
||||
fun hasComments(): Boolean = comments.isNotEmpty()
|
||||
|
||||
/**
|
||||
* Returns true if the entry has tags.
|
||||
*/
|
||||
fun hasTags(): Boolean = tags.isNotEmpty()
|
||||
|
||||
/**
|
||||
* Returns true if a string is contained in the link, title, or nick.
|
||||
*/
|
||||
fun matches(match: String?): Boolean {
|
||||
return (StringUtils.containsIgnoreCase(link, match)
|
||||
|| StringUtils.containsIgnoreCase(title, match)
|
||||
|| StringUtils.containsIgnoreCase(nick, match))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comment.
|
||||
*/
|
||||
fun setComment(index: Int, comment: String?, nick: String?) {
|
||||
if (index < comments.size) {
|
||||
comments[index] = EntryComment(comment!!, nick!!)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags.
|
||||
*/
|
||||
fun setTags(tags: String) {
|
||||
setTags(tags.split(LinksMgr.TAG_MATCH).toTypedArray().toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags.
|
||||
*/
|
||||
fun setTags(tags: List<String?>) {
|
||||
if (!tags.isEmpty()) {
|
||||
var category: SyndCategoryImpl
|
||||
for (tag in tags) {
|
||||
if (StringUtils.isNoneBlank(tag)) {
|
||||
val t = StringUtils.lowerCase(tag)
|
||||
val mod = t[0]
|
||||
if (mod == '-') {
|
||||
// Don't remove the channel tag
|
||||
if (channel.substring(1) != t.substring(1)) {
|
||||
category = SyndCategoryImpl()
|
||||
category.name = t.substring(1)
|
||||
this.tags.remove(category)
|
||||
}
|
||||
} else {
|
||||
category = SyndCategoryImpl()
|
||||
if (mod == '+') {
|
||||
category.name = t.substring(1)
|
||||
} else {
|
||||
category.name = t
|
||||
}
|
||||
if (!this.tags.contains(category)) {
|
||||
this.tags.add(category)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object.
|
||||
*/
|
||||
override fun toString(): String {
|
||||
return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," +
|
||||
"nick='$nick', tags=$tags, title='$title'}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Serial version UID
|
||||
const val serialVersionUID = 1L
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ 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.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
@ -76,7 +77,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
override val isValidProperties: Boolean
|
||||
get() {
|
||||
for (s in propertyKeys) {
|
||||
if (AUTOPOST_PROP != s && HANDLE_PROP != s && properties[s]!!.isBlank()) {
|
||||
if (AUTOPOST_PROP != s && HANDLE_PROP != s && properties[s].isNullOrBlank()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +131,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
Thread {
|
||||
try {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1)
|
||||
logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkCmd(index))
|
||||
}
|
||||
post(message = msg, isDm = false)
|
||||
} catch (e: ModuleException) {
|
||||
|
@ -146,7 +147,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
if (isAutoPost) {
|
||||
addEntry(index)
|
||||
if (bot.logger.isDebugEnabled) {
|
||||
bot.logger.debug("Scheduling {}{} for posting on Twitter.", Constants.LINK_CMD, index + 1)
|
||||
bot.logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkCmd(index))
|
||||
}
|
||||
bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L)
|
||||
}
|
||||
|
|
|
@ -138,8 +138,8 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) {
|
|||
}
|
||||
if (cwd.hasWindData()) {
|
||||
with(cwd.windData) {
|
||||
if (this != null && hasSpeed()) {
|
||||
messages.add(NoticeMessage("Wind: ${wind(speed)}"))
|
||||
if (this != null && hasSpeed() && speed != null) {
|
||||
messages.add(NoticeMessage("Wind: ${wind(speed!!)}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,9 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) {
|
|||
val list = cwd.weatherList
|
||||
if (list != null) {
|
||||
for (w in list) {
|
||||
condition.append(' ').append(w!!.getDescription().capitalize()).append('.')
|
||||
if (w != null) {
|
||||
condition.append(' ').append(w.getDescription().capitalize()).append('.')
|
||||
}
|
||||
}
|
||||
messages.add(NoticeMessage(condition.toString()))
|
||||
}
|
||||
|
@ -182,9 +184,9 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) {
|
|||
return messages
|
||||
}
|
||||
|
||||
private fun wind(w: Double?): String {
|
||||
val kmh = w!! * 1.60934
|
||||
return "${Math.round(w)} mph, ${kmh.roundToInt()} km/h"
|
||||
private fun wind(w: Double): String {
|
||||
val kmh = w * 1.60934
|
||||
return "${w.roundToInt()} mph, ${kmh.roundToInt()} km/h"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* LocalProperties.java
|
||||
* LocalProperties.kt
|
||||
*
|
||||
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,54 +29,47 @@
|
|||
* 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 org.apache.commons.lang3.StringUtils;
|
||||
import org.testng.annotations.BeforeSuite;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Properties;
|
||||
import org.testng.annotations.BeforeSuite
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The <code>properties</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-08
|
||||
* @since 1.0
|
||||
* Access to `local.properties`.
|
||||
*/
|
||||
public class LocalProperties {
|
||||
private static final Properties localProps = new Properties();
|
||||
|
||||
public static String getProperty(final String key) {
|
||||
if (localProps.containsKey(key)) {
|
||||
return localProps.getProperty(key);
|
||||
} else {
|
||||
final String env = System.getenv(keyToEnv(key));
|
||||
if (env != null) {
|
||||
localProps.setProperty(key, env);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
}
|
||||
|
||||
private static String keyToEnv(final String key) {
|
||||
return StringUtils.upperCase(key.replace('-', '_'));
|
||||
}
|
||||
|
||||
open class LocalProperties {
|
||||
@BeforeSuite(alwaysRun = true)
|
||||
public void loadProperties() {
|
||||
final Path localPath = Paths.get("local.properties");
|
||||
fun loadProperties() {
|
||||
val localPath = Paths.get("local.properties")
|
||||
if (Files.exists(localPath)) {
|
||||
try (final InputStream stream = Files.newInputStream(localPath)) {
|
||||
localProps.load(stream);
|
||||
} catch (IOException ignore) {
|
||||
try {
|
||||
Files.newInputStream(localPath).use { stream -> localProps.load(stream) }
|
||||
} catch (ignore: IOException) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val localProps = Properties()
|
||||
|
||||
fun getProperty(key: String): String {
|
||||
return if (localProps.containsKey(key)) {
|
||||
localProps.getProperty(key)
|
||||
} else {
|
||||
val env = System.getenv(keyToEnv(key))
|
||||
if (env != null) {
|
||||
localProps.setProperty(key, env)
|
||||
}
|
||||
env
|
||||
}
|
||||
}
|
||||
|
||||
private fun keyToEnv(key: String): String {
|
||||
return key.replace('-', '_').toUpperCase()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* EntryLinkTest.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.entries;
|
||||
|
||||
import com.rometools.rome.feed.synd.SyndCategory;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* The <code>EntryUtilsTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-19
|
||||
* @since 1.0
|
||||
*/
|
||||
public class EntryLinkTest {
|
||||
private final EntryLink entryLink = new EntryLink("https://www.mobitopia.org/", "Mobitopia", "Skynx",
|
||||
"JimH", "#mobitopia", List.of("tag1", "tag2", "tag3", "TAG4", "Tag5"));
|
||||
|
||||
|
||||
@Test
|
||||
public void testAddDeleteComment() {
|
||||
int i = 0;
|
||||
for (; i < 5; i++) {
|
||||
entryLink.addComment("c" + i, "u" + i);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (final EntryComment comment : entryLink.getComments()) {
|
||||
assertThat(comment.getComment()).as("getComment(" + i + ')').isEqualTo("c" + i);
|
||||
assertThat(comment.getNick()).as("getNick(" + i + ')').isEqualTo("u" + i);
|
||||
i++;
|
||||
}
|
||||
|
||||
final SecureRandom r = new SecureRandom();
|
||||
while (entryLink.getCommentsCount() > 0) {
|
||||
entryLink.deleteComment(r.nextInt(entryLink.getCommentsCount()));
|
||||
}
|
||||
assertThat(entryLink.hasComments()).as("hasComments()").isFalse();
|
||||
|
||||
entryLink.addComment("nothing", "nobody");
|
||||
entryLink.setComment(0, "something", "somebody");
|
||||
assertThat(entryLink.getComment(0).getNick()).as("getNick(somebody)").isEqualTo("somebody");
|
||||
assertThat(entryLink.getComment(0).getComment()).as("getComment(something)").isEqualTo("something");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTags() {
|
||||
final List<SyndCategory> tags = entryLink.getTags();
|
||||
|
||||
int i = 0;
|
||||
for (final SyndCategory tag : tags) {
|
||||
assertThat(tag.getName()).as("tag.getName(" + i + ')').isEqualTo("tag" + (i + 1));
|
||||
i++;
|
||||
}
|
||||
|
||||
entryLink.setTags("-tag5");
|
||||
entryLink.setTags("+mobitopia");
|
||||
entryLink.setTags("tag4");
|
||||
entryLink.setTags("-mobitopia");
|
||||
|
||||
assertThat(entryLink.getPinboardTags()).as("getPinboardTags()")
|
||||
.isEqualTo(entryLink.getNick() + ",tag1,tag2,tag3,tag4,mobitopia");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* EntryLinkTest.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.entries
|
||||
|
||||
import com.rometools.rome.feed.synd.SyndCategory
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
import java.security.SecureRandom
|
||||
|
||||
/**
|
||||
* The `EntryUtilsTest` class.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
||||
* @created 2019-04-19
|
||||
* @since 1.0
|
||||
*/
|
||||
class EntryLinkTest {
|
||||
private val entryLink = EntryLink(
|
||||
"https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia",
|
||||
listOf("tag1", "tag2", "tag3", "TAG4", "Tag5")
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testAddDeleteComment() {
|
||||
var i = 0
|
||||
while (i < 5) {
|
||||
entryLink.addComment("c$i", "u$i")
|
||||
i++
|
||||
}
|
||||
Assertions.assertThat(entryLink.comments.size).`as`("getComments().size() == 5").isEqualTo(i)
|
||||
i = 0
|
||||
for (comment in entryLink.comments) {
|
||||
Assertions.assertThat(comment.comment).`as`("getComment($i)").isEqualTo("c$i")
|
||||
Assertions.assertThat(comment.nick).`as`("getNick($i)").isEqualTo("u$i")
|
||||
i++
|
||||
}
|
||||
val r = SecureRandom()
|
||||
while (entryLink.comments.size > 0) {
|
||||
entryLink.deleteComment(r.nextInt(entryLink.comments.size))
|
||||
}
|
||||
Assertions.assertThat(entryLink.hasComments()).`as`("hasComments()").isFalse
|
||||
entryLink.addComment("nothing", "nobody")
|
||||
entryLink.setComment(0, "something", "somebody")
|
||||
Assertions.assertThat(entryLink.getComment(0).nick).`as`("getNick(somebody)").isEqualTo("somebody")
|
||||
Assertions.assertThat(entryLink.getComment(0).comment).`as`("getComment(something)").isEqualTo("something")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTags() {
|
||||
val tags: List<SyndCategory> = entryLink.tags
|
||||
for ((i, tag) in tags.withIndex()) {
|
||||
Assertions.assertThat(tag.name).`as`("tag.getName($i)").isEqualTo("tag" + (i + 1))
|
||||
}
|
||||
Assertions.assertThat(entryLink.tags.size).`as`("getTags().size() is 5").isEqualTo(5)
|
||||
Assertions.assertThat(entryLink.hasTags()).`as`("hasTags() is true").isTrue
|
||||
entryLink.setTags("-tag5")
|
||||
entryLink.setTags("+mobitopia")
|
||||
entryLink.setTags("tag4")
|
||||
entryLink.setTags("-mobitopia")
|
||||
Assertions.assertThat(entryLink.pinboardTags).`as`("getPinboardTags()")
|
||||
.isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia")
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue