Added addons.exec() and addons.match() functions.

Converted TellMessagesMgr and EntriesMgr to objects.
This commit is contained in:
Erik C. Thauvin 2020-12-07 01:33:59 -08:00
parent b24c0ebe54
commit f9edff4813
4 changed files with 261 additions and 270 deletions

View file

@ -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. * Sort commands and modules names.
*/ */

View file

@ -58,9 +58,6 @@ import net.thauvin.erik.mobibot.commands.Users
import net.thauvin.erik.mobibot.commands.Versions import net.thauvin.erik.mobibot.commands.Versions
import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.Comment
import net.thauvin.erik.mobibot.commands.links.LinksMgr 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.Posting
import net.thauvin.erik.mobibot.commands.links.Tags import net.thauvin.erik.mobibot.commands.links.Tags
import net.thauvin.erik.mobibot.commands.links.View 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 if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
helpResponse(sender, args, false) helpResponse(sender, args, false)
} else { } else {
// Commands // Execute module or command
for (command in addons.commands) { addons.exec(sender, login, cmd, args, isOp(sender), false)
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
}
}
}
} }
} else { } else {
// Commands, e.g.: https://www.example.com/ // Links, e.g.: https://www.example.com/ or L1: , etc.
for (command in addons.commands) { addons.match(sender, login, message, isOp(sender), false)
if (command.matches(message)) {
command.commandResponse(sender, login, message, isOp(sender), false)
return
}
}
} }
storeRecap(sender, message, 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!") quitServer("The Bot Is Out There!")
exitProcess(0) exitProcess(0)
} else { } else {
for (command in addons.commands) { if (!addons.exec(sender, login, cmd, args, isOp, true)) {
if (command.name.startsWith(cmd)) { helpDefault(sender, isOp, true)
command.commandResponse(sender, login, args, isOp, true)
return
}
} }
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) HelpFormatter().printHelp(Mobibot::class.java.name, options)
} }
commandLine.hasOption(Constants.VERSION_ARG[0]) -> { commandLine.hasOption(Constants.VERSION_ARG[0]) -> {
// Output the version
println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${isoLocalDate(ReleaseInfo.BUILDDATE)})") println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${isoLocalDate(ReleaseInfo.BUILDDATE)})")
println(ReleaseInfo.WEBSITE) println(ReleaseInfo.WEBSITE)
} }
else -> { else -> {
// Load the properties
val p = Properties() val p = Properties()
try { try {
Files.newInputStream( Files.newInputStream(
Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG[0], "./mobibot.properties")) Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG[0], "./mobibot.properties"))
).use { fis -> ).use { fis ->
// Load the properties files
p.load(fis) p.load(fis)
} }
} catch (e: FileNotFoundException) { } catch (e: FileNotFoundException) {
@ -625,7 +590,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
val channel = p.getProperty("channel") val channel = p.getProperty("channel")
val logsDir = ensureDir(p.getProperty("logs", "."), false) val logsDir = ensureDir(p.getProperty("logs", "."), false)
// Redirect the stdout and stderr // Redirect stdout and stderr
if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) { if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) {
try { try {
val stdout = PrintStream( val stdout = PrintStream(
@ -680,15 +645,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
// Store the default logger level // Store the default logger level
loggerLevel = 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) setVerbose(true)
setAutoNickChange(true) setAutoNickChange(true)
login = p.getProperty("login", name) login = p.getProperty("login", name)
@ -704,6 +660,14 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
weblogUrl = p.getProperty("weblog", "") weblogUrl = p.getProperty("weblog", "")
backlogsUrl = ensureDir(p.getProperty("backlogs", weblogUrl), true) 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 // Set the pinboard authentication
setPinboardAuth(p.getProperty("pinboard-api-token", "")) setPinboardAuth(p.getProperty("pinboard-api-token", ""))
@ -756,6 +720,6 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
addons.sort() addons.sort()
// Save the entries // Save the entries
saveEntries(this, true) LinksMgr.saveEntries(this, true)
} }
} }

View file

@ -46,53 +46,51 @@ import java.time.LocalDateTime
/** /**
* The Tell Messages Manager. * The Tell Messages Manager.
*/ */
internal class TellMessagesMgr private constructor() { internal object TellMessagesMgr {
companion object { /**
/** * Cleans the messages queue.
* Cleans the messages queue. */
*/ fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Int): Boolean {
fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Int): Boolean { val today = LocalDateTime.now(Clock.systemUTC())
val today = LocalDateTime.now(Clock.systemUTC()) return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays.toLong()).isBefore(today) }
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<TellMessage> { fun load(file: String, logger: Logger): List<TellMessage> {
try { try {
ObjectInputStream( ObjectInputStream(
BufferedInputStream(Files.newInputStream(Paths.get(file))) BufferedInputStream(Files.newInputStream(Paths.get(file)))
).use { input -> ).use { input ->
if (logger.isDebugEnabled) logger.debug("Loading the messages.") if (logger.isDebugEnabled) logger.debug("Loading the messages.")
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return input.readObject() as List<TellMessage> return input.readObject() as List<TellMessage>
}
} 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() } 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. * Saves the messages.
*/ */
fun save(file: String, messages: List<TellMessage?>?, logger: Logger) { fun save(file: String, messages: List<TellMessage?>?, logger: Logger) {
try { try {
BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos ->
ObjectOutputStream(bos).use { output -> ObjectOutputStream(bos).use { output ->
if (logger.isDebugEnabled) logger.debug("Saving the messages.") if (logger.isDebugEnabled) logger.debug("Saving the messages.")
output.writeObject(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)
} }
} }
} }

View file

@ -52,203 +52,199 @@ import java.util.*
/** /**
* Manages the feed entries. * 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 .xml extension
* The name of the file containing the current entries. */
*/ const val XML_EXT = ".xml"
const val CURRENT_XML = "current.xml"
/** // Maximum number of backlogs to keep
* The name of the file containing the backlog entries. private const val maxBacklogs = 10
*/
const val NAV_XML = "nav.xml"
/** /**
* The .xml extension * Loads the backlogs.
*/ */
const val XML_EXT = ".xml" @Throws(IOException::class, FeedException::class)
fun loadBacklogs(file: String, history: MutableList<String>) {
// Maximum number of backlogs to keep history.clear()
private const val MAX_BACKLOGS = 10 val input = SyndFeedInput()
InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8).use { reader ->
/** val feed = input.build(reader)
* Loads the backlogs. val items = feed.entries
*/ for (i in items.indices.reversed()) {
@Throws(IOException::class, FeedException::class) history.add(items[i].title)
fun loadBacklogs(file: String, history: MutableList<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. * Loads the current entries.
*/ */
@Throws(IOException::class, FeedException::class) @Throws(IOException::class, FeedException::class)
fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String { fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String {
entries.clear() entries.clear()
val input = SyndFeedInput() val input = SyndFeedInput()
var today: String var today: String
InputStreamReader( InputStreamReader(
Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8 Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8
).use { reader -> ).use { reader ->
val feed = input.build(reader) val feed = input.build(reader)
today = isoLocalDate(feed.publishedDate) today = isoLocalDate(feed.publishedDate)
val items = feed.entries val items = feed.entries
var entry: EntryLink var entry: EntryLink
for (i in items.indices.reversed()) { for (i in items.indices.reversed()) {
with(items[i]) { with(items[i]) {
entry = EntryLink( entry = EntryLink(
link, link,
title, title,
author.substring(author.lastIndexOf('(') + 1, author.length - 1), author.substring(author.lastIndexOf('(') + 1, author.length - 1),
channel, channel,
publishedDate, publishedDate,
categories categories
) )
var split: List<String> var split: List<String>
for (comment in description.value.split("<br/>")) { for (comment in description.value.split("<br/>")) {
split = comment.split(": ".toRegex(), 2) split = comment.split(": ".toRegex(), 2)
if (split.size == 2) { if (split.size == 2) {
entry.addComment(comment = split[1].trim(), nick = split[0].trim()) entry.addComment(comment = split[1].trim(), nick = split[0].trim())
}
} }
} }
entries.add(entry)
} }
entries.add(entry)
} }
return today
} }
return today
}
/** /**
* Saves the entries. * Saves the entries.
*/ */
fun saveEntries( fun saveEntries(
bot: Mobibot, bot: Mobibot,
entries: List<EntryLink>, entries: List<EntryLink>,
history: MutableList<String>, history: MutableList<String>,
isDayBackup: Boolean isDayBackup: Boolean
) { ) {
if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...") if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...")
if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) { if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) {
try { try {
val output = SyndFeedOutput() val output = SyndFeedOutput()
var rss: SyndFeed = SyndFeedImpl() var rss: SyndFeed = SyndFeedImpl()
val items: MutableList<SyndEntry> = mutableListOf() val items: MutableList<SyndEntry> = mutableListOf()
var item: SyndEntry var item: SyndEntry
OutputStreamWriter( OutputStreamWriter(
Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8 Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8
).use { fw -> ).use { fw ->
rss.apply { rss.apply {
feedType = "rss_2.0" feedType = "rss_2.0"
title = bot.channel + " IRC Links" title = bot.channel + " IRC Links"
description = "Links from ${bot.ircServer} on ${bot.channel}" description = "Links from ${bot.ircServer} on ${bot.channel}"
link = bot.weblogUrl link = bot.weblogUrl
publishedDate = Calendar.getInstance().time publishedDate = Calendar.getInstance().time
language = "en" language = "en"
} }
var buff: StringBuilder var buff: StringBuilder
var comment: EntryComment var comment: EntryComment
for (i in entries.size - 1 downTo 0) { for (i in entries.size - 1 downTo 0) {
with(entries[i]) { with(entries[i]) {
buff = StringBuilder() buff = StringBuilder()
.append("Posted by <b>") .append("Posted by <b>")
.append(nick) .append(nick)
.append("</b> on <a href=\"irc://") .append("</b> on <a href=\"irc://")
.append(bot.ircServer).append('/') .append(bot.ircServer).append('/')
.append(channel) .append(channel)
.append("\"><b>") .append("\"><b>")
.append(channel) .append(channel)
.append("</b></a>") .append("</b></a>")
if (comments.size > 0) { if (comments.size > 0) {
buff.append(" <br/><br/>") buff.append(" <br/><br/>")
val comments = comments val comments = comments
for (j in comments.indices) { for (j in comments.indices) {
comment = comments[j] comment = comments[j]
if (j > 0) { if (j > 0) {
buff.append(" <br/>") buff.append(" <br/>")
}
buff.append(comment.nick).append(": ").append(comment.comment)
} }
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 = SyndEntryImpl()
item.link = link item.apply {
item.description = SyndContentImpl().apply { value = buff.toString() } link = bot.backlogsUrl + date + ".xml"
item.title = title title = date
item.publishedDate = date description = SyndContentImpl().apply { value = "Links for $date" }
item.author = "${bot.channel.substring(1)}@${bot.ircServer} ($nick)" }
item.categories = tags
items.add(item) items.add(item)
} }
rss.entries = items
if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the backlog feed.")
output.output(rss, fw)
} }
rss.entries = items } else {
if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the entries feed.") bot.logger.warn("Unable to generate the backlogs feed. No property configured.")
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 > 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.")
} }
} }
} }