External plugins

This commit is contained in:
ekardnam 2019-07-22 18:36:09 +02:00
parent 04537e3a46
commit e97c52212a
6 changed files with 124 additions and 26 deletions

View file

@ -17,6 +17,8 @@
package org.congressodeiradicali.karlmarx
import java.io.File
import com.bot4s.telegram.api.RequestHandler
import com.bot4s.telegram.api.declarative.{Command, Commands}
import com.bot4s.telegram.clients.ScalajHttpClient
@ -52,6 +54,21 @@ class Bot(val token: String) extends TelegramBot
plugin(new SetPlugin(this))
)
// Load external plugins
val pluginLoader: PluginLoader = new JARPluginLoader()
private def loadExternalPlugins = {
val d = new File("plugins")
if (d.exists && d.isDirectory) {
d.listFiles().filter(_.isFile).map(pluginLoader.loadFromFile)
}
}
loadExternalPlugins
////////////////////////
// Future[Option[T]] is just two wrappers; remove the inner, useless one
def collapseFutureOption[T](arg: Future[Option[T]]): Future[T] = arg.flatMap {
case Some(x) => Future(x)

View file

@ -0,0 +1,65 @@
/*
This file is part of karl-marx.
karl-marx is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
karl-marx is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with karl-marx. If not, see <https://www.gnu.org/licenses/>.
*/
package org.congressodeiradicali.karlmarx
import java.io.File
import java.net.URL
import java.util.Properties
import java.util.jar.JarFile
import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader
import scala.util.{Failure, Success, Try}
class JARPluginLoader extends PluginLoader {
final val MANIFEST_LOCATION = "plugin-manifest.properties"
// manifest structure is
// package=some.package
// class=PluginClassName
override def loadFromFile(file: File): Try[Plugin] = {
val jarFile = new JarFile(file)
val manifestInputStream = jarFile.getInputStream(jarFile.getEntry(MANIFEST_LOCATION))
val props = new Properties()
props.load(manifestInputStream)
manifestInputStream.close()
val `package` = props.getProperty("package")
val `class` = props.getProperty("class")
val fullClassName = s"${`package`}.${`class`}"
try {
val url = file.toURI.toURL
val jarUrl = s"jar:${url.toString}!/"
val urls = Seq[URL](new URL(jarUrl))
val ucl = new URLClassLoader(urls, null)
// TODO deprecation
val plugin = Class.forName(fullClassName, true, ucl).newInstance().asInstanceOf[Plugin]
Success(plugin)
} catch {
case e: Throwable => Failure(e)
}
}
}

View file

@ -33,7 +33,6 @@ object Locales {
(LocalizableString.BAN_UNAUTHORIZED, "Only an administrator can ban users"),
(LocalizableString.BAN_SUCCESSFUL, "User was banned successfully"),
(LocalizableString.BAN_FAILED, "Cannot ban, something went wrong"),
(LocalizableString.BAN_FAILED_INVALID_USER, "Cannot ban, invalid user"),
(LocalizableString.BAN_FAILED_BAN_ADMIN, "Cannot ban an admin user"),
(LocalizableString.BAN_FAILED_REPLY, "Please reply to a message to ban someone"),
@ -49,7 +48,6 @@ object Locales {
(LocalizableString.BAN_UNAUTHORIZED, "Solo un amministratore può bannare gli utenti"),
(LocalizableString.BAN_SUCCESSFUL, "Utente bannato con successo"),
(LocalizableString.BAN_FAILED, "Non sono riuscito a bannare, qualcosa è andato storto"),
(LocalizableString.BAN_FAILED_INVALID_USER, "Impossibile bannare, utente non valido"),
(LocalizableString.BAN_FAILED_BAN_ADMIN, "Non puoi bannare un admin"),
(LocalizableString.BAN_FAILED_REPLY, "Rispondi a un messaggio per bannare qualcuno"),

View file

@ -0,0 +1,28 @@
/*
This file is part of karl-marx.
karl-marx is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
karl-marx is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with karl-marx. If not, see <https://www.gnu.org/licenses/>.
*/
package org.congressodeiradicali.karlmarx
import java.io.File
import scala.util.Try
trait PluginLoader {
def loadFromFile(file: File): Try[Plugin]
}

View file

@ -18,11 +18,9 @@
package org.congressodeiradicali.karlmarx.coreplugins
import com.bot4s.telegram.api.declarative.{CommandFilterMagnet, CommandImplicits}
import org.congressodeiradicali.karlmarx.LocalizableString.LocalizableString
import org.congressodeiradicali.karlmarx._
import scala.concurrent.Future
import scala.util.{Failure, Success}
class BanPlugin(bot: Bot) extends CorePlugin with CommandImplicits {
@ -32,33 +30,25 @@ class BanPlugin(bot: Bot) extends CorePlugin with CommandImplicits {
override val identifier: String = "ban"
override var commandHandlers: Map[CommandFilterMagnet, CommandHandler] = Map {
stringToCommandFilter("ban") -> { (msg, argv) =>
stringToCommandFilter("ban") -> { (msg, _) =>
var futureLocalizedString: Future[LocalizableString] = null
if (argv.length > 0) {
} else {
futureLocalizedString = msg.replyToMessage.fold {
Future { LocalizableString.BAN_FAILED_REPLY }
} { reply =>
reply.from.fold {
Future { LocalizableString.BAN_FAILED_INVALID_USER }
} { user =>
bot.requestBotUser(user, msg.chat).map { target =>
if (target.isAdmin || target.isCreator)
LocalizableString.BAN_FAILED_BAN_ADMIN
else {
target.ban()
LocalizableString.BAN_SUCCESSFUL
}
val futureLocalizedString = msg.replyToMessage.fold {
Future { LocalizableString.BAN_FAILED_REPLY }
} { reply =>
reply.from.fold {
Future { LocalizableString.BAN_FAILED_INVALID_USER }
} { user =>
bot.requestBotUser(user, msg.chat).map { target =>
if (target.isAdmin || target.isCreator)
LocalizableString.BAN_FAILED_BAN_ADMIN
else {
target.ban()
LocalizableString.BAN_SUCCESSFUL
}
}
}
}
futureLocalizedString.map { s => Some(bot.localize(s)) }
}
}

View file

@ -71,7 +71,7 @@ class PluginManagerPlugin(bot: Bot) extends CorePlugin with CommandImplicits {
Some(
bot.plugins.values.map { plugin =>
s"${plugin.identifier}: ${plugin.name}. ${plugin.description}. License: ${plugin.license}. Author: ${plugin.author}"
}.mkString("\n")
}.mkString("\n\n")
)
}
},