diff --git a/src/main/java/net/thauvin/erik/mobibot/Addons.kt b/src/main/java/net/thauvin/erik/mobibot/Addons.kt index 1974aa4..04dfcd8 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Addons.kt @@ -87,6 +87,39 @@ class Addons { } } + /** + * Execute a command or module. + */ + fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean { + for (command in commands) { + if (command.name.startsWith(cmd)) { + command.commandResponse(sender, login, args, isOp, isPrivate) + return true + } + } + for (module in modules) { + if ((isPrivate && module.isPrivateMsgEnabled) || !isPrivate) { + if (module.commands.contains(cmd)) { + module.commandResponse(sender, cmd, args, isPrivate) + return true + } + } + } + return false + } + + /** + * Match a command. + */ + fun match(sender: String, login: String, message: String, isOp: Boolean, isPrivate: Boolean) { + for (command in commands) { + if (command.matches(message)) { + command.commandResponse(sender, login, message, isOp, isPrivate) + break + } + } + } + /** * Sort commands and modules names. */ diff --git a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt index e6c1f8b..16c28c0 100644 --- a/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/java/net/thauvin/erik/mobibot/Mobibot.kt @@ -58,9 +58,6 @@ import net.thauvin.erik.mobibot.commands.Users import net.thauvin.erik.mobibot.commands.Versions import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.LinksMgr -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.saveEntries -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.startDate -import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.startup import net.thauvin.erik.mobibot.commands.links.Posting import net.thauvin.erik.mobibot.commands.links.Tags import net.thauvin.erik.mobibot.commands.links.View @@ -363,31 +360,12 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help helpResponse(sender, args, false) } else { - // Commands - for (command in addons.commands) { - if (command.isPublic && command.name.startsWith(cmd)) { - command.commandResponse(sender, login, args, isOp(sender), false) - return - } - } - // Modules - for (module in addons.modules) { // modules - for (c in module.commands) { - if (cmd.startsWith(c)) { - module.commandResponse(sender, cmd, args, false) - return - } - } - } + // Execute module or command + addons.exec(sender, login, cmd, args, isOp(sender), false) } } else { - // Commands, e.g.: https://www.example.com/ - for (command in addons.commands) { - if (command.matches(message)) { - command.commandResponse(sender, login, message, isOp(sender), false) - return - } - } + // Links, e.g.: https://www.example.com/ or L1: , etc. + addons.match(sender, login, message, isOp(sender), false) } storeRecap(sender, message, false) } @@ -420,23 +398,9 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert quitServer("The Bot Is Out There!") exitProcess(0) } else { - for (command in addons.commands) { - if (command.name.startsWith(cmd)) { - command.commandResponse(sender, login, args, isOp, true) - return - } + if (!addons.exec(sender, login, cmd, args, isOp, true)) { + helpDefault(sender, isOp, true) } - for (module in addons.modules) { - if (module.isPrivateMsgEnabled) { - for (c in module.commands) { - if (cmd == c) { - module.commandResponse(sender, cmd, args, true) - return - } - } - } - } - helpDefault(sender, isOp, true) } } @@ -600,16 +564,17 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert HelpFormatter().printHelp(Mobibot::class.java.name, options) } commandLine.hasOption(Constants.VERSION_ARG[0]) -> { + // Output the version println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${isoLocalDate(ReleaseInfo.BUILDDATE)})") println(ReleaseInfo.WEBSITE) } else -> { + // Load the properties val p = Properties() try { Files.newInputStream( Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG[0], "./mobibot.properties")) ).use { fis -> - // Load the properties files p.load(fis) } } catch (e: FileNotFoundException) { @@ -625,7 +590,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert val channel = p.getProperty("channel") val logsDir = ensureDir(p.getProperty("logs", "."), false) - // Redirect the stdout and stderr + // Redirect stdout and stderr if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) { try { val stdout = PrintStream( @@ -680,15 +645,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert // Store the default logger level loggerLevel = logger.level - // Load the current entries and backlogs, if any - try { - startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) - if (logger.isDebugEnabled) logger.debug("Last feed: $startDate") - } catch (e: Exception) { - logger.error("An error occurred while loading the logs.", e) - } - - // Initialize the bot setVerbose(true) setAutoNickChange(true) login = p.getProperty("login", name) @@ -704,6 +660,14 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert weblogUrl = p.getProperty("weblog", "") backlogsUrl = ensureDir(p.getProperty("backlogs", weblogUrl), true) + // Load the current entries and backlogs, if any + try { + LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) + if (logger.isDebugEnabled) logger.debug("Last feed: ${LinksMgr.startDate}") + } catch (e: Exception) { + logger.error("An error occurred while loading the logs.", e) + } + // Set the pinboard authentication setPinboardAuth(p.getProperty("pinboard-api-token", "")) @@ -756,6 +720,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert addons.sort() // Save the entries - saveEntries(this, true) + LinksMgr.saveEntries(this, true) } } diff --git a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt index 7a7e927..8bc0b18 100644 --- a/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgr.kt @@ -46,53 +46,51 @@ import java.time.LocalDateTime /** * The Tell Messages Manager. */ -internal class TellMessagesMgr private constructor() { - companion object { - /** - * Cleans the messages queue. - */ - fun clean(tellMessages: MutableList, tellMaxDays: Int): Boolean { - val today = LocalDateTime.now(Clock.systemUTC()) - return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays.toLong()).isBefore(today) } - } +internal object TellMessagesMgr { + /** + * Cleans the messages queue. + */ + fun clean(tellMessages: MutableList, tellMaxDays: Int): Boolean { + val today = LocalDateTime.now(Clock.systemUTC()) + return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays.toLong()).isBefore(today) } + } - /** - * Loads the messages. - */ + /** + * Loads the messages. + */ - fun load(file: String, logger: Logger): List { - try { - ObjectInputStream( - BufferedInputStream(Files.newInputStream(Paths.get(file))) - ).use { input -> - if (logger.isDebugEnabled) logger.debug("Loading the messages.") - @Suppress("UNCHECKED_CAST") - return input.readObject() as List - } - } catch (ignore: FileNotFoundException) { - // Do nothing - } catch (e: IOException) { - logger.error("An IO error occurred loading the messages queue.", e) - } catch (e: ClassNotFoundException) { - logger.error("An error occurred loading the messages queue.", e) + fun load(file: String, logger: Logger): List { + try { + ObjectInputStream( + BufferedInputStream(Files.newInputStream(Paths.get(file))) + ).use { input -> + if (logger.isDebugEnabled) logger.debug("Loading the messages.") + @Suppress("UNCHECKED_CAST") + return input.readObject() as List } - return listOf() + } catch (ignore: FileNotFoundException) { + // Do nothing + } catch (e: IOException) { + logger.error("An IO error occurred loading the messages queue.", e) + } catch (e: ClassNotFoundException) { + logger.error("An error occurred loading the messages queue.", e) } + return listOf() + } - /** - * Saves the messages. - */ - fun save(file: String, messages: List?, logger: Logger) { - try { - BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> - ObjectOutputStream(bos).use { output -> - if (logger.isDebugEnabled) logger.debug("Saving the messages.") - output.writeObject(messages) - } + /** + * Saves the messages. + */ + fun save(file: String, messages: List?, logger: Logger) { + try { + BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> + ObjectOutputStream(bos).use { output -> + if (logger.isDebugEnabled) logger.debug("Saving the messages.") + output.writeObject(messages) } - } catch (e: IOException) { - logger.error("Unable to save messages queue.", e) } + } catch (e: IOException) { + logger.error("Unable to save messages queue.", e) } } } diff --git a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt index 57702c9..823d2ab 100644 --- a/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt +++ b/src/main/java/net/thauvin/erik/mobibot/entries/EntriesMgr.kt @@ -52,203 +52,199 @@ import java.util.* /** * Manages the feed entries. */ -class EntriesMgr private constructor() { +object EntriesMgr { + /** + * 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" - companion object { - /** - * The name of the file containing the current entries. - */ - const val CURRENT_XML = "current.xml" + /** + * The .xml extension + */ + const val XML_EXT = ".xml" - /** - * The name of the file containing the backlog entries. - */ - const val NAV_XML = "nav.xml" + // Maximum number of backlogs to keep + private const val maxBacklogs = 10 - /** - * 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: MutableList) { - 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 backlogs. + */ + @Throws(IOException::class, FeedException::class) + fun loadBacklogs(file: String, history: MutableList) { + 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: MutableList): 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 - for (comment in description.value.split("
")) { - split = comment.split(": ".toRegex(), 2) - if (split.size == 2) { - entry.addComment(comment = split[1].trim(), nick = split[0].trim()) - } + /** + * Loads the current entries. + */ + @Throws(IOException::class, FeedException::class) + fun loadEntries(file: String, channel: String, entries: MutableList): 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 + for (comment in description.value.split("
")) { + split = comment.split(": ".toRegex(), 2) + if (split.size == 2) { + entry.addComment(comment = split[1].trim(), nick = split[0].trim()) } } - entries.add(entry) } + entries.add(entry) } - return today } + return today + } - /** - * Saves the entries. - */ - fun saveEntries( - bot: Mobibot, - entries: List, - history: MutableList, - isDayBackup: Boolean - ) { - if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...") - if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { - try { - val output = SyndFeedOutput() - var rss: SyndFeed = SyndFeedImpl() - val items: MutableList = mutableListOf() - 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 ") - .append(nick) - .append(" on ") - .append(channel) - .append("") - if (comments.size > 0) { - buff.append("

") - val comments = comments - for (j in comments.indices) { - comment = comments[j] - if (j > 0) { - buff.append("
") - } - buff.append(comment.nick).append(": ").append(comment.comment) + /** + * Saves the entries. + */ + fun saveEntries( + bot: Mobibot, + entries: List, + history: MutableList, + isDayBackup: Boolean + ) { + if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...") + if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { + try { + val output = SyndFeedOutput() + var rss: SyndFeed = SyndFeedImpl() + val items: MutableList = mutableListOf() + 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 ") + .append(nick) + .append(" on ") + .append(channel) + .append("") + if (comments.size > 0) { + buff.append("

") + val comments = comments + for (j in comments.indices) { + comment = comments[j] + if (j > 0) { + buff.append("
") } + 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 (bot.backlogsUrl.isNotBlank()) { + if (!history.contains(bot.today)) { + history.add(bot.today) + while (history.size > maxBacklogs) { + 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.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 + 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) } - rss.entries = items - if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the entries feed.") - output.output(rss, fw) + } else { + bot.logger.warn("Unable to generate the backlogs feed. No property configured.") } - OutputStreamWriter( - Files.newOutputStream( - Paths.get( - bot.logsDir + bot.today + XML_EXT - ) - ), StandardCharsets.UTF_8 - ).use { fw -> output.output(rss, fw) } - if (isDayBackup) { - if (bot.backlogsUrl.isNotBlank()) { - 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) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) - } catch (e: IOException) { - if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) - } - } else { - if (bot.logger.isWarnEnabled) { - bot.logger.warn("Unable to generate the entries feed. A required property is missing.") } + } catch (e: FeedException) { + if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) + } catch (e: IOException) { + if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e) + } + } else { + if (bot.logger.isWarnEnabled) { + bot.logger.warn("Unable to generate the entries feed. A required property is missing.") } } }