Cleanup tests and KDocs
This commit is contained in:
parent
b3aab20adf
commit
5bedd323ca
105 changed files with 2523 additions and 1542 deletions
6
.idea/kotlinc.xml
generated
6
.idea/kotlinc.xml
generated
|
@ -7,10 +7,10 @@
|
||||||
<option name="jvmTarget" value="17" />
|
<option name="jvmTarget" value="17" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinCommonCompilerArguments">
|
<component name="KotlinCommonCompilerArguments">
|
||||||
<option name="apiVersion" value="2.0" />
|
<option name="apiVersion" value="2.2" />
|
||||||
<option name="languageVersion" value="2.0" />
|
<option name="languageVersion" value="2.2" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="2.1.0" />
|
<option name="version" value="2.1.20" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
110
.idea/misc.xml
generated
110
.idea/misc.xml
generated
|
@ -1,12 +1,122 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="EntryPointsManager">
|
<component name="EntryPointsManager">
|
||||||
|
<entry_points version="2.0">
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.Calc name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.ChannelFeed help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Comment help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Cycle help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Die help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Info help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.LinksManager help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Me help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Modules help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Msg help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Nick help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Posting help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Recap help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Say help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Tags help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Users help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Versions help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.View help" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.ChannelFeed isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Comment isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Cycle isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Die isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Ignore isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Info isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.LinksManager isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Me isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Modules isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Msg isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Nick isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Posting isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Recap isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Say isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.seen.Seen isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Tags isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.tell.Tell isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Users isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Versions isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.View isOpOnly" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.WorldTime isPrivateMsgEnabled" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.ChannelFeed isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Comment isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Cycle isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Die isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Ignore isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Info isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.LinksManager isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Me isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Modules isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Msg isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Nick isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Posting isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Recap isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Say isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.seen.Seen isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Tags isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.tell.Tell isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Users isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Versions isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.View isPublic" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.ChannelFeed isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Comment isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Cycle isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Die isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Ignore isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Info isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.LinksManager isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Me isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Modules isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Msg isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Nick isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Posting isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Recap isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Say isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.seen.Seen isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Tags isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.tell.Tell isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Users isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Versions isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.View isVisible" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.ChannelFeed name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Comment name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.CryptoPrices name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.CurrencyConverter name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.Dice name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.Die name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.GoogleSearch name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.Joke name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.LinksManager name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.Lookup name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.Mastodon name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.Ping name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Posting name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.RockPaperScissors name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.StockQuote name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.commands.links.Tags name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.War name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.Weather2 name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.WolframAlpha name" />
|
||||||
|
<entry_point TYPE="field" FQNAME="net.thauvin.erik.mobibot.modules.WorldTime name" />
|
||||||
|
</entry_points>
|
||||||
<pattern value="net.thauvin.erik.MobibotBuild" method="deploy" />
|
<pattern value="net.thauvin.erik.MobibotBuild" method="deploy" />
|
||||||
<pattern value="net.thauvin.erik.MobibotBuild" method="jacoco" />
|
<pattern value="net.thauvin.erik.MobibotBuild" method="jacoco" />
|
||||||
<pattern value="net.thauvin.erik.MobibotBuild" />
|
<pattern value="net.thauvin.erik.MobibotBuild" />
|
||||||
<pattern value="net.thauvin.erik.MobibotBuild" method="detekt" />
|
<pattern value="net.thauvin.erik.MobibotBuild" method="detekt" />
|
||||||
<pattern value="net.thauvin.erik.MobibotBuild" method="detektBaseline" />
|
<pattern value="net.thauvin.erik.MobibotBuild" method="detektBaseline" />
|
||||||
<pattern value="net.thauvin.erik.MobibotBuild" method="rootPom" />
|
<pattern value="net.thauvin.erik.MobibotBuild" method="rootPom" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.Addons" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.Addons" method="match" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.modules.Calc" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.modules.Calc.Companion" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.modules.Mastodon" method="formatEntry" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.Mobibot.Companion" method="main" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.DisableOnCi" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.modules.ModuleExceptionTest.Companion" method="moduleExceptions" />
|
||||||
|
<pattern value="net.thauvin.erik.mobibot.modules.WolframAlphaTest.Companion" method="queries" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build" />
|
<output url="file://$PROJECT_DIR$/build" />
|
||||||
|
|
9
.idea/runConfigurations/Run Tests.xml
generated
9
.idea/runConfigurations/Run Tests.xml
generated
|
@ -1,9 +0,0 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Run Tests" type="Application" factoryName="Application" nameIsGenerated="true">
|
|
||||||
<option name="MAIN_CLASS_NAME" value="net.thauvin.erik.MobibotTest" />
|
|
||||||
<module name="app" />
|
|
||||||
<method v="2">
|
|
||||||
<option name="Make" enabled="true" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" ?>
|
<?xml version="1.0" ?>
|
||||||
<SmellBaseline>
|
<SmellBaseline>
|
||||||
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
<ManuallySuppressedIssues/>
|
||||||
<CurrentIssues>
|
<CurrentIssues>
|
||||||
<ID>CyclomaticComplexMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID>
|
<ID>CyclomaticComplexMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID>
|
||||||
<ID>CyclomaticComplexMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
<ID>CyclomaticComplexMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||||
|
@ -10,8 +10,6 @@
|
||||||
<ID>LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
<ID>LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||||
<ID>LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent )</ID>
|
<ID>LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent )</ID>
|
||||||
<ID>LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String )</ID>
|
<ID>LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String )</ID>
|
||||||
<ID>MagicNumber:ChatGpt.kt$ChatGpt.Companion$200</ID>
|
|
||||||
<ID>MagicNumber:ChatGpt.kt$ChatGpt.Companion$429</ID>
|
|
||||||
<ID>MagicNumber:Comment.kt$Comment$3</ID>
|
<ID>MagicNumber:Comment.kt$Comment$3</ID>
|
||||||
<ID>MagicNumber:CryptoPrices.kt$CryptoPrices$10</ID>
|
<ID>MagicNumber:CryptoPrices.kt$CryptoPrices$10</ID>
|
||||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID>
|
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID>
|
||||||
|
@ -47,7 +45,6 @@
|
||||||
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID>
|
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID>
|
||||||
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean</ID>
|
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean</ID>
|
||||||
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID>
|
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID>
|
||||||
<ID>NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String</ID>
|
|
||||||
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?)</ID>
|
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?)</ID>
|
||||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message</ID>
|
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message</ID>
|
||||||
|
@ -71,7 +68,6 @@
|
||||||
<ID>ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean</ID>
|
<ID>ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean</ID>
|
||||||
<ID>ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException</ID>
|
<ID>ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException</ID>
|
||||||
<ID>ReturnCount:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
<ID>ReturnCount:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
<ID>ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String</ID>
|
|
||||||
<ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message></ID>
|
<ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message></ID>
|
||||||
<ID>ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message></ID>
|
<ID>ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message></ID>
|
||||||
<ID>ThrowsCount:Mastodon.kt$Mastodon.Companion$@JvmStatic @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String</ID>
|
<ID>ThrowsCount:Mastodon.kt$Mastodon.Companion$@JvmStatic @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String</ID>
|
||||||
|
@ -80,7 +76,6 @@
|
||||||
<ID>ThrowsCount:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
<ID>ThrowsCount:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||||
<ID>ThrowsCount:WolframAlpha.kt$WolframAlpha.Companion$@JvmStatic @Throws(ModuleException::class) fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String</ID>
|
<ID>ThrowsCount:WolframAlpha.kt$WolframAlpha.Companion$@JvmStatic @Throws(ModuleException::class) fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String</ID>
|
||||||
<ID>TooGenericExceptionCaught:ChatGpt2.kt$ChatGpt2.Companion$e: Exception</ID>
|
<ID>TooGenericExceptionCaught:ChatGpt2.kt$ChatGpt2.Companion$e: Exception</ID>
|
||||||
<ID>TooGenericExceptionCaught:Gemini.kt$Gemini.Companion$e: Exception</ID>
|
|
||||||
<ID>TooGenericExceptionCaught:Gemini2.kt$Gemini2.Companion$e: Exception</ID>
|
<ID>TooGenericExceptionCaught:Gemini2.kt$Gemini2.Companion$e: Exception</ID>
|
||||||
<ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID>
|
<ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID>
|
||||||
<ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID>
|
<ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID>
|
||||||
|
@ -94,9 +89,9 @@
|
||||||
<ID>WildcardImport:FeedReaderTest.kt$import assertk.assertions.*</ID>
|
<ID>WildcardImport:FeedReaderTest.kt$import assertk.assertions.*</ID>
|
||||||
<ID>WildcardImport:FeedsManager.kt$import com.rometools.rome.feed.synd.*</ID>
|
<ID>WildcardImport:FeedsManager.kt$import com.rometools.rome.feed.synd.*</ID>
|
||||||
<ID>WildcardImport:Gemini2Test.kt$import assertk.assertions.*</ID>
|
<ID>WildcardImport:Gemini2Test.kt$import assertk.assertions.*</ID>
|
||||||
<ID>WildcardImport:GeminiTest.kt$import assertk.assertions.*</ID>
|
|
||||||
<ID>WildcardImport:GoogleSearchTest.kt$import assertk.assertions.*</ID>
|
<ID>WildcardImport:GoogleSearchTest.kt$import assertk.assertions.*</ID>
|
||||||
<ID>WildcardImport:JokeTest.kt$import assertk.assertions.*</ID>
|
<ID>WildcardImport:JokeTest.kt$import assertk.assertions.*</ID>
|
||||||
|
<ID>WildcardImport:LinksManagerTest.kt$import assertk.assertions.*</ID>
|
||||||
<ID>WildcardImport:Mobibot.kt$import java.io.*</ID>
|
<ID>WildcardImport:Mobibot.kt$import java.io.*</ID>
|
||||||
<ID>WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.*</ID>
|
<ID>WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.*</ID>
|
||||||
<ID>WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.links.*</ID>
|
<ID>WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.links.*</ID>
|
||||||
|
|
4
pom.xml
4
pom.xml
|
@ -4,7 +4,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>net.thauvin.erik.mobibot</groupId>
|
<groupId>net.thauvin.erik.mobibot</groupId>
|
||||||
<artifactId>mobibot</artifactId>
|
<artifactId>mobibot</artifactId>
|
||||||
<version>0.8.0-rc+20250424113056</version>
|
<version>0.8.0-rc+20250506091211</version>
|
||||||
<name>mobibot</name>
|
<name>mobibot</name>
|
||||||
<description></description>
|
<description></description>
|
||||||
<url></url>
|
<url></url>
|
||||||
|
@ -168,7 +168,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jsoup</groupId>
|
<groupId>org.jsoup</groupId>
|
||||||
<artifactId>jsoup</artifactId>
|
<artifactId>jsoup</artifactId>
|
||||||
<version>1.19.1</version>
|
<version>1.20.1</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -16,6 +16,8 @@ ident=changeme
|
||||||
logs=./logs
|
logs=./logs
|
||||||
ignore=chanserv,nickserv
|
ignore=chanserv,nickserv
|
||||||
tags=mobile mobitopia
|
tags=mobile mobitopia
|
||||||
|
|
||||||
|
# Keywords to add as tags if found in links titles
|
||||||
tags-keywords=android ios apple google
|
tags-keywords=android ios apple google
|
||||||
|
|
||||||
feed=http://www.mobitopia.org/rss.xml
|
feed=http://www.mobitopia.org/rss.xml
|
||||||
|
@ -68,7 +70,7 @@ disabled-modules=mastodon
|
||||||
#alphavantage-api-key=
|
#alphavantage-api-key=
|
||||||
|
|
||||||
#
|
#
|
||||||
# Get Wolfram Alpha AppID from: https://developer.wolframalpha.com/portal/
|
# Get Wolfram Alpha AppID from: https://developer.wolframalpha.com/
|
||||||
#
|
#
|
||||||
#wolfram-appid=
|
#wolfram-appid=
|
||||||
#wolfram-units=imperial
|
#wolfram-units=imperial
|
||||||
|
|
|
@ -38,7 +38,6 @@ import rife.bld.extension.CompileKotlinOperation;
|
||||||
import rife.bld.extension.DetektOperation;
|
import rife.bld.extension.DetektOperation;
|
||||||
import rife.bld.extension.GeneratedVersionOperation;
|
import rife.bld.extension.GeneratedVersionOperation;
|
||||||
import rife.bld.extension.JacocoReportOperation;
|
import rife.bld.extension.JacocoReportOperation;
|
||||||
import rife.bld.extension.kotlin.CompileOptions;
|
|
||||||
import rife.bld.operations.exceptions.ExitStatusException;
|
import rife.bld.operations.exceptions.ExitStatusException;
|
||||||
import rife.bld.publish.PomBuilder;
|
import rife.bld.publish.PomBuilder;
|
||||||
import rife.tools.FileUtils;
|
import rife.tools.FileUtils;
|
||||||
|
@ -72,8 +71,10 @@ public class MobibotBuild extends Project {
|
||||||
mainClass = pkg + ".Mobibot";
|
mainClass = pkg + ".Mobibot";
|
||||||
|
|
||||||
javaRelease = 17;
|
javaRelease = 17;
|
||||||
|
|
||||||
downloadSources = true;
|
downloadSources = true;
|
||||||
autoDownloadPurge = true;
|
autoDownloadPurge = true;
|
||||||
|
|
||||||
repositories = List.of(
|
repositories = List.of(
|
||||||
MAVEN_LOCAL,
|
MAVEN_LOCAL,
|
||||||
MAVEN_CENTRAL,
|
MAVEN_CENTRAL,
|
||||||
|
@ -166,10 +167,9 @@ public class MobibotBuild extends Project {
|
||||||
@Override
|
@Override
|
||||||
public void compile() throws Exception {
|
public void compile() throws Exception {
|
||||||
releaseInfo();
|
releaseInfo();
|
||||||
new CompileKotlinOperation()
|
var op = new CompileKotlinOperation().fromProject(this);
|
||||||
.compileOptions(new CompileOptions().progressive(true).verbose(true))
|
op.compileOptions().progressive(true).verbose(true);
|
||||||
.fromProject(this)
|
op.execute();
|
||||||
.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -41,7 +41,7 @@ import org.slf4j.LoggerFactory
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modules and Commands addons.
|
* Registers and manages commands and modules.
|
||||||
*/
|
*/
|
||||||
class Addons(private val props: Properties) {
|
class Addons(private val props: Properties) {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(Addons::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(Addons::class.java)
|
||||||
|
|
|
@ -30,10 +30,16 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot
|
package net.thauvin.erik.mobibot
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Constants`.
|
* The global constants.
|
||||||
*/
|
*/
|
||||||
object Constants {
|
object Constants {
|
||||||
|
/**
|
||||||
|
* CLI command for usage.
|
||||||
|
*/
|
||||||
|
const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The connect/read timeout in ms.
|
* The connect/read timeout in ms.
|
||||||
*/
|
*/
|
||||||
|
@ -54,16 +60,6 @@ object Constants {
|
||||||
*/
|
*/
|
||||||
const val DEFAULT_SERVER = "irc.libera.chat"
|
const val DEFAULT_SERVER = "irc.libera.chat"
|
||||||
|
|
||||||
/**
|
|
||||||
* CLI command for usage.
|
|
||||||
*/
|
|
||||||
const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User-Agent
|
|
||||||
*/
|
|
||||||
const val USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The help command.
|
* The help command.
|
||||||
*/
|
*/
|
||||||
|
@ -94,6 +90,11 @@ object Constants {
|
||||||
*/
|
*/
|
||||||
const val TIMER_DELAY = 10L
|
const val TIMER_DELAY = 10L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-Agent
|
||||||
|
*/
|
||||||
|
const val USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Properties version line argument.
|
* Properties version line argument.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -51,23 +51,6 @@ import java.net.URL
|
||||||
class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable {
|
class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java)
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the Feed's items.
|
|
||||||
*/
|
|
||||||
override fun run() {
|
|
||||||
try {
|
|
||||||
readFeed(url).forEach {
|
|
||||||
event.sendMessage("", it)
|
|
||||||
}
|
|
||||||
} catch (e: FeedException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e)
|
|
||||||
event.sendMessage("An error has occurred while parsing the feed: ${e.message}")
|
|
||||||
} catch (e: IOException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e)
|
|
||||||
event.sendMessage("An IO error has occurred while fetching the feed: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Throws(FeedException::class, IOException::class)
|
@Throws(FeedException::class, IOException::class)
|
||||||
|
@ -89,4 +72,21 @@ class FeedReader(private val url: String, val event: GenericMessageEvent) : Runn
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the Feed's items.
|
||||||
|
*/
|
||||||
|
override fun run() {
|
||||||
|
try {
|
||||||
|
readFeed(url).forEach {
|
||||||
|
event.sendMessage("", it)
|
||||||
|
}
|
||||||
|
} catch (e: FeedException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e)
|
||||||
|
event.sendMessage("An error has occurred while parsing the feed: ${e.message}")
|
||||||
|
} catch (e: IOException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e)
|
||||||
|
event.sendMessage("An IO error has occurred while fetching the feed: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ import kotlinx.cli.ArgType
|
||||||
import kotlinx.cli.default
|
import kotlinx.cli.default
|
||||||
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
||||||
import net.thauvin.erik.mobibot.Utils.bot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalize
|
||||||
import net.thauvin.erik.mobibot.Utils.getIntProperty
|
import net.thauvin.erik.mobibot.Utils.getIntProperty
|
||||||
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
@ -81,148 +81,6 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
|
||||||
/** Logger. */
|
/** Logger. */
|
||||||
val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java)
|
val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java)
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects to the server and joins the channel.
|
|
||||||
*/
|
|
||||||
fun connect() {
|
|
||||||
PircBotX(config).startBot()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds with the default help.
|
|
||||||
*/
|
|
||||||
private fun helpDefault(event: GenericMessageEvent) {
|
|
||||||
event.sendMessage("Type a URL on $channel to post it.")
|
|
||||||
event.sendMessage("For more information on a specific command, type:")
|
|
||||||
event.sendMessage(
|
|
||||||
helpFormat(
|
|
||||||
helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
event.sendMessage("The commands are:")
|
|
||||||
event.sendList(addons.names.commands, 8, isBold = true, isIndent = true)
|
|
||||||
if (event.isChannelOp(channel)) {
|
|
||||||
if (addons.names.disabledCommands.isNotEmpty()) {
|
|
||||||
event.sendMessage("The disabled commands are:")
|
|
||||||
event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true)
|
|
||||||
}
|
|
||||||
event.sendMessage("The op commands are:")
|
|
||||||
event.sendList(addons.names.ops, 8, isBold = true, isIndent = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds with the default, commands or modules help.
|
|
||||||
*/
|
|
||||||
private fun helpResponse(event: GenericMessageEvent, topic: String) {
|
|
||||||
if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) {
|
|
||||||
helpDefault(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAction(event: ActionEvent?) {
|
|
||||||
event?.channel?.let {
|
|
||||||
if (channel == it.name) {
|
|
||||||
event.user?.let { user ->
|
|
||||||
storeRecap(user.nick, event.action, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDisconnect(event: DisconnectEvent?) {
|
|
||||||
event?.let {
|
|
||||||
with(event.getBot<PircBotX>()) {
|
|
||||||
LinksManager.socialManager.notification("$nick disconnected from $serverHostname")
|
|
||||||
seen.add(userChannelDao.getChannel(channel).users)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LinksManager.socialManager.shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPrivateMessage(event: PrivateMessageEvent?) {
|
|
||||||
event?.user?.let { user ->
|
|
||||||
if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}")
|
|
||||||
val cmds = event.message.trim().split(" ".toRegex(), 2)
|
|
||||||
val cmd = cmds[0].lowercase()
|
|
||||||
val args = cmds.lastOrEmpty().trim()
|
|
||||||
if (cmd.startsWith(Constants.HELP_CMD)) { // help
|
|
||||||
helpResponse(event, args)
|
|
||||||
} else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module
|
|
||||||
helpDefault(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onJoin(event: JoinEvent?) {
|
|
||||||
event?.user?.let { user ->
|
|
||||||
with(event.getBot<PircBotX>()) {
|
|
||||||
if (user.nick == nick) {
|
|
||||||
LinksManager.socialManager.notification(
|
|
||||||
"$nick has joined ${event.channel.name} on $serverHostname"
|
|
||||||
)
|
|
||||||
seen.add(userChannelDao.getChannel(channel).users)
|
|
||||||
} else {
|
|
||||||
tell.send(event)
|
|
||||||
seen.add(user.nick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMessage(event: MessageEvent?) {
|
|
||||||
event?.user?.let { user ->
|
|
||||||
tell.send(event)
|
|
||||||
if (event.message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command>
|
|
||||||
if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}")
|
|
||||||
val cmds = event.message.substring(event.bot().nick.length + 1).trim().split(" ".toRegex(), 2)
|
|
||||||
val cmd = cmds[0].lowercase()
|
|
||||||
val args = cmds.lastOrEmpty().trim()
|
|
||||||
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
|
|
||||||
helpResponse(event, args)
|
|
||||||
} else {
|
|
||||||
// Execute module or command
|
|
||||||
addons.exec(channel, cmd, args, event)
|
|
||||||
}
|
|
||||||
} else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc.
|
|
||||||
if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}")
|
|
||||||
}
|
|
||||||
storeRecap(user.nick, event.message, false)
|
|
||||||
seen.add(user.nick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNickChange(event: NickChangeEvent?) {
|
|
||||||
event?.let {
|
|
||||||
tell.send(event)
|
|
||||||
if (!it.oldNick.equals(it.newNick, true)) {
|
|
||||||
seen.add(it.oldNick)
|
|
||||||
}
|
|
||||||
seen.add(it.newNick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPart(event: PartEvent?) {
|
|
||||||
event?.user?.let { user ->
|
|
||||||
with(event.getBot<PircBotX>()) {
|
|
||||||
if (user.nick == nick) {
|
|
||||||
LinksManager.socialManager.notification(
|
|
||||||
"$nick has left ${event.channel.name} on $serverHostname"
|
|
||||||
)
|
|
||||||
seen.add(userChannelDao.getChannel(channel).users)
|
|
||||||
} else {
|
|
||||||
seen.add(user.nick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onQuit(event: QuitEvent?) {
|
|
||||||
event?.user?.let { user ->
|
|
||||||
seen.add(user.nick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -254,7 +112,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
|
||||||
if (version) {
|
if (version) {
|
||||||
// Output the version
|
// Output the version
|
||||||
println(
|
println(
|
||||||
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" +
|
"${ReleaseInfo.PROJECT.capitalize()} ${ReleaseInfo.VERSION}" +
|
||||||
" (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})"
|
" (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})"
|
||||||
)
|
)
|
||||||
println(ReleaseInfo.WEBSITE)
|
println(ReleaseInfo.WEBSITE)
|
||||||
|
@ -416,5 +274,145 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
|
||||||
// Sort the addons
|
// Sort the addons
|
||||||
addons.names.sort()
|
addons.names.sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to the server and joins the channel.
|
||||||
|
*/
|
||||||
|
fun connect() {
|
||||||
|
PircBotX(config).startBot()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds with the default help.
|
||||||
|
*/
|
||||||
|
private fun helpDefault(event: GenericMessageEvent) {
|
||||||
|
event.sendMessage("Type a URL on $channel to post it.")
|
||||||
|
event.sendMessage("For more information on a specific command, type:")
|
||||||
|
event.sendMessage(
|
||||||
|
helpFormat(
|
||||||
|
helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
event.sendMessage("The commands are:")
|
||||||
|
event.sendList(addons.names.commands, 8, isBold = true, isIndent = true)
|
||||||
|
if (event.isChannelOp(channel)) {
|
||||||
|
if (addons.names.disabledCommands.isNotEmpty()) {
|
||||||
|
event.sendMessage("The disabled commands are:")
|
||||||
|
event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true)
|
||||||
|
}
|
||||||
|
event.sendMessage("The op commands are:")
|
||||||
|
event.sendList(addons.names.ops, 8, isBold = true, isIndent = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds with the default, commands or modules help.
|
||||||
|
*/
|
||||||
|
private fun helpResponse(event: GenericMessageEvent, topic: String) {
|
||||||
|
if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) {
|
||||||
|
helpDefault(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAction(event: ActionEvent?) {
|
||||||
|
event?.channel?.let {
|
||||||
|
if (channel == it.name) {
|
||||||
|
event.user?.let { user ->
|
||||||
|
storeRecap(user.nick, event.action, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisconnect(event: DisconnectEvent?) {
|
||||||
|
event?.let {
|
||||||
|
with(event.getBot<PircBotX>()) {
|
||||||
|
LinksManager.socialManager.notification("$nick disconnected from $serverHostname")
|
||||||
|
seen.add(userChannelDao.getChannel(channel).users)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LinksManager.socialManager.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrivateMessage(event: PrivateMessageEvent?) {
|
||||||
|
event?.user?.let { user ->
|
||||||
|
if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}")
|
||||||
|
val cmds = event.message.trim().split(" ".toRegex(), 2)
|
||||||
|
val cmd = cmds[0].lowercase()
|
||||||
|
val args = cmds.lastOrEmpty().trim()
|
||||||
|
if (cmd.startsWith(Constants.HELP_CMD)) { // help
|
||||||
|
helpResponse(event, args)
|
||||||
|
} else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module
|
||||||
|
helpDefault(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onJoin(event: JoinEvent?) {
|
||||||
|
event?.user?.let { user ->
|
||||||
|
with(event.getBot<PircBotX>()) {
|
||||||
|
if (user.nick == nick) {
|
||||||
|
LinksManager.socialManager.notification(
|
||||||
|
"$nick has joined ${event.channel.name} on $serverHostname"
|
||||||
|
)
|
||||||
|
seen.add(userChannelDao.getChannel(channel).users)
|
||||||
|
} else {
|
||||||
|
tell.send(event)
|
||||||
|
seen.add(user.nick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessage(event: MessageEvent?) {
|
||||||
|
event?.user?.let { user ->
|
||||||
|
if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}")
|
||||||
|
tell.send(event)
|
||||||
|
if (event.message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command>
|
||||||
|
val cmds = event.message.substring(event.bot().nick.length + 1).trim().split(" ".toRegex(), 2)
|
||||||
|
val cmd = cmds[0].lowercase()
|
||||||
|
val args = cmds.lastOrEmpty().trim()
|
||||||
|
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
|
||||||
|
helpResponse(event, args)
|
||||||
|
} else {
|
||||||
|
// Execute module or command
|
||||||
|
addons.exec(channel, cmd, args, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storeRecap(user.nick, event.message, false)
|
||||||
|
seen.add(user.nick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNickChange(event: NickChangeEvent?) {
|
||||||
|
event?.let {
|
||||||
|
tell.send(event)
|
||||||
|
if (!it.oldNick.equals(it.newNick, true)) {
|
||||||
|
seen.add(it.oldNick)
|
||||||
|
}
|
||||||
|
seen.add(it.newNick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPart(event: PartEvent?) {
|
||||||
|
event?.user?.let { user ->
|
||||||
|
with(event.getBot<PircBotX>()) {
|
||||||
|
if (user.nick == nick) {
|
||||||
|
LinksManager.socialManager.notification(
|
||||||
|
"$nick has left ${event.channel.name} on $serverHostname"
|
||||||
|
)
|
||||||
|
seen.add(userChannelDao.getChannel(channel).users)
|
||||||
|
} else {
|
||||||
|
seen.add(user.nick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQuit(event: QuitEvent?) {
|
||||||
|
event?.user?.let { user ->
|
||||||
|
seen.add(user.nick)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ import java.time.temporal.ChronoUnit
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles posts to pinboard.in.
|
* Handles posts to `pinboard.in`.
|
||||||
*/
|
*/
|
||||||
class Pinboard {
|
class Pinboard {
|
||||||
private val poster = PinboardPoster()
|
private val poster = PinboardPoster()
|
||||||
|
@ -56,13 +56,6 @@ class Pinboard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the pinboard API token.
|
|
||||||
*/
|
|
||||||
fun setApiToken(apiToken: String) {
|
|
||||||
poster.apiToken = apiToken
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a pin.
|
* Deletes a pin.
|
||||||
*/
|
*/
|
||||||
|
@ -73,6 +66,13 @@ class Pinboard {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the pinboard API token.
|
||||||
|
*/
|
||||||
|
fun setApiToken(apiToken: String) {
|
||||||
|
poster.apiToken = apiToken
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a pin.
|
* Updates a pin.
|
||||||
*/
|
*/
|
||||||
|
@ -87,15 +87,6 @@ class Pinboard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a date to a UTC timestamp.
|
|
||||||
*/
|
|
||||||
private fun Date.toTimestamp(): String {
|
|
||||||
return ZonedDateTime.ofInstant(
|
|
||||||
toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault()
|
|
||||||
).format(DateTimeFormatter.ISO_INSTANT)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the tags for pinboard.
|
* Formats the tags for pinboard.
|
||||||
*/
|
*/
|
||||||
|
@ -109,5 +100,14 @@ class Pinboard {
|
||||||
private fun EntryLink.postedBy(ircServer: String): String {
|
private fun EntryLink.postedBy(ircServer: String): String {
|
||||||
return "Posted by $nick on $channel ( $ircServer )"
|
return "Posted by $nick on $channel ( $ircServer )"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a date to a UTC timestamp.
|
||||||
|
*/
|
||||||
|
private fun Date.toTimestamp(): String {
|
||||||
|
return ZonedDateTime.ofInstant(
|
||||||
|
toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault()
|
||||||
|
).format(DateTimeFormatter.ISO_INSTANT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,12 @@ import java.time.ZoneId
|
||||||
*/
|
*/
|
||||||
object ReleaseInfo {
|
object ReleaseInfo {
|
||||||
const val PROJECT = "mobibot"
|
const val PROJECT = "mobibot"
|
||||||
const val VERSION = "0.8.0-rc+20250322004101"
|
const val VERSION = "0.8.0-rc+20250507140607"
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant(
|
val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant(
|
||||||
Instant.ofEpochMilli(1742629261438L), ZoneId.systemDefault()
|
Instant.ofEpochMilli(1746651967388L), ZoneId.systemDefault()
|
||||||
)
|
)
|
||||||
|
|
||||||
const val WEBSITE = "https://mobitopia.org/mobibot/"
|
const val WEBSITE = "https://mobitopia.org/mobibot/"
|
||||||
|
|
|
@ -52,7 +52,7 @@ import kotlin.io.path.exists
|
||||||
import kotlin.io.path.fileSize
|
import kotlin.io.path.fileSize
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Miscellaneous utilities.
|
* Miscellaneous utility functions.
|
||||||
*/
|
*/
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
object Utils {
|
object Utils {
|
||||||
|
@ -111,13 +111,13 @@ object Utils {
|
||||||
* Capitalize a string.
|
* Capitalize a string.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun String.capitalise(): String = lowercase().replaceFirstChar { it.uppercase() }
|
fun String.capitalize(): String = lowercase().replaceFirstChar { it.uppercase() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capitalize words
|
* Capitalize words
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() }
|
fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalize() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Colorize a string.
|
* Colorize a string.
|
||||||
|
@ -204,7 +204,7 @@ object Utils {
|
||||||
fun Int.isHttpSuccess() = this in 200..399
|
fun Int.isHttpSuccess() = this in 200..399
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the last item of a list of strings or empty if none.
|
* Returns the last item of a list or empty if the list only has one item.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun List<String>.lastOrEmpty(): String {
|
fun List<String>.lastOrEmpty(): String {
|
||||||
|
@ -261,6 +261,32 @@ object Utils {
|
||||||
return if (count > 1) "${this}s" else this
|
return if (count > 1) "${this}s" else this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads contents of a URL.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun URL.reader(): UrlReaderResponse {
|
||||||
|
val connection = this.openConnection() as HttpURLConnection
|
||||||
|
try {
|
||||||
|
connection.setRequestProperty(
|
||||||
|
"User-Agent",
|
||||||
|
Constants.USER_AGENT
|
||||||
|
)
|
||||||
|
return if (connection.responseCode.isHttpSuccess()) {
|
||||||
|
UrlReaderResponse(
|
||||||
|
connection.responseCode,
|
||||||
|
connection.inputStream.bufferedReader().use { it.readText() })
|
||||||
|
} else {
|
||||||
|
UrlReaderResponse(
|
||||||
|
connection.responseCode,
|
||||||
|
connection.errorStream.bufferedReader().use { it.readText() })
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
connection.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the given string red.
|
* Makes the given string red.
|
||||||
*/
|
*/
|
||||||
|
@ -401,45 +427,19 @@ object Utils {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the given string bold.
|
* Makes the given string bold.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun String?.underline(): String = colorize(Colors.UNDERLINE)
|
fun String?.underline(): String = colorize(Colors.UNDERLINE)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts XML/XHTML entities to plain text.
|
* Converts XML/XHTML entities to plain text.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun String.unescapeXml(): String = Jsoup.parse(this).text()
|
fun String.unescapeXml(): String = Jsoup.parse(this).text()
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads contents of a URL.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun URL.reader(): UrlReaderResponse {
|
|
||||||
val connection = this.openConnection() as HttpURLConnection
|
|
||||||
try {
|
|
||||||
connection.setRequestProperty(
|
|
||||||
"User-Agent",
|
|
||||||
Constants.USER_AGENT
|
|
||||||
)
|
|
||||||
return if (connection.responseCode.isHttpSuccess()) {
|
|
||||||
UrlReaderResponse(
|
|
||||||
connection.responseCode,
|
|
||||||
connection.inputStream.bufferedReader().use { it.readText() })
|
|
||||||
} else {
|
|
||||||
UrlReaderResponse(
|
|
||||||
connection.responseCode,
|
|
||||||
connection.errorStream.bufferedReader().use { it.readText() })
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
connection.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the [URL.reader] response code and body text.
|
* Holds the [URL.reader] response code and body text.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -38,6 +38,12 @@ import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an abstract base class for implementing bot commands.
|
||||||
|
*
|
||||||
|
* A command is characterized by its name, visibility, access restrictions, and other properties. This class provides a
|
||||||
|
* framework for handling command-specific responses and help information.
|
||||||
|
*/
|
||||||
abstract class AbstractCommand {
|
abstract class AbstractCommand {
|
||||||
abstract val name: String
|
abstract val name: String
|
||||||
abstract val help: List<String>
|
abstract val help: List<String>
|
||||||
|
|
|
@ -35,6 +35,9 @@ import net.thauvin.erik.mobibot.FeedReader
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the last 5 posts from the channel's weblog feed.
|
||||||
|
*/
|
||||||
class ChannelFeed(channel: String) : AbstractCommand() {
|
class ChannelFeed(channel: String) : AbstractCommand() {
|
||||||
override val name = channel
|
override val name = channel
|
||||||
override val help = listOf("To list the last 5 posts from the channel's weblog feed:", helpFormat("%c $channel"))
|
override val help = listOf("To list the last 5 posts from the channel's weblog feed:", helpFormat("%c $channel"))
|
||||||
|
|
|
@ -39,6 +39,9 @@ import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an operator to have the bot leave the channel and come back.
|
||||||
|
*/
|
||||||
class Cycle : AbstractCommand() {
|
class Cycle : AbstractCommand() {
|
||||||
private val wait = 10
|
private val wait = 10
|
||||||
override val name = "cycle"
|
override val name = "cycle"
|
||||||
|
|
|
@ -35,6 +35,9 @@ import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an operator to terminate the bot's operations on the server.
|
||||||
|
*/
|
||||||
class Die : AbstractCommand() {
|
class Die : AbstractCommand() {
|
||||||
override val name = "die"
|
override val name = "die"
|
||||||
override val help = emptyList<String>()
|
override val help = emptyList<String>()
|
||||||
|
@ -42,6 +45,14 @@ class Die : AbstractCommand() {
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = false
|
override val isVisible = false
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DIE_PROP = "die"
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
initProperties(DIE_PROP)
|
||||||
|
}
|
||||||
|
|
||||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
with(event.bot()) {
|
with(event.bot()) {
|
||||||
if (event.isChannelOp(channel) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) {
|
if (event.isChannelOp(channel) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) {
|
||||||
|
@ -51,12 +62,4 @@ class Die : AbstractCommand() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val DIE_PROP = "die"
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
initProperties(DIE_PROP)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,23 @@ import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.commands.links.LinksManager
|
import net.thauvin.erik.mobibot.commands.links.LinksManager
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds or removed nicks from the ignored list.
|
||||||
|
*/
|
||||||
class Ignore : AbstractCommand() {
|
class Ignore : AbstractCommand() {
|
||||||
private val me = "me"
|
private val me = "me"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val IGNORE_CMD = "ignore"
|
||||||
|
const val IGNORE_PROP = IGNORE_CMD
|
||||||
|
private val ignored = mutableSetOf<String>()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isNotIgnored(nick: String): Boolean {
|
||||||
|
return !ignored.contains(nick.lowercase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initProperties(IGNORE_PROP)
|
initProperties(IGNORE_PROP)
|
||||||
}
|
}
|
||||||
|
@ -65,17 +79,6 @@ class Ignore : AbstractCommand() {
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val IGNORE_CMD = "ignore"
|
|
||||||
const val IGNORE_PROP = IGNORE_CMD
|
|
||||||
private val ignored = mutableSetOf<String>()
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun isNotIgnored(nick: String): Boolean {
|
|
||||||
return !ignored.contains(nick.lowercase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
val isMe = args.trim().equals(me, true)
|
val isMe = args.trim().equals(me, true)
|
||||||
if (isMe || !event.isChannelOp(channel)) {
|
if (isMe || !event.isChannelOp(channel)) {
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalize
|
||||||
import net.thauvin.erik.mobibot.Utils.green
|
import net.thauvin.erik.mobibot.Utils.green
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
@ -46,9 +46,12 @@ import java.lang.management.ManagementFactory
|
||||||
import kotlin.time.DurationUnit
|
import kotlin.time.DurationUnit
|
||||||
import kotlin.time.toDuration
|
import kotlin.time.toDuration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides detailed bot and channel statistics.
|
||||||
|
*/
|
||||||
class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() {
|
class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() {
|
||||||
private val allVersions = listOf(
|
private val allVersions = listOf(
|
||||||
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})",
|
"${ReleaseInfo.PROJECT.capitalize()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})",
|
||||||
"Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})"
|
"Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})"
|
||||||
)
|
)
|
||||||
override val name = "info"
|
override val name = "info"
|
||||||
|
@ -71,30 +74,32 @@ class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() {
|
||||||
val weeks = days / 7
|
val weeks = days / 7
|
||||||
days %= 7
|
days %= 7
|
||||||
|
|
||||||
with(StringBuffer()) {
|
with(mutableListOf<String>()) {
|
||||||
if (years > 0) {
|
if (years > 0) {
|
||||||
append(years).append(" year".plural(years)).append(' ')
|
add("$years".plus(" year".plural(years)))
|
||||||
}
|
}
|
||||||
if (months > 0) {
|
if (months > 0) {
|
||||||
append(months).append(" month".plural(months)).append(' ')
|
add("$months".plus(" month".plural(months)))
|
||||||
}
|
}
|
||||||
if (weeks > 0) {
|
if (weeks > 0) {
|
||||||
append(weeks).append(" week".plural(weeks)).append(' ')
|
add("$weeks".plus(" week".plural(weeks)))
|
||||||
}
|
}
|
||||||
if (days > 0) {
|
if (days > 0) {
|
||||||
append(days).append(" day".plural(days)).append(' ')
|
add("$days".plus(" day".plural(days)))
|
||||||
}
|
}
|
||||||
if (hours > 0) {
|
if (hours > 0) {
|
||||||
append(hours).append(" hour".plural(hours.toLong())).append(' ')
|
add("$hours".plus(" hour".plural(hours.toLong())))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minutes > 0) {
|
if (minutes > 0) {
|
||||||
append(minutes).append(" minute".plural(minutes.toLong()))
|
add("$minutes".plus(" minute".plural(minutes.toLong())))
|
||||||
} else {
|
} else if (seconds > 0) {
|
||||||
append(seconds).append(" second".plural(seconds.toLong()))
|
add("$seconds seconds")
|
||||||
|
} else if (isEmpty()) {
|
||||||
|
return "0 second"
|
||||||
}
|
}
|
||||||
|
|
||||||
return toString()
|
return this.joinToString(" ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an operator to have the bot perform an action in the specified channel.
|
||||||
|
*/
|
||||||
class Me : AbstractCommand() {
|
class Me : AbstractCommand() {
|
||||||
override val name = "me"
|
override val name = "me"
|
||||||
override val help = listOf("To have the bot perform an action:", helpFormat("%c $name <action>"))
|
override val help = listOf("To have the bot perform an action:", helpFormat("%c $name <action>"))
|
||||||
|
|
|
@ -36,6 +36,9 @@ import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
import net.thauvin.erik.mobibot.Utils.sendList
|
import net.thauvin.erik.mobibot.Utils.sendList
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the enabled/disabled modules.
|
||||||
|
*/
|
||||||
class Modules(private val modules: List<String>, private val disabledModules: List<String>) : AbstractCommand() {
|
class Modules(private val modules: List<String>, private val disabledModules: List<String>) : AbstractCommand() {
|
||||||
override val name = "modules"
|
override val name = "modules"
|
||||||
override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name"))
|
override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name"))
|
||||||
|
|
|
@ -36,6 +36,9 @@ import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an operator to send a private message to the specified user.
|
||||||
|
*/
|
||||||
class Msg : AbstractCommand() {
|
class Msg : AbstractCommand() {
|
||||||
override val name = "msg"
|
override val name = "msg"
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
|
|
|
@ -36,6 +36,9 @@ import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an operator to change the bot's nickname.
|
||||||
|
*/
|
||||||
class Nick : AbstractCommand() {
|
class Nick : AbstractCommand() {
|
||||||
override val name = "nick"
|
override val name = "nick"
|
||||||
override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <new_nick>"))
|
override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <new_nick>"))
|
||||||
|
|
|
@ -38,6 +38,9 @@ import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the last 10 public channel messages.
|
||||||
|
*/
|
||||||
class Recap : AbstractCommand() {
|
class Recap : AbstractCommand() {
|
||||||
override val name = "recap"
|
override val name = "recap"
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
|
|
|
@ -36,6 +36,9 @@ import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an operator to have the bot say something in the specified channel.
|
||||||
|
*/
|
||||||
class Say : AbstractCommand() {
|
class Say : AbstractCommand() {
|
||||||
override val name = "say"
|
override val name = "say"
|
||||||
override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name <text>"))
|
override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name <text>"))
|
||||||
|
|
|
@ -36,6 +36,9 @@ import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.Utils.sendList
|
import net.thauvin.erik.mobibot.Utils.sendList
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the users present on the channel.
|
||||||
|
*/
|
||||||
class Users : AbstractCommand() {
|
class Users : AbstractCommand() {
|
||||||
override val name = "users"
|
override val name = "users"
|
||||||
override val help = listOf("To list the users present on the channel:", helpFormat("%c $name"))
|
override val help = listOf("To list the users present on the channel:", helpFormat("%c $name"))
|
||||||
|
|
|
@ -38,6 +38,9 @@ import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||||
import org.pircbotx.PircBotX
|
import org.pircbotx.PircBotX
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the bot version, OS version, JVM version, Kotlin version and PircBotX version.
|
||||||
|
*/
|
||||||
class Versions : AbstractCommand() {
|
class Versions : AbstractCommand() {
|
||||||
private val allVersions = listOf(
|
private val allVersions = listOf(
|
||||||
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})",
|
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})",
|
||||||
|
|
|
@ -42,6 +42,10 @@ import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
||||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes commands such as viewing, deleting, editing, or changing the author of a comment
|
||||||
|
* based on the input arguments and the state of the entries.
|
||||||
|
*/
|
||||||
class Comment : AbstractCommand() {
|
class Comment : AbstractCommand() {
|
||||||
override val name = COMMAND
|
override val name = COMMAND
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
|
|
|
@ -49,6 +49,12 @@ import org.jsoup.Jsoup
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a URL, fetch its metadata, and register it with associated details.
|
||||||
|
*
|
||||||
|
* It checks for duplicate entries, retrieves or assigns a title, associates tags, and adds the URL to a collection of
|
||||||
|
* entries for further processing.
|
||||||
|
*/
|
||||||
class LinksManager : AbstractCommand() {
|
class LinksManager : AbstractCommand() {
|
||||||
private val defaultTags: MutableList<String> = mutableListOf()
|
private val defaultTags: MutableList<String> = mutableListOf()
|
||||||
private val keywords: MutableList<String> = mutableListOf()
|
private val keywords: MutableList<String> = mutableListOf()
|
||||||
|
@ -59,10 +65,6 @@ class LinksManager : AbstractCommand() {
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = false
|
override val isVisible = false
|
||||||
|
|
||||||
init {
|
|
||||||
initProperties(TAGS_PROP, KEYWORDS_PROP)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*".toRegex()
|
val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*".toRegex()
|
||||||
const val KEYWORDS_PROP = "tags-keywords"
|
const val KEYWORDS_PROP = "tags-keywords"
|
||||||
|
@ -100,6 +102,10 @@ class LinksManager : AbstractCommand() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
initProperties(TAGS_PROP, KEYWORDS_PROP)
|
||||||
|
}
|
||||||
|
|
||||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
val cmds = args.split(" ".toRegex(), 2)
|
val cmds = args.split(" ".toRegex(), 2)
|
||||||
val sender = event.user.nick
|
val sender = event.user.nick
|
||||||
|
@ -120,10 +126,11 @@ class LinksManager : AbstractCommand() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title.isBlank()) {
|
if (title.isBlank()) {
|
||||||
title = fetchTitle(link)
|
title = fetchPageTitle(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title != Constants.NO_TITLE) {
|
if (title != Constants.NO_TITLE) {
|
||||||
|
// Add keywords as tags if found in the title
|
||||||
matchTagKeywords(title, tags)
|
matchTagKeywords(title, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +165,12 @@ class LinksManager : AbstractCommand() {
|
||||||
return message.matches(LINK_MATCH)
|
return message.matches(LINK_MATCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun fetchTitle(link: String): String {
|
/**
|
||||||
|
* Fetches and returns the page title of the given URL.
|
||||||
|
*
|
||||||
|
* If the title cannot be fetched or is blank, [Constants.NO_TITLE] is returned.
|
||||||
|
*/
|
||||||
|
internal fun fetchPageTitle(link: String): String {
|
||||||
try {
|
try {
|
||||||
val html = Jsoup.connect(link)
|
val html = Jsoup.connect(link)
|
||||||
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0")
|
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0")
|
||||||
|
@ -187,6 +199,10 @@ class LinksManager : AbstractCommand() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches [keywords] in the given title and adds them to the provided tag list.
|
||||||
|
*/
|
||||||
internal fun matchTagKeywords(title: String, tags: MutableList<String>) {
|
internal fun matchTagKeywords(title: String, tags: MutableList<String>) {
|
||||||
for (match in keywords) {
|
for (match in keywords) {
|
||||||
val m = Regex.escape(match)
|
val m = Regex.escape(match)
|
||||||
|
|
|
@ -44,6 +44,11 @@ import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
||||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies or displays the content of an entry.
|
||||||
|
*
|
||||||
|
* It handles actions such as adding comments, changing author, title, or URL, and removing or printing entries.
|
||||||
|
*/
|
||||||
class Posting : AbstractCommand() {
|
class Posting : AbstractCommand() {
|
||||||
override val name = "posting"
|
override val name = "posting"
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
|
@ -97,6 +102,20 @@ class Posting : AbstractCommand() {
|
||||||
entries.save()
|
entries.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) {
|
||||||
|
if (event.isChannelOp(channel)) {
|
||||||
|
if (cmd.length > 1) {
|
||||||
|
val entry: EntryLink = entries.links[index]
|
||||||
|
entry.nick = cmd.substring(1)
|
||||||
|
LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||||
|
event.sendMessage(EntriesUtils.printLink(index, entry))
|
||||||
|
entries.save()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.sendMessage("Please ask a channel op to change the author of this link for you.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||||
if (cmd.length > 1) {
|
if (cmd.length > 1) {
|
||||||
val entry: EntryLink = entries.links[entryIndex]
|
val entry: EntryLink = entries.links[entryIndex]
|
||||||
|
@ -121,20 +140,6 @@ class Posting : AbstractCommand() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) {
|
|
||||||
if (event.isChannelOp(channel)) {
|
|
||||||
if (cmd.length > 1) {
|
|
||||||
val entry: EntryLink = entries.links[index]
|
|
||||||
entry.nick = cmd.substring(1)
|
|
||||||
LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
|
||||||
event.sendMessage(EntriesUtils.printLink(index, entry))
|
|
||||||
entries.save()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
event.sendMessage("Please ask a channel op to change the author of this link for you.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) {
|
private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) {
|
||||||
val entry: EntryLink = entries.links[index]
|
val entry: EntryLink = entries.links[index]
|
||||||
if (entry.login == event.user.login || event.isChannelOp(channel)) {
|
if (entry.login == event.user.login || event.isChannelOp(channel)) {
|
||||||
|
|
|
@ -41,6 +41,12 @@ import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages tags associated with a specific link entry.
|
||||||
|
*
|
||||||
|
* Allows users to modify or view tags associated with a link. Users can only change tags for their own links unless
|
||||||
|
* they are channel operators.
|
||||||
|
*/
|
||||||
class Tags : AbstractCommand() {
|
class Tags : AbstractCommand() {
|
||||||
override val name = COMMAND
|
override val name = COMMAND
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
|
@ -55,6 +61,7 @@ class Tags : AbstractCommand() {
|
||||||
const val COMMAND = "tags"
|
const val COMMAND = "tags"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2)
|
val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2)
|
||||||
val index = cmds[0].toInt() - 1
|
val index = cmds[0].toInt() - 1
|
||||||
|
|
|
@ -43,6 +43,9 @@ import net.thauvin.erik.mobibot.entries.EntryLink
|
||||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a list of entries or an appropriate message if no entries exist.
|
||||||
|
*/
|
||||||
class View : AbstractCommand() {
|
class View : AbstractCommand() {
|
||||||
override val name = VIEW_CMD
|
override val name = VIEW_CMD
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
|
@ -61,12 +64,17 @@ class View : AbstractCommand() {
|
||||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
if (entries.links.isNotEmpty()) {
|
if (entries.links.isNotEmpty()) {
|
||||||
val p = parseArgs(args)
|
val p = parseArgs(args)
|
||||||
showPosts(p.first, p.second, event)
|
viewEntries(p.first, p.second, event)
|
||||||
} else {
|
} else {
|
||||||
event.sendMessage("There is currently nothing to view. Why don't you post something?")
|
event.sendMessage("There is currently nothing to view. Why don't you post something?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the view command input arguments and determines a starting index and query string.
|
||||||
|
*
|
||||||
|
*`view [<start>] [<query>]`
|
||||||
|
*/
|
||||||
internal fun parseArgs(args: String): Pair<Int, String> {
|
internal fun parseArgs(args: String): Pair<Int, String> {
|
||||||
var query = args.lowercase().trim()
|
var query = args.lowercase().trim()
|
||||||
var start = 0
|
var start = 0
|
||||||
|
@ -88,7 +96,7 @@ class View : AbstractCommand() {
|
||||||
return Pair(start, query)
|
return Pair(start, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showPosts(start: Int, query: String, event: GenericMessageEvent) {
|
private fun viewEntries(start: Int, query: String, event: GenericMessageEvent) {
|
||||||
var index = start
|
var index = start
|
||||||
var entry: EntryLink
|
var entry: EntryLink
|
||||||
var sent = 0
|
var sent = 0
|
||||||
|
|
|
@ -33,13 +33,15 @@ package net.thauvin.erik.mobibot.commands.seen
|
||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A comparator implementation for comparing nicknames in a case-insensitive manner.
|
||||||
|
*/
|
||||||
class NickComparator : Comparator<String>, Serializable {
|
class NickComparator : Comparator<String>, Serializable {
|
||||||
override fun compare(a: String, b: String): Int {
|
override fun compare(a: String, b: String): Int {
|
||||||
return a.lowercase().compareTo(b.lowercase())
|
return a.lowercase().compareTo(b.lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Suppress("ConstPropertyName")
|
|
||||||
private const val serialVersionUID = 1L
|
private const val serialVersionUID = 1L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,9 @@ import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays when a user was last seen, or all seen nicks.
|
||||||
|
*/
|
||||||
class Seen(private val serialObject: String) : AbstractCommand() {
|
class Seen(private val serialObject: String) : AbstractCommand() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(Seen::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(Seen::class.java)
|
||||||
private val allKeyword = "all"
|
private val allKeyword = "all"
|
||||||
|
@ -64,6 +66,9 @@ class Seen(private val serialObject: String) : AbstractCommand() {
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
init {
|
||||||
|
load()
|
||||||
|
}
|
||||||
|
|
||||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
if (isEnabled()) {
|
if (isEnabled()) {
|
||||||
|
@ -143,8 +148,4 @@ class Seen(private val serialObject: String) : AbstractCommand() {
|
||||||
fun save() {
|
fun save() {
|
||||||
saveSerialData(serialObject, seenNicks, logger, "seen nicknames")
|
saveSerialData(serialObject, seenNicks, logger, "seen nicknames")
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
load()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,11 @@ package net.thauvin.erik.mobibot.commands.seen
|
||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a [Seen] nickname.
|
||||||
|
*/
|
||||||
data class SeenNick(val nick: String, val lastSeen: Long) : Serializable {
|
data class SeenNick(val nick: String, val lastSeen: Long) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
@Suppress("ConstPropertyName")
|
|
||||||
private const val serialVersionUID = 1L
|
private const val serialVersionUID = 1L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import org.pircbotx.hooks.types.GenericUserEvent
|
import org.pircbotx.hooks.types.GenericUserEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Tell` command.
|
* Queues a message to be sent to someone when they join or are active on the channel.
|
||||||
*/
|
*/
|
||||||
class Tell(private val serialObject: String) : AbstractCommand() {
|
class Tell(private val serialObject: String) : AbstractCommand() {
|
||||||
// Messages queue
|
// Messages queue
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Tell Messages Manager.
|
* Manages the [Tell] messages queue.
|
||||||
*/
|
*/
|
||||||
object TellManager {
|
object TellManager {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(TellManager::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(TellManager::class.java)
|
||||||
|
|
|
@ -36,7 +36,7 @@ import java.time.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell Message.
|
* Holds a [Tell] message.
|
||||||
*/
|
*/
|
||||||
class TellMessage(
|
class TellMessage(
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +98,6 @@ class TellMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Suppress("ConstPropertyName")
|
|
||||||
private const val serialVersionUID = 2L
|
private const val serialVersionUID = 2L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,9 @@ package net.thauvin.erik.mobibot.entries
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Utils.today
|
import net.thauvin.erik.mobibot.Utils.today
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds [EntryLink] entries.
|
||||||
|
*/
|
||||||
class Entries(
|
class Entries(
|
||||||
var channel: String = "",
|
var channel: String = "",
|
||||||
var ircServer: String = "",
|
var ircServer: String = "",
|
||||||
|
|
|
@ -35,7 +35,7 @@ import net.thauvin.erik.mobibot.Utils.bold
|
||||||
import net.thauvin.erik.mobibot.Utils.green
|
import net.thauvin.erik.mobibot.Utils.green
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entries utilities.
|
* Provides functions used to manage [Entries].
|
||||||
*/
|
*/
|
||||||
object EntriesUtils {
|
object EntriesUtils {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,7 +34,7 @@ import java.io.Serializable
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry comments data class.
|
* [Entries] comment.
|
||||||
*/
|
*/
|
||||||
data class EntryComment(var comment: String, var nick: String) : Serializable {
|
data class EntryComment(var comment: String, var nick: String) : Serializable {
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +46,6 @@ data class EntryComment(var comment: String, var nick: String) : Serializable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Serial version UID
|
// Serial version UID
|
||||||
@Suppress("ConstPropertyName")
|
|
||||||
private const val serialVersionUID: Long = 1L
|
private const val serialVersionUID: Long = 1L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ import java.io.Serializable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class used to store link entries.
|
* Holds [Entries] link.
|
||||||
*/
|
*/
|
||||||
class EntryLink(
|
class EntryLink(
|
||||||
// Link's comments
|
// Link's comments
|
||||||
|
@ -92,6 +92,11 @@ class EntryLink(
|
||||||
this.tags.addAll(tags)
|
this.tags.addAll(tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// Serial version UID
|
||||||
|
private const val serialVersionUID: Long = 1L
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new comment
|
* Adds a new comment
|
||||||
*/
|
*/
|
||||||
|
@ -204,10 +209,4 @@ class EntryLink(
|
||||||
return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," +
|
return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," +
|
||||||
"nick='$nick', tags=$tags, title='$title'}")
|
"nick='$nick', tags=$tags, title='$title'}")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
// Serial version UID
|
|
||||||
@Suppress("ConstPropertyName")
|
|
||||||
private const val serialVersionUID: Long = 1L
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,9 @@ import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Module` abstract class.
|
* Represents an abstract module, which can be extended to implement specific functionality.
|
||||||
|
*
|
||||||
|
* This class provides a foundation for creating modules with configurable properties, commands, and help features.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractModule {
|
abstract class AbstractModule {
|
||||||
/**
|
/**
|
||||||
|
@ -76,6 +78,7 @@ abstract class AbstractModule {
|
||||||
/**
|
/**
|
||||||
* Responds with the module's help.
|
* Responds with the module's help.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("SameReturnValue")
|
||||||
open fun helpResponse(event: GenericMessageEvent): Boolean {
|
open fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||||
for (h in help) {
|
for (h in help) {
|
||||||
event.sendMessage(helpCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent))
|
event.sendMessage(helpCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent))
|
||||||
|
|
|
@ -40,29 +40,13 @@ import org.slf4j.LoggerFactory
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Calc module.
|
* Performs a calculation.
|
||||||
*/
|
*/
|
||||||
class Calc : AbstractModule() {
|
class Calc : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(Calc::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(Calc::class.java)
|
||||||
|
|
||||||
override val name = "Calc"
|
override val name = "Calc"
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
if (args.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
event.respond(calculate(args))
|
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
|
||||||
event.respond("No idea. This is the kind of math I don't get.")
|
|
||||||
} catch (e: UnknownFunctionOrVariableException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e)
|
|
||||||
event.respond("No idea. I must've some form of Dyscalculia.")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Calc command
|
// Calc command
|
||||||
private const val CALC_CMD = "calc"
|
private const val CALC_CMD = "calc"
|
||||||
|
@ -84,4 +68,20 @@ class Calc : AbstractModule() {
|
||||||
help.add("To solve a mathematical calculation:")
|
help.add("To solve a mathematical calculation:")
|
||||||
help.add(helpFormat("%c $CALC_CMD <calculation>"))
|
help.add(helpFormat("%c $CALC_CMD <calculation>"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
if (args.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
event.respond(calculate(args))
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
||||||
|
event.respond("No idea. This is the kind of math I don't get.")
|
||||||
|
} catch (e: UnknownFunctionOrVariableException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e)
|
||||||
|
event.respond("No idea. I must've some form of Dyscalculia.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,37 +39,14 @@ import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows user to interact with ChatGPT.
|
||||||
|
*/
|
||||||
class ChatGpt2 : AbstractModule() {
|
class ChatGpt2 : AbstractModule() {
|
||||||
val logger: Logger = LoggerFactory.getLogger(ChatGpt2::class.java)
|
val logger: Logger = LoggerFactory.getLogger(ChatGpt2::class.java)
|
||||||
|
|
||||||
override val name = CHATGPT_NAME
|
override val name = CHATGPT_NAME
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
if (args.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val answer = chat(
|
|
||||||
args.trim(), properties[API_KEY_PROP],
|
|
||||||
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()
|
|
||||||
)
|
|
||||||
if (answer.isNotBlank()) {
|
|
||||||
event.sendMessage(answer)
|
|
||||||
} else {
|
|
||||||
event.respond("$name is stumped.")
|
|
||||||
}
|
|
||||||
} catch (e: ModuleException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
|
||||||
e.message?.let {
|
|
||||||
event.respond(it)
|
|
||||||
}
|
|
||||||
} catch (e: NumberFormatException) {
|
|
||||||
if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e)
|
|
||||||
event.respond("The $name module is misconfigured.")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* The service name.
|
* The service name.
|
||||||
|
@ -125,4 +102,33 @@ class ChatGpt2 : AbstractModule() {
|
||||||
}
|
}
|
||||||
initProperties(API_KEY_PROP, MAX_TOKENS_PROP)
|
initProperties(API_KEY_PROP, MAX_TOKENS_PROP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets answers by chatting with ChatGPT.
|
||||||
|
*/
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
if (args.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
val answer = chat(
|
||||||
|
args.trim(), properties[API_KEY_PROP],
|
||||||
|
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()
|
||||||
|
)
|
||||||
|
if (answer.isNotBlank()) {
|
||||||
|
event.sendMessage(answer)
|
||||||
|
} else {
|
||||||
|
event.respond("$name is stumped.")
|
||||||
|
}
|
||||||
|
} catch (e: ModuleException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
|
e.message?.let {
|
||||||
|
event.respond(it)
|
||||||
|
}
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e)
|
||||||
|
event.respond("The $name module is misconfigured.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Cryptocurrency Prices module.
|
* Retrieves cryptocurrency market prices.
|
||||||
*/
|
*/
|
||||||
class CryptoPrices : AbstractModule() {
|
class CryptoPrices : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java)
|
||||||
|
@ -102,7 +102,7 @@ class CryptoPrices : AbstractModule() {
|
||||||
private const val CODES_KEYWORD = "codes"
|
private const val CODES_KEYWORD = "codes"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current market price.
|
* Get the current market price.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun currentPrice(args: List<String>): CryptoPrice {
|
fun currentPrice(args: List<String>): CryptoPrice {
|
||||||
|
|
|
@ -50,78 +50,13 @@ import java.util.*
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CurrencyConverter module.
|
* Converts between currencies.
|
||||||
*/
|
*/
|
||||||
class CurrencyConverter : AbstractModule() {
|
class CurrencyConverter : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java)
|
||||||
|
|
||||||
override val name = "CurrencyConverter"
|
override val name = "CurrencyConverter"
|
||||||
|
|
||||||
// Reload currency codes
|
|
||||||
private fun reload(apiKey: String?) {
|
|
||||||
if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) {
|
|
||||||
try {
|
|
||||||
loadSymbols(apiKey)
|
|
||||||
} catch (e: ModuleException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the specified currencies.
|
|
||||||
*/
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
reload(properties[API_KEY_PROP])
|
|
||||||
|
|
||||||
when {
|
|
||||||
SYMBOLS.isEmpty() -> {
|
|
||||||
event.respond(EMPTY_SYMBOLS_TABLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> {
|
|
||||||
val msg = convertCurrency(properties[API_KEY_PROP], args)
|
|
||||||
event.respond(msg.msg)
|
|
||||||
if (msg.isError) {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args.contains(CODES_KEYWORD) -> {
|
|
||||||
event.sendMessage("The supported currency codes are:")
|
|
||||||
event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun helpResponse(event: GenericMessageEvent): Boolean {
|
|
||||||
reload(properties[API_KEY_PROP])
|
|
||||||
|
|
||||||
if (SYMBOLS.isEmpty()) {
|
|
||||||
event.sendMessage(EMPTY_SYMBOLS_TABLE)
|
|
||||||
} else {
|
|
||||||
val nick = event.bot().nick
|
|
||||||
event.sendMessage("To convert from one currency to another:")
|
|
||||||
event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)))
|
|
||||||
event.sendMessage(
|
|
||||||
helpFormat(
|
|
||||||
helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to USD", nick, isPrivateMsgEnabled)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
event.sendMessage("To list the supported currency codes:")
|
|
||||||
event.sendMessage(
|
|
||||||
helpFormat(
|
|
||||||
helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* The API Key property.
|
* The API Key property.
|
||||||
|
@ -219,4 +154,69 @@ class CurrencyConverter : AbstractModule() {
|
||||||
initProperties(API_KEY_PROP)
|
initProperties(API_KEY_PROP)
|
||||||
loadSymbols(properties[ChatGpt2.API_KEY_PROP])
|
loadSymbols(properties[ChatGpt2.API_KEY_PROP])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload currency codes
|
||||||
|
private fun reload(apiKey: String?) {
|
||||||
|
if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) {
|
||||||
|
try {
|
||||||
|
loadSymbols(apiKey)
|
||||||
|
} catch (e: ModuleException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the specified currencies.
|
||||||
|
*/
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
reload(properties[API_KEY_PROP])
|
||||||
|
|
||||||
|
when {
|
||||||
|
SYMBOLS.isEmpty() -> {
|
||||||
|
event.respond(EMPTY_SYMBOLS_TABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> {
|
||||||
|
val msg = convertCurrency(properties[API_KEY_PROP], args)
|
||||||
|
event.respond(msg.msg)
|
||||||
|
if (msg.isError) {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args.contains(CODES_KEYWORD) -> {
|
||||||
|
event.sendMessage("The supported currency codes are:")
|
||||||
|
event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||||
|
reload(properties[API_KEY_PROP])
|
||||||
|
|
||||||
|
if (SYMBOLS.isEmpty()) {
|
||||||
|
event.sendMessage(EMPTY_SYMBOLS_TABLE)
|
||||||
|
} else {
|
||||||
|
val nick = event.bot().nick
|
||||||
|
event.sendMessage("To convert from one currency to another:")
|
||||||
|
event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)))
|
||||||
|
event.sendMessage(
|
||||||
|
helpFormat(
|
||||||
|
helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to USD", nick, isPrivateMsgEnabled)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
event.sendMessage("To list the supported currency codes:")
|
||||||
|
event.sendMessage(
|
||||||
|
helpFormat(
|
||||||
|
helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,22 +35,11 @@ import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Dice module.
|
* Rolls dice.
|
||||||
*/
|
*/
|
||||||
class Dice : AbstractModule() {
|
class Dice : AbstractModule() {
|
||||||
override val name = "Dice"
|
override val name = "Dice"
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
val arg = if (args.isBlank()) "2d6" else args.trim()
|
|
||||||
val match = Regex("^([1-9]|[12]\\d|3[0-2])[dD]([1-9]|[12]\\d|3[0-2])$").find(arg)
|
|
||||||
if (match != null) {
|
|
||||||
val (dice, sides) = match.destructured
|
|
||||||
event.respond("you rolled " + roll(dice.toInt(), sides.toInt()))
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Dice command
|
// Dice command
|
||||||
private const val DICE_CMD = "dice"
|
private const val DICE_CMD = "dice"
|
||||||
|
@ -84,4 +73,15 @@ class Dice : AbstractModule() {
|
||||||
help.add("To roll 2 dice with 6 sides:")
|
help.add("To roll 2 dice with 6 sides:")
|
||||||
help.add(helpFormat("%c $DICE_CMD [2d6]"))
|
help.add(helpFormat("%c $DICE_CMD [2d6]"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
val arg = if (args.isBlank()) "2d6" else args.trim()
|
||||||
|
val match = Regex("^([1-9]|[12]\\d|3[0-2])[dD]([1-9]|[12]\\d|3[0-2])$").find(arg)
|
||||||
|
if (match != null) {
|
||||||
|
val (dice, sides) = match.destructured
|
||||||
|
event.respond("you rolled " + roll(dice.toInt(), sides.toInt()))
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,47 +37,35 @@ import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows user to interact with Gemini.
|
||||||
|
*/
|
||||||
class Gemini2 : AbstractModule() {
|
class Gemini2 : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(Gemini2::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(Gemini2::class.java)
|
||||||
|
|
||||||
override val name = GEMINI_NAME
|
override val name = GEMINI_NAME
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
if (args.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val answer = chat(
|
|
||||||
args.trim(),
|
|
||||||
properties[GEMINI_API_KEY],
|
|
||||||
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()
|
|
||||||
)
|
|
||||||
if (!answer.isNullOrEmpty()) {
|
|
||||||
event.sendMessage(answer)
|
|
||||||
} else {
|
|
||||||
event.respond("$name is stumped.")
|
|
||||||
}
|
|
||||||
} catch (e: ModuleException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
|
||||||
e.message?.let {
|
|
||||||
event.respond(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* API Key error message
|
||||||
|
*/
|
||||||
|
const val API_KEY_ERROR = "Please specify an API key."
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The API key
|
||||||
|
*/
|
||||||
|
const val GEMINI_API_KEY = "gemini-api-key"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service name.
|
* The service name.
|
||||||
*/
|
*/
|
||||||
const val GEMINI_NAME = "Gemini"
|
const val GEMINI_NAME = "Gemini"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The API key
|
* IO error message
|
||||||
*/
|
*/
|
||||||
const val GEMINI_API_KEY = "gemini-api-key"
|
const val IO_ERROR = "An IO error has occurred while conversing with $GEMINI_NAME."
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The max number of output tokens property.
|
* The max number of output tokens property.
|
||||||
|
@ -104,14 +92,10 @@ class Gemini2 : AbstractModule() {
|
||||||
|
|
||||||
return gemini.generate(query)
|
return gemini.generate(query)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw ModuleException(
|
throw ModuleException("$GEMINI_CMD($query): IO", IO_ERROR, e)
|
||||||
"$GEMINI_CMD($query): IO",
|
|
||||||
"An IO error has occurred while conversing with ${GEMINI_NAME}.",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw ModuleException("${GEMINI_CMD}($query)", "No $GEMINI_NAME Project ID or Location specified.")
|
throw ModuleException("${GEMINI_CMD}($query)", API_KEY_ERROR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,4 +111,28 @@ class Gemini2 : AbstractModule() {
|
||||||
}
|
}
|
||||||
initProperties(GEMINI_API_KEY, MAX_TOKENS_PROP)
|
initProperties(GEMINI_API_KEY, MAX_TOKENS_PROP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
if (args.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
val answer = chat(
|
||||||
|
args.trim(),
|
||||||
|
properties[GEMINI_API_KEY],
|
||||||
|
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()
|
||||||
|
)
|
||||||
|
if (!answer.isNullOrEmpty()) {
|
||||||
|
event.sendMessage(answer)
|
||||||
|
} else {
|
||||||
|
event.respond("$name is stumped.")
|
||||||
|
}
|
||||||
|
} catch (e: ModuleException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
|
e.message?.let {
|
||||||
|
event.respond(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalize
|
||||||
import net.thauvin.erik.mobibot.Utils.colorize
|
import net.thauvin.erik.mobibot.Utils.colorize
|
||||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
@ -51,43 +51,13 @@ import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GoogleSearch module.
|
* Allows user to search Google.
|
||||||
*/
|
*/
|
||||||
class GoogleSearch : AbstractModule() {
|
class GoogleSearch : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java)
|
||||||
|
|
||||||
override val name = "GoogleSearch"
|
override val name = "GoogleSearch"
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches Google.
|
|
||||||
*/
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
if (args.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val results = searchGoogle(
|
|
||||||
args,
|
|
||||||
properties[API_KEY_PROP],
|
|
||||||
properties[CSE_KEY_PROP],
|
|
||||||
event.user.nick
|
|
||||||
)
|
|
||||||
for (msg in results) {
|
|
||||||
if (msg.isError) {
|
|
||||||
event.respond(msg.msg.colorize(msg.color))
|
|
||||||
} else {
|
|
||||||
event.sendMessage(channel, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: ModuleException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
|
||||||
e.message?.let {
|
|
||||||
event.respond(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Google API Key property
|
// Google API Key property
|
||||||
const val API_KEY_PROP = "google-api-key"
|
const val API_KEY_PROP = "google-api-key"
|
||||||
|
@ -112,7 +82,7 @@ class GoogleSearch : AbstractModule() {
|
||||||
if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) {
|
if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) {
|
||||||
throw ModuleException(
|
throw ModuleException(
|
||||||
"${GoogleSearch::class.java.name} is disabled.",
|
"${GoogleSearch::class.java.name} is disabled.",
|
||||||
"${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing."
|
"${GOOGLE_CMD.capitalize()} is disabled. The API keys are missing."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val results = mutableListOf<Message>()
|
val results = mutableListOf<Message>()
|
||||||
|
@ -159,4 +129,34 @@ class GoogleSearch : AbstractModule() {
|
||||||
help.add(helpFormat("%c $GOOGLE_CMD <query>"))
|
help.add(helpFormat("%c $GOOGLE_CMD <query>"))
|
||||||
initProperties(API_KEY_PROP, CSE_KEY_PROP)
|
initProperties(API_KEY_PROP, CSE_KEY_PROP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches Google.
|
||||||
|
*/
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
if (args.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
val results = searchGoogle(
|
||||||
|
args,
|
||||||
|
properties[API_KEY_PROP],
|
||||||
|
properties[CSE_KEY_PROP],
|
||||||
|
event.user.nick
|
||||||
|
)
|
||||||
|
for (msg in results) {
|
||||||
|
if (msg.isError) {
|
||||||
|
event.respond(msg.msg.colorize(msg.color))
|
||||||
|
} else {
|
||||||
|
event.sendMessage(channel, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: ModuleException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
|
e.message?.let {
|
||||||
|
event.respond(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,31 +47,13 @@ import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Joke module.
|
* Displays jokes from [JokeAPI](https://v2.jokeapi.dev/).
|
||||||
*/
|
*/
|
||||||
class Joke : AbstractModule() {
|
class Joke : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(Joke::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(Joke::class.java)
|
||||||
|
|
||||||
override val name = "Joke"
|
override val name = "Joke"
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/).
|
|
||||||
*/
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
with(event.bot()) {
|
|
||||||
try {
|
|
||||||
randomJoke().forEach {
|
|
||||||
sendIRC().notice(channel, it.msg.colorize(it.color))
|
|
||||||
}
|
|
||||||
} catch (e: ModuleException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
|
||||||
e.message?.let {
|
|
||||||
event.respond(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Joke command
|
// Joke command
|
||||||
private const val JOKE_CMD = "joke"
|
private const val JOKE_CMD = "joke"
|
||||||
|
@ -102,4 +84,22 @@ class Joke : AbstractModule() {
|
||||||
help.add("To display a random joke:")
|
help.add("To display a random joke:")
|
||||||
help.add(helpFormat("%c $JOKE_CMD"))
|
help.add(helpFormat("%c $JOKE_CMD"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/).
|
||||||
|
*/
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
with(event.bot()) {
|
||||||
|
try {
|
||||||
|
randomJoke().forEach {
|
||||||
|
sendIRC().notice(channel, it.msg.colorize(it.color))
|
||||||
|
}
|
||||||
|
} catch (e: ModuleException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
|
e.message?.let {
|
||||||
|
event.respond(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,57 +42,13 @@ import java.net.InetAddress
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Lookup module.
|
* Performs a DNS lookup or Whois IP query.
|
||||||
*/
|
*/
|
||||||
class Lookup : AbstractModule() {
|
class Lookup : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java)
|
||||||
|
|
||||||
override val name = "Lookup"
|
override val name = "Lookup"
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
if (args.matches("(\\S.)+(\\S)+".toRegex())) {
|
|
||||||
try {
|
|
||||||
event.respondWith(nslookup(args).prependIndent())
|
|
||||||
} catch (ignore: UnknownHostException) {
|
|
||||||
if (args.matches(
|
|
||||||
("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")
|
|
||||||
.toRegex()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
val lines = whois(args)
|
|
||||||
if (lines.isNotEmpty()) {
|
|
||||||
var line: String
|
|
||||||
var hasData = false
|
|
||||||
for (rawLine in lines) {
|
|
||||||
line = rawLine.trim()
|
|
||||||
if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) {
|
|
||||||
if (!hasData) {
|
|
||||||
event.respondWith(line)
|
|
||||||
hasData = true
|
|
||||||
} else {
|
|
||||||
event.bot().sendIRC().notice(event.user.nick, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
event.respond("Unknown host.")
|
|
||||||
}
|
|
||||||
} catch (ioe: IOException) {
|
|
||||||
if (logger.isWarnEnabled) {
|
|
||||||
logger.warn("Unable to perform whois IP lookup: $args", ioe)
|
|
||||||
}
|
|
||||||
event.respond("Unable to perform whois IP lookup: ${ioe.message}")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
event.respond("Unknown host.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* The whois default host.
|
* The whois default host.
|
||||||
|
@ -168,4 +124,48 @@ class Lookup : AbstractModule() {
|
||||||
help.add("To perform a DNS lookup query:")
|
help.add("To perform a DNS lookup query:")
|
||||||
help.add(helpFormat("%c $LOOKUP_CMD <ip address or hostname>"))
|
help.add(helpFormat("%c $LOOKUP_CMD <ip address or hostname>"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
if (args.matches("(\\S.)+(\\S)+".toRegex())) {
|
||||||
|
try {
|
||||||
|
event.respondWith(nslookup(args).prependIndent())
|
||||||
|
} catch (_: UnknownHostException) {
|
||||||
|
if (args.matches(
|
||||||
|
("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")
|
||||||
|
.toRegex()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
val lines = whois(args)
|
||||||
|
if (lines.isNotEmpty()) {
|
||||||
|
var line: String
|
||||||
|
var hasData = false
|
||||||
|
for (rawLine in lines) {
|
||||||
|
line = rawLine.trim()
|
||||||
|
if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) {
|
||||||
|
if (!hasData) {
|
||||||
|
event.respondWith(line)
|
||||||
|
hasData = true
|
||||||
|
} else {
|
||||||
|
event.bot().sendIRC().notice(event.user.nick, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.respond("Unknown host.")
|
||||||
|
}
|
||||||
|
} catch (ioe: IOException) {
|
||||||
|
if (logger.isWarnEnabled) {
|
||||||
|
logger.warn("Unable to perform whois IP lookup: $args", ioe)
|
||||||
|
}
|
||||||
|
event.respond("Unable to perform whois IP lookup: ${ioe.message}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.respond("Unknown host.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,9 @@ import java.net.http.HttpClient
|
||||||
import java.net.http.HttpRequest
|
import java.net.http.HttpRequest
|
||||||
import java.net.http.HttpResponse
|
import java.net.http.HttpResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows users to post on Mastodon.
|
||||||
|
*/
|
||||||
class Mastodon : SocialModule() {
|
class Mastodon : SocialModule() {
|
||||||
override val name = "Mastodon"
|
override val name = "Mastodon"
|
||||||
|
|
||||||
|
@ -56,32 +59,6 @@ class Mastodon : SocialModule() {
|
||||||
override val isValidProperties: Boolean
|
override val isValidProperties: Boolean
|
||||||
get() = !(properties[INSTANCE_PROP].isNullOrBlank() || properties[ACCESS_TOKEN_PROP].isNullOrBlank())
|
get() = !(properties[INSTANCE_PROP].isNullOrBlank() || properties[ACCESS_TOKEN_PROP].isNullOrBlank())
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the entry for posting.
|
|
||||||
*/
|
|
||||||
override fun formatEntry(entry: EntryLink): String {
|
|
||||||
return "${entry.title} (via ${entry.nick} on ${entry.channel})${formatTags(entry)}\n\n${entry.link}"
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun formatTags(entry: EntryLink): String {
|
|
||||||
return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) }
|
|
||||||
.joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Posts on Mastodon.
|
|
||||||
*/
|
|
||||||
@Throws(ModuleException::class)
|
|
||||||
override fun post(message: String, isDm: Boolean): String {
|
|
||||||
return toot(
|
|
||||||
apiKey = properties[ACCESS_TOKEN_PROP],
|
|
||||||
instance = properties[INSTANCE_PROP],
|
|
||||||
handle = handle,
|
|
||||||
message = message,
|
|
||||||
isDm = isDm
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Property keys
|
// Property keys
|
||||||
const val ACCESS_TOKEN_PROP = "mastodon-access-token"
|
const val ACCESS_TOKEN_PROP = "mastodon-access-token"
|
||||||
|
@ -146,4 +123,30 @@ class Mastodon : SocialModule() {
|
||||||
properties[AUTO_POST_PROP] = "false"
|
properties[AUTO_POST_PROP] = "false"
|
||||||
initProperties(ACCESS_TOKEN_PROP, HANDLE_PROP, INSTANCE_PROP)
|
initProperties(ACCESS_TOKEN_PROP, HANDLE_PROP, INSTANCE_PROP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the entry for posting.
|
||||||
|
*/
|
||||||
|
override fun formatEntry(entry: EntryLink): String {
|
||||||
|
return "${entry.title} (via ${entry.nick} on ${entry.channel})${formatTags(entry)}\n\n${entry.link}"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatTags(entry: EntryLink): String {
|
||||||
|
return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) }
|
||||||
|
.joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posts on Mastodon.
|
||||||
|
*/
|
||||||
|
@Throws(ModuleException::class)
|
||||||
|
override fun post(message: String, isDm: Boolean): String {
|
||||||
|
return toot(
|
||||||
|
apiKey = properties[ACCESS_TOKEN_PROP],
|
||||||
|
instance = properties[INSTANCE_PROP],
|
||||||
|
handle = handle,
|
||||||
|
message = message,
|
||||||
|
isDm = isDm
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,16 +30,12 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
/**
|
|
||||||
* The `ModuleException` class.
|
|
||||||
*/
|
|
||||||
class ModuleException @JvmOverloads constructor(
|
class ModuleException @JvmOverloads constructor(
|
||||||
val debugMessage: String,
|
val debugMessage: String,
|
||||||
message: String? = null,
|
message: String? = null,
|
||||||
cause: Throwable? = null
|
cause: Throwable? = null
|
||||||
) : Exception(message, cause) {
|
) : Exception(message, cause) {
|
||||||
companion object {
|
companion object {
|
||||||
@Suppress("ConstPropertyName")
|
|
||||||
private const val serialVersionUID = 1L
|
private const val serialVersionUID = 1L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,15 +35,11 @@ import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Ping module.
|
* Responds with a random quirky response.
|
||||||
*/
|
*/
|
||||||
class Ping : AbstractModule() {
|
class Ping : AbstractModule() {
|
||||||
override val name = "Ping"
|
override val name = "Ping"
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
event.bot().sendIRC().action(channel, randomPing())
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* The ping responses.
|
* The ping responses.
|
||||||
|
@ -80,4 +76,8 @@ class Ping : AbstractModule() {
|
||||||
help.add("To ping the bot:")
|
help.add("To ping the bot:")
|
||||||
help.add(helpFormat("%c $PING_CMD"))
|
help.add(helpFormat("%c $PING_CMD"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
event.bot().sendIRC().action(channel, randomPing())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,25 @@ import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple module example in Kotlin.
|
* Allows users to play Rock Paper Scissors.
|
||||||
*/
|
*/
|
||||||
class RockPaperScissors : AbstractModule() {
|
class RockPaperScissors : AbstractModule() {
|
||||||
override val name = "RockPaperScissors"
|
override val name = "RockPaperScissors"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// For testing.
|
||||||
|
fun winLoseOrDraw(player: String, bot: String): String {
|
||||||
|
val hand = Hands.valueOf(player.uppercase())
|
||||||
|
val botHand = Hands.valueOf(bot.uppercase())
|
||||||
|
|
||||||
|
return when {
|
||||||
|
hand == botHand -> "draw"
|
||||||
|
hand.beats(botHand) -> "win"
|
||||||
|
else -> "lose"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
with(commands) {
|
with(commands) {
|
||||||
add(Hands.ROCK.name.lowercase())
|
add(Hands.ROCK.name.lowercase())
|
||||||
|
@ -80,20 +94,6 @@ class RockPaperScissors : AbstractModule() {
|
||||||
abstract fun beats(hand: Hands): Boolean
|
abstract fun beats(hand: Hands): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
// For testing.
|
|
||||||
fun winLoseOrDraw(player: String, bot: String): String {
|
|
||||||
val hand = Hands.valueOf(player.uppercase())
|
|
||||||
val botHand = Hands.valueOf(bot.uppercase())
|
|
||||||
|
|
||||||
return when {
|
|
||||||
hand == botHand -> "draw"
|
|
||||||
hand.beats(botHand) -> "win"
|
|
||||||
else -> "lose"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
val hand = Hands.valueOf(cmd.uppercase())
|
val hand = Hands.valueOf(cmd.uppercase())
|
||||||
val botHand = Hands.entries[(0..Hands.entries.size).random()]
|
val botHand = Hands.entries[(0..Hands.entries.size).random()]
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalize
|
||||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.Utils.reader
|
import net.thauvin.erik.mobibot.Utils.reader
|
||||||
|
@ -49,34 +49,13 @@ import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The StockQuote module.
|
* Retrieves stock quotes from Alpha Vantage.
|
||||||
*/
|
*/
|
||||||
class StockQuote : AbstractModule() {
|
class StockQuote : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java)
|
||||||
|
|
||||||
override val name = "StockQuote"
|
override val name = "StockQuote"
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the specified stock quote from Alpha Vantage.
|
|
||||||
*/
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
if (args.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val messages = getQuote(args, properties[API_KEY_PROP])
|
|
||||||
for (msg in messages) {
|
|
||||||
event.sendMessage(channel, msg)
|
|
||||||
}
|
|
||||||
} catch (e: ModuleException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
|
||||||
e.message?.let {
|
|
||||||
event.respond(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* The API property key.
|
* The API property key.
|
||||||
|
@ -133,7 +112,7 @@ class StockQuote : AbstractModule() {
|
||||||
if (apiKey.isNullOrBlank()) {
|
if (apiKey.isNullOrBlank()) {
|
||||||
throw ModuleException(
|
throw ModuleException(
|
||||||
"${StockQuote::class.java.name} is disabled.",
|
"${StockQuote::class.java.name} is disabled.",
|
||||||
"${STOCK_CMD.capitalise()} is disabled. The API key is missing."
|
"${STOCK_CMD.capitalize()} is disabled. The API key is missing."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val messages = mutableListOf<Message>()
|
val messages = mutableListOf<Message>()
|
||||||
|
@ -233,4 +212,25 @@ class StockQuote : AbstractModule() {
|
||||||
help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>"))
|
help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>"))
|
||||||
initProperties(API_KEY_PROP)
|
initProperties(API_KEY_PROP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the specified stock quote from Alpha Vantage.
|
||||||
|
*/
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
if (args.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
val messages = getQuote(args, properties[API_KEY_PROP])
|
||||||
|
for (msg in messages) {
|
||||||
|
event.sendMessage(channel, msg)
|
||||||
|
}
|
||||||
|
} catch (e: ModuleException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
|
e.message?.let {
|
||||||
|
event.respond(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,14 +36,31 @@ import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The War module.
|
* Plays the `war` card game.
|
||||||
*
|
|
||||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
*/
|
||||||
class War : AbstractModule() {
|
class War : AbstractModule() {
|
||||||
override val name = "War"
|
override val name = "War"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val CLUBS = arrayOf("🃑", "🃞", "🃝", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒")
|
||||||
|
private val DIAMONDS = arrayOf("🃁", "🃎", "🃍", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂")
|
||||||
|
private val HEARTS = arrayOf("🂱", "🂾", "🂽", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲")
|
||||||
|
|
||||||
|
// Random
|
||||||
|
private val RANDOM = SecureRandom()
|
||||||
|
private val SPADES = arrayOf("🂡", "🂮", "🂭", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢")
|
||||||
|
private val DECK = arrayOf(HEARTS, SPADES, DIAMONDS, CLUBS)
|
||||||
|
|
||||||
|
// War command
|
||||||
|
private const val WAR_CMD = "war"
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
commands.add(WAR_CMD)
|
||||||
|
help.add("To play war:")
|
||||||
|
help.add(helpFormat("%c $WAR_CMD"))
|
||||||
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(
|
||||||
channel: String, cmd: String, args: String,
|
channel: String, cmd: String, args: String,
|
||||||
event: GenericMessageEvent
|
event: GenericMessageEvent
|
||||||
|
@ -66,24 +83,4 @@ class War : AbstractModule() {
|
||||||
)
|
)
|
||||||
} while (i == y)
|
} while (i == y)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val CLUBS = arrayOf("🃑", "🃞", "🃝", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒")
|
|
||||||
private val DIAMONDS = arrayOf("🃁", "🃎", "🃍", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂")
|
|
||||||
private val HEARTS = arrayOf("🂱", "🂾", "🂽", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲")
|
|
||||||
|
|
||||||
// Random
|
|
||||||
private val RANDOM = SecureRandom()
|
|
||||||
private val SPADES = arrayOf("🂡", "🂮", "🂭", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢")
|
|
||||||
private val DECK = arrayOf(HEARTS, SPADES, DIAMONDS, CLUBS)
|
|
||||||
|
|
||||||
// War command
|
|
||||||
private const val WAR_CMD = "war"
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
commands.add(WAR_CMD)
|
|
||||||
help.add("To play war:")
|
|
||||||
help.add(helpFormat("%c $WAR_CMD"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ import net.aksingh.owmjapis.core.OWM
|
||||||
import net.aksingh.owmjapis.core.OWM.Country
|
import net.aksingh.owmjapis.core.OWM.Country
|
||||||
import net.aksingh.owmjapis.model.CurrentWeather
|
import net.aksingh.owmjapis.model.CurrentWeather
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalize
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalizeWords
|
import net.thauvin.erik.mobibot.Utils.capitalizeWords
|
||||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
@ -51,38 +51,13 @@ import org.slf4j.LoggerFactory
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Weather2` module.
|
* Retrieve weather information from OpenWeatherMap.
|
||||||
*/
|
*/
|
||||||
class Weather2 : AbstractModule() {
|
class Weather2 : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java)
|
||||||
|
|
||||||
override val name = "Weather"
|
override val name = "Weather"
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the weather data from a specific city.
|
|
||||||
*/
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
if (args.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val messages = getWeather(args, properties[API_KEY_PROP])
|
|
||||||
if (messages[0].isError) {
|
|
||||||
helpResponse(event)
|
|
||||||
} else {
|
|
||||||
for (msg in messages) {
|
|
||||||
event.sendMessage(channel, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: ModuleException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
|
||||||
e.message?.let {
|
|
||||||
event.respond(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* The OpenWeatherMap API Key property.
|
* The OpenWeatherMap API Key property.
|
||||||
|
@ -121,7 +96,7 @@ class Weather2 : AbstractModule() {
|
||||||
if (apiKey.isNullOrBlank()) {
|
if (apiKey.isNullOrBlank()) {
|
||||||
throw ModuleException(
|
throw ModuleException(
|
||||||
"${Weather2::class.java.name} is disabled.",
|
"${Weather2::class.java.name} is disabled.",
|
||||||
"${WEATHER_CMD.capitalise()} is disabled. The API key is missing."
|
"${WEATHER_CMD.capitalize()} is disabled. The API key is missing."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val owm = OWM(apiKey)
|
val owm = OWM(apiKey)
|
||||||
|
@ -181,7 +156,7 @@ class Weather2 : AbstractModule() {
|
||||||
for (w in it) {
|
for (w in it) {
|
||||||
w?.let {
|
w?.let {
|
||||||
condition.append(' ')
|
condition.append(' ')
|
||||||
.append(w.getDescription().capitalise())
|
.append(w.getDescription().capitalize())
|
||||||
.append('.')
|
.append('.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,4 +222,29 @@ class Weather2 : AbstractModule() {
|
||||||
}
|
}
|
||||||
initProperties(API_KEY_PROP)
|
initProperties(API_KEY_PROP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the weather data from a specific location.
|
||||||
|
*/
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
if (args.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
val messages = getWeather(args, properties[API_KEY_PROP])
|
||||||
|
if (messages[0].isError) {
|
||||||
|
helpResponse(event)
|
||||||
|
} else {
|
||||||
|
for (msg in messages) {
|
||||||
|
event.sendMessage(channel, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: ModuleException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
|
e.message?.let {
|
||||||
|
event.respond(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,40 +47,6 @@ class WolframAlpha : AbstractModule() {
|
||||||
|
|
||||||
override val name = "WolframAlpha"
|
override val name = "WolframAlpha"
|
||||||
|
|
||||||
private fun getUnits(unit: String?): String {
|
|
||||||
return if (unit?.lowercase() == METRIC) {
|
|
||||||
METRIC
|
|
||||||
} else {
|
|
||||||
IMPERIAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
|
||||||
if (args.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val query = args.trim().split("units=", limit = 2, ignoreCase = true)
|
|
||||||
event.sendMessage(
|
|
||||||
queryWolfram(
|
|
||||||
query[0].trim(),
|
|
||||||
units = if (query.size == 2) {
|
|
||||||
getUnits(query[1].trim())
|
|
||||||
} else {
|
|
||||||
getUnits(properties[UNITS_PROP])
|
|
||||||
},
|
|
||||||
appId = properties[APPID_KEY_PROP]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} catch (e: ModuleException) {
|
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
|
||||||
e.message?.let {
|
|
||||||
event.respond(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpResponse(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* The Wolfram Alpha API Key property.
|
* The Wolfram Alpha API Key property.
|
||||||
|
@ -139,4 +105,41 @@ class WolframAlpha : AbstractModule() {
|
||||||
}
|
}
|
||||||
initProperties(APPID_KEY_PROP, UNITS_PROP)
|
initProperties(APPID_KEY_PROP, UNITS_PROP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getUnits(unit: String?): String {
|
||||||
|
return if (unit?.lowercase() == METRIC) {
|
||||||
|
METRIC
|
||||||
|
} else {
|
||||||
|
IMPERIAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries Wolfram Alpha.
|
||||||
|
*/
|
||||||
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
|
if (args.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
val query = args.trim().split("units=", limit = 2, ignoreCase = true)
|
||||||
|
event.sendMessage(
|
||||||
|
queryWolfram(
|
||||||
|
query[0].trim(),
|
||||||
|
units = if (query.size == 2) {
|
||||||
|
getUnits(query[1].trim())
|
||||||
|
} else {
|
||||||
|
getUnits(properties[UNITS_PROP])
|
||||||
|
},
|
||||||
|
appId = properties[APPID_KEY_PROP]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (e: ModuleException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
|
e.message?.let {
|
||||||
|
event.respond(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
helpResponse(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,6 +367,16 @@ class WorldTime : AbstractModule() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
with(help) {
|
||||||
|
add("To display a country's current date/time:")
|
||||||
|
add(helpFormat("%c $TIME_CMD [<country code or zone>]"))
|
||||||
|
add("For a listing of the supported countries/zones:")
|
||||||
|
add(helpFormat("%c $TIME_CMD $ZONES_ARGS"))
|
||||||
|
}
|
||||||
|
commands.add(TIME_CMD)
|
||||||
|
}
|
||||||
|
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
if (args.equals(ZONES_ARGS, true)) {
|
if (args.equals(ZONES_ARGS, true)) {
|
||||||
event.sendMessage("The supported countries/zones are: ")
|
event.sendMessage("The supported countries/zones are: ")
|
||||||
|
@ -377,14 +387,4 @@ class WorldTime : AbstractModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override val isPrivateMsgEnabled = true
|
override val isPrivateMsgEnabled = true
|
||||||
|
|
||||||
init {
|
|
||||||
with(help) {
|
|
||||||
add("To display a country's current date/time:")
|
|
||||||
add(helpFormat("%c $TIME_CMD [<country code or zone>]"))
|
|
||||||
add("For a listing of the supported countries/zones:")
|
|
||||||
add(helpFormat("%c $TIME_CMD $ZONES_ARGS"))
|
|
||||||
}
|
|
||||||
commands.add(TIME_CMD)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
package net.thauvin.erik.mobibot.msg
|
package net.thauvin.erik.mobibot.msg
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `ErrorMessage` class.
|
* Holds an error message.
|
||||||
*/
|
*/
|
||||||
class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
||||||
Message(msg, color, isError = true)
|
Message(msg, color, isError = true)
|
||||||
|
|
|
@ -30,8 +30,9 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.msg
|
package net.thauvin.erik.mobibot.msg
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Message` class.
|
* Holds a message.
|
||||||
*/
|
*/
|
||||||
open class Message @JvmOverloads constructor(
|
open class Message @JvmOverloads constructor(
|
||||||
var msg: String,
|
var msg: String,
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
package net.thauvin.erik.mobibot.msg
|
package net.thauvin.erik.mobibot.msg
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `NoticeMessage` class.
|
* Holds a notice message.
|
||||||
*/
|
*/
|
||||||
class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
||||||
Message(msg, color, isNotice = true)
|
Message(msg, color, isNotice = true)
|
||||||
|
|
|
@ -30,8 +30,9 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.msg
|
package net.thauvin.erik.mobibot.msg
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `PrivateMessage` class.
|
* Holds a private message.
|
||||||
*/
|
*/
|
||||||
class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) :
|
||||||
Message(msg, color, isPrivate = true)
|
Message(msg, color, isPrivate = true)
|
||||||
|
|
|
@ -31,6 +31,6 @@
|
||||||
package net.thauvin.erik.mobibot.msg
|
package net.thauvin.erik.mobibot.msg
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `PublicMessage` class.
|
* Holds a public message.
|
||||||
*/
|
*/
|
||||||
class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color)
|
class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color)
|
||||||
|
|
|
@ -39,7 +39,7 @@ import org.slf4j.LoggerFactory
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Social Manager.
|
* Manages social media modules and handles operations such as notifications, posting, and queuing entries.
|
||||||
*/
|
*/
|
||||||
class SocialManager {
|
class SocialManager {
|
||||||
private val entries: MutableSet<Int> = HashSet()
|
private val entries: MutableSet<Int> = HashSet()
|
||||||
|
|
|
@ -40,6 +40,10 @@ import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the ability to handle notifications, post entries and manage interaction with a specific social media
|
||||||
|
* service.
|
||||||
|
*/
|
||||||
abstract class SocialModule : AbstractModule() {
|
abstract class SocialModule : AbstractModule() {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java)
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ package net.thauvin.erik.mobibot.social
|
||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer used to post social entries.
|
||||||
|
*/
|
||||||
class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() {
|
class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
socialManager.postEntry(index)
|
socialManager.postEntry(index)
|
||||||
|
|
|
@ -53,7 +53,7 @@ class AddonsTest {
|
||||||
private val addons = Addons(p)
|
private val addons = Addons(p)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addTest() {
|
fun `Add modules and commands`() {
|
||||||
// Modules
|
// Modules
|
||||||
addons.add(Joke())
|
addons.add(Joke())
|
||||||
addons.add(RockPaperScissors())
|
addons.add(RockPaperScissors())
|
||||||
|
|
|
@ -34,9 +34,6 @@ import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables tests on CI annotation.
|
* Disables tests on CI annotation.
|
||||||
*
|
|
||||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
*/
|
||||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
|
|
@ -36,9 +36,6 @@ import org.junit.jupiter.api.extension.ExtensionContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables tests on CI condition.
|
* Disables tests on CI condition.
|
||||||
*
|
|
||||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
*/
|
||||||
class DisableOnCiCondition : ExecutionCondition {
|
class DisableOnCiCondition : ExecutionCondition {
|
||||||
override fun evaluateExecutionCondition(context: ExtensionContext): ConditionEvaluationResult {
|
override fun evaluateExecutionCondition(context: ExtensionContext): ConditionEvaluationResult {
|
||||||
|
|
|
@ -37,39 +37,68 @@ import assertk.assertions.*
|
||||||
import com.rometools.rome.io.FeedException
|
import com.rometools.rome.io.FeedException
|
||||||
import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed
|
import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed
|
||||||
import net.thauvin.erik.mobibot.msg.Message
|
import net.thauvin.erik.mobibot.msg.Message
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class FeedReaderTest {
|
class FeedReaderTest {
|
||||||
@Test
|
@Nested
|
||||||
fun readFeedTest() {
|
@DisplayName("Failure Tests")
|
||||||
var messages = readFeed("https://feeds.thauvin.net/ethauvin")
|
inner class FailureTests {
|
||||||
assertThat(messages, "messages").all {
|
@Test
|
||||||
size().isEqualTo(10)
|
fun invalidFeed() {
|
||||||
index(1).prop(Message::msg).contains("erik.thauvin.net")
|
assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0")
|
@Test
|
||||||
assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing")
|
fun invalidHost() {
|
||||||
|
assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42)
|
@Test
|
||||||
assertThat(messages, "messages").size().isEqualTo(84)
|
fun invalidLocation() {
|
||||||
messages.forEachIndexed { i, m ->
|
assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java)
|
||||||
if (i % 2 == 0) {
|
}
|
||||||
assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum")
|
|
||||||
} else {
|
@Test
|
||||||
assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/")
|
fun invalidUrl() {
|
||||||
|
assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Read Feed Tests")
|
||||||
|
inner class ReadFeedTests {
|
||||||
|
@Test
|
||||||
|
fun readEmptyFeed() {
|
||||||
|
val messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0")
|
||||||
|
assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun readFeed() {
|
||||||
|
val messages = readFeed("https://feeds.thauvin.net/ethauvin")
|
||||||
|
assertThat(messages, "messages").all {
|
||||||
|
size().isEqualTo(10)
|
||||||
|
index(1).prop(Message::msg).contains("erik.thauvin.net")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java)
|
@Test
|
||||||
|
fun readThenValidateFeedContent() {
|
||||||
assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java)
|
val messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42)
|
||||||
|
assertThat(messages, "messages").size().isEqualTo(84)
|
||||||
assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java)
|
messages.forEachIndexed { i, m ->
|
||||||
|
if (i % 2 == 0) {
|
||||||
assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java)
|
assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum")
|
||||||
|
} else {
|
||||||
|
@Suppress("HttpUrlsUsage")
|
||||||
|
assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.nio.file.Paths
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access to `local.properties`.
|
* Provides functions to access local properties and environment variables.
|
||||||
*/
|
*/
|
||||||
open class LocalProperties {
|
open class LocalProperties {
|
||||||
init {
|
init {
|
||||||
|
@ -72,7 +72,7 @@ open class LocalProperties {
|
||||||
env?.let {
|
env?.let {
|
||||||
localProps.setProperty(key, env)
|
localProps.setProperty(key, env)
|
||||||
}
|
}
|
||||||
env
|
throw IOException("The $key property not found in local.properties or environment variables.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,30 +40,17 @@ import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class PinboardTest : LocalProperties() {
|
class PinboardTest : LocalProperties() {
|
||||||
private val pinboard = Pinboard()
|
private val apiToken = getProperty("pinboard-api-token")
|
||||||
|
|
||||||
@Test
|
private val ircServer = "irc.test.com"
|
||||||
fun testPinboard() {
|
private val pinboard = Pinboard().apply { setApiToken(apiToken) }
|
||||||
val apiToken = getProperty("pinboard-api-token")
|
|
||||||
val url = "https://www.example.com/${(1000..5000).random()}"
|
|
||||||
val ircServer = "irc.test.com"
|
|
||||||
val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test"))
|
|
||||||
|
|
||||||
pinboard.setApiToken(apiToken)
|
private fun newEntry(): EntryLink {
|
||||||
|
return EntryLink(randomUrl(), "Test Example", "ErikT", "", "#mobitopia", listOf("test"))
|
||||||
|
}
|
||||||
|
|
||||||
pinboard.addPin(ircServer, entry)
|
private fun randomUrl(): String {
|
||||||
assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin")
|
return "https://www.example.com/${(5001..9999).random()}"
|
||||||
|
|
||||||
entry.link = "https://www.example.com/${(5001..9999).random()}"
|
|
||||||
pinboard.updatePin(ircServer, url, entry)
|
|
||||||
assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin")
|
|
||||||
|
|
||||||
entry.title = "Foo Title"
|
|
||||||
pinboard.updatePin(ircServer, entry.link, entry)
|
|
||||||
assertTrue(validatePin(apiToken, url = entry.link, entry.title), "updatePin(${entry.title}")
|
|
||||||
|
|
||||||
pinboard.deletePin(entry)
|
|
||||||
assertFalse(validatePin(apiToken, url = entry.link), "deletePin")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean {
|
private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean {
|
||||||
|
@ -78,4 +65,43 @@ class PinboardTest : LocalProperties() {
|
||||||
|
|
||||||
return response.contains(url)
|
return response.contains(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addPin() {
|
||||||
|
val entry = newEntry()
|
||||||
|
pinboard.addPin(ircServer, entry)
|
||||||
|
assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin")
|
||||||
|
|
||||||
|
pinboard.deletePin(entry)
|
||||||
|
assertFalse(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "deletePin")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updatePin() {
|
||||||
|
val entry = newEntry()
|
||||||
|
pinboard.addPin(ircServer, entry)
|
||||||
|
assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin")
|
||||||
|
|
||||||
|
val url = entry.link
|
||||||
|
entry.link = randomUrl()
|
||||||
|
pinboard.updatePin(ircServer, url, entry)
|
||||||
|
assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin")
|
||||||
|
|
||||||
|
pinboard.deletePin(entry)
|
||||||
|
assertFalse(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "deletePin")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updatePinTitle() {
|
||||||
|
val entry = newEntry()
|
||||||
|
pinboard.addPin(ircServer, entry)
|
||||||
|
assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin")
|
||||||
|
|
||||||
|
pinboard.updatePin(ircServer, entry.link, entry)
|
||||||
|
assertTrue(validatePin(apiToken, url = entry.link, entry.title), "updatePin")
|
||||||
|
|
||||||
|
pinboard.deletePin(entry)
|
||||||
|
assertFalse(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "deletePin")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ import assertk.assertions.isEqualTo
|
||||||
import assertk.assertions.length
|
import assertk.assertions.length
|
||||||
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalize
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalizeWords
|
import net.thauvin.erik.mobibot.Utils.capitalizeWords
|
||||||
import net.thauvin.erik.mobibot.Utils.colorize
|
import net.thauvin.erik.mobibot.Utils.colorize
|
||||||
import net.thauvin.erik.mobibot.Utils.cyan
|
import net.thauvin.erik.mobibot.Utils.cyan
|
||||||
|
@ -60,6 +60,8 @@ import net.thauvin.erik.mobibot.Utils.underline
|
||||||
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
||||||
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import org.pircbotx.Colors
|
import org.pircbotx.Colors
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -71,205 +73,356 @@ import kotlin.test.Test
|
||||||
class UtilsTest {
|
class UtilsTest {
|
||||||
private val ascii =
|
private val ascii =
|
||||||
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
||||||
private val cal = Calendar.getInstance()
|
private val p = Properties().apply {
|
||||||
private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0)
|
setProperty("one", "1")
|
||||||
|
setProperty("two", "two")
|
||||||
|
}
|
||||||
private val test = "This is a test."
|
private val test = "This is a test."
|
||||||
|
|
||||||
@BeforeEach
|
@Nested
|
||||||
fun setUp() {
|
@DisplayName("Date Tests")
|
||||||
cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0
|
inner class DateTests {
|
||||||
}
|
private val cal = Calendar.getInstance()
|
||||||
|
private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0)
|
||||||
|
|
||||||
@Test
|
@BeforeEach
|
||||||
fun testAppendIfMissing() {
|
fun beforeEach() {
|
||||||
val dir = "dir"
|
cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0
|
||||||
val sep = '/'
|
|
||||||
val url = "https://erik.thauvin.net"
|
|
||||||
assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)")
|
|
||||||
.isEqualTo(dir + File.separatorChar)
|
|
||||||
assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep")
|
|
||||||
assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testBold() {
|
|
||||||
assertThat(1.bold(), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD)
|
|
||||||
assertThat(2L.bold(), "bold(2L)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD)
|
|
||||||
assertThat(ascii.bold(), "ascii.bold()").isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
|
||||||
assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCapitalise() {
|
|
||||||
assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test")
|
|
||||||
assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test")
|
|
||||||
assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test)
|
|
||||||
assertThat("".capitalise(), "capitalize()").isEqualTo("")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun textCapitaliseWords() {
|
|
||||||
assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.")
|
|
||||||
assertThat("Already Capitalized".capitalizeWords(), "already capitalized")
|
|
||||||
.isEqualTo("Already Capitalized")
|
|
||||||
assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testColorize() {
|
|
||||||
assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo(
|
|
||||||
Colors.REVERSE + ascii + Colors.REVERSE
|
|
||||||
)
|
|
||||||
assertThat(ascii.colorize(Colors.RED), "red.colorize()")
|
|
||||||
.isEqualTo(Colors.RED + ascii + Colors.NORMAL)
|
|
||||||
assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)")
|
|
||||||
.isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
|
||||||
assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("")
|
|
||||||
assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("")
|
|
||||||
assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii)
|
|
||||||
assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()")
|
|
||||||
.isEqualTo(Colors.NORMAL + " " + Colors.NORMAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCyan() {
|
|
||||||
assertThat(ascii.cyan()).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testEncodeUrl() {
|
|
||||||
assertThat("Hello Günter".encodeUrl()).isEqualTo("Hello%20G%C3%BCnter")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGetIntProperty() {
|
|
||||||
val p = Properties()
|
|
||||||
p["one"] = "1"
|
|
||||||
p["two"] = "two"
|
|
||||||
assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1)
|
|
||||||
assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2)
|
|
||||||
assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGreen() {
|
|
||||||
assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testHelpCmdSyntax() {
|
|
||||||
val bot = "mobibot"
|
|
||||||
assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)")
|
|
||||||
.isEqualTo("$bot: $test $bot $test")
|
|
||||||
assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)")
|
|
||||||
.isEqualTo("/msg $bot $bot $test /msg $bot $test $bot")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testHelpFormat() {
|
|
||||||
assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)")
|
|
||||||
.isEqualTo("${Colors.BOLD}$test${Colors.BOLD}")
|
|
||||||
assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)")
|
|
||||||
.isEqualTo(test.prependIndent())
|
|
||||||
assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)")
|
|
||||||
.isEqualTo(test.colorize(Colors.BOLD).prependIndent())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testIsoLocalDate() {
|
|
||||||
assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17")
|
|
||||||
assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testLastOrEmpty() {
|
|
||||||
val two = listOf("1", "2")
|
|
||||||
assertThat(two.lastOrEmpty(), "lastOrEmpty(1,2)").isEqualTo("2")
|
|
||||||
val one = listOf("1")
|
|
||||||
assertThat(one.lastOrEmpty(), "lastOrEmpty(1)").isEqualTo("")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testObfuscate() {
|
|
||||||
assertThat(ascii.obfuscate(), "obfuscate()").all {
|
|
||||||
length().isEqualTo(ascii.length)
|
|
||||||
isEqualTo(("x".repeat(ascii.length)))
|
|
||||||
}
|
}
|
||||||
assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testPlural() {
|
fun `Convert a Date to an ISO date`() {
|
||||||
val week = "week"
|
assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17")
|
||||||
val weeks = "weeks"
|
}
|
||||||
|
|
||||||
for (i in -1..3) {
|
@Test
|
||||||
assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week)
|
fun `Convert a LocalDate to an ISO date`() {
|
||||||
|
assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Convert a Date to a UTC date-time`() {
|
||||||
|
assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Convert a LocalDate to a UTC date-time`() {
|
||||||
|
assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Today should return the current date in ISO format`() {
|
||||||
|
assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun testReplaceEach() {
|
@DisplayName("Help Tests")
|
||||||
val search = arrayOf("one", "two", "three")
|
inner class HelpTests {
|
||||||
val replace = arrayOf("1", "2", "3")
|
private val bot = "mobibot"
|
||||||
assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3")
|
|
||||||
.isEqualTo(replace.joinToString(","))
|
|
||||||
|
|
||||||
assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test)
|
@Test
|
||||||
|
fun `Construct help string for public message`() {
|
||||||
|
assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)")
|
||||||
|
.isEqualTo("/msg $bot $bot $test /msg $bot $test $bot")
|
||||||
|
}
|
||||||
|
|
||||||
assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)")
|
@Test
|
||||||
.isEqualTo(test.replace("t", "").replace("e", "E"))
|
fun `Construct help string for private message`() {
|
||||||
|
assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)")
|
||||||
|
.isEqualTo("$bot: $test $bot $test")
|
||||||
|
}
|
||||||
|
|
||||||
assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)")
|
@Test
|
||||||
.isEqualTo(test)
|
fun `Format help string with bold`() {
|
||||||
|
assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)")
|
||||||
|
.isEqualTo("${Colors.BOLD}$test${Colors.BOLD}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Format help string with indent`() {
|
||||||
|
assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)")
|
||||||
|
.isEqualTo(test.prependIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Format help string with bold and indent`() {
|
||||||
|
assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)")
|
||||||
|
.isEqualTo(test.colorize(Colors.BOLD).prependIndent())
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRed() {
|
@Nested
|
||||||
assertThat(ascii.red()).isEqualTo(ascii.colorize(Colors.RED))
|
@DisplayName("Properties Tests")
|
||||||
|
inner class PropertiesTests {
|
||||||
|
@Test
|
||||||
|
fun `Convert properties to int`() {
|
||||||
|
assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1)
|
||||||
|
assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2)
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `Convert property to int using default value`() {
|
||||||
|
assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testReverseColor() {
|
@Nested
|
||||||
assertThat(ascii.reverseColor()).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE)
|
@DisplayName("List Tests")
|
||||||
|
inner class ListTests {
|
||||||
|
@Test
|
||||||
|
fun `Get last item of list`() {
|
||||||
|
val two = listOf("1", "2")
|
||||||
|
assertThat(two.lastOrEmpty(), "lastOrEmpty(1,2)").isEqualTo("2")
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `Return empty if list only has one item`() {
|
||||||
|
val one = listOf("1")
|
||||||
|
assertThat(one.lastOrEmpty(), "lastOrEmpty(1)").isEqualTo("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun testToday() {
|
@DisplayName("String Manipulation Tests")
|
||||||
assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate())
|
inner class StringManipulationTests {
|
||||||
}
|
private val dir = "dir"
|
||||||
|
private val sep = '/'
|
||||||
|
private val url = "https://erik.thauvin.net"
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun testToIntOrDefault() {
|
@DisplayName("Appending Tests")
|
||||||
assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10)
|
inner class AppendingTests {
|
||||||
assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2)
|
@Test
|
||||||
}
|
fun `Append separator char if missing`() {
|
||||||
|
assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)")
|
||||||
|
.isEqualTo(dir + File.separatorChar)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testUnderline() {
|
fun `Append separator char if already present`() {
|
||||||
assertThat(ascii.underline()).isEqualTo(ascii.colorize(Colors.UNDERLINE))
|
assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testUnescapeXml() {
|
fun `Append separator char if not present`() {
|
||||||
assertThat("<a name="test & ''">".unescapeXml()).isEqualTo(
|
assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep")
|
||||||
"<a name=\"test & ''\">"
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Capitalization Tests")
|
||||||
|
inner class CapitalizationTests {
|
||||||
|
@Test
|
||||||
|
fun `Capitalize string`() {
|
||||||
|
assertThat("test".capitalize(), "capitalize(test)").isEqualTo("Test")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Capitalize string already capitalized`() {
|
||||||
|
assertThat("Test".capitalize(), "capitalize(Test)").isEqualTo("Test")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Capitalize string with spaces`() {
|
||||||
|
assertThat(test.capitalize(), "capitalize($test)").isEqualTo(test)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Capitalize empty string`() {
|
||||||
|
assertThat("".capitalize(), "capitalize()").isEqualTo("")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Capitalize words`() {
|
||||||
|
assertThat(test.capitalizeWords(), "capitalizeWords(test)").isEqualTo("This Is A Test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Capitalize words already capitalized`() {
|
||||||
|
assertThat("Already Capitalized".capitalizeWords(), "already capitalized")
|
||||||
|
.isEqualTo("Already Capitalized")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Capitalize words with leading and ending spaces`() {
|
||||||
|
assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Conversion Tests")
|
||||||
|
inner class ConversionTests {
|
||||||
|
@Test
|
||||||
|
fun `Convert string to int`() {
|
||||||
|
assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10)
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `Convert string to int using default value`() {
|
||||||
|
assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Encode URL`() {
|
||||||
|
assertThat("Hello Günter".encodeUrl()).isEqualTo("Hello%20G%C3%BCnter")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Obfuscation Tests")
|
||||||
|
inner class ObfuscationTests {
|
||||||
|
@Test
|
||||||
|
fun `Obfuscate string`() {
|
||||||
|
assertThat(ascii.obfuscate(), "obfuscate()").all {
|
||||||
|
length().isEqualTo(ascii.length)
|
||||||
|
isEqualTo(("x".repeat(ascii.length)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Obfuscate empty string`() {
|
||||||
|
assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Pluralize string`() {
|
||||||
|
val week = "week"
|
||||||
|
val weeks = "weeks"
|
||||||
|
|
||||||
|
for (i in -1..3) {
|
||||||
|
assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Replace Tests")
|
||||||
|
inner class ReplaceTests {
|
||||||
|
|
||||||
|
private val replace = arrayOf("1", "2", "3")
|
||||||
|
|
||||||
|
private val search = arrayOf("one", "two", "three")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Replace occurrences in string`() {
|
||||||
|
assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3")
|
||||||
|
.isEqualTo(replace.joinToString(","))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Replace occurrences not found in string`() {
|
||||||
|
assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test)
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `Replace and remove occurrences in string`() {
|
||||||
|
assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)")
|
||||||
|
.isEqualTo(test.replace("t", "").replace("e", "E"))
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `Replace empty occurrences in string`() {
|
||||||
|
assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)")
|
||||||
|
.isEqualTo(test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Unescape XML`() {
|
||||||
|
assertThat("<a name="test & ''">".unescapeXml()).isEqualTo(
|
||||||
|
"<a name=\"test & ''\">"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun testUrlReader() {
|
fun `URL reader`() {
|
||||||
val reader = URL("https://postman-echo.com/status/200").reader()
|
val reader = URL("https://postman-echo.com/status/200").reader()
|
||||||
assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}")
|
assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}")
|
||||||
assertThat(reader.responseCode).isEqualTo(200)
|
assertThat(reader.responseCode).isEqualTo(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun testUtcDateTime() {
|
@DisplayName("Text Styling Tests")
|
||||||
assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30")
|
inner class TextStylingTests {
|
||||||
assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30")
|
@Nested
|
||||||
|
@DisplayName("Colorize Tests")
|
||||||
|
inner class ColorizeTests {
|
||||||
|
@Test
|
||||||
|
fun `Colorize ASCII characters red`() {
|
||||||
|
assertThat(ascii.colorize(Colors.RED), "red.colorize()")
|
||||||
|
.isEqualTo(Colors.RED + ascii + Colors.NORMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Colorize blank string`() {
|
||||||
|
assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()")
|
||||||
|
.isEqualTo(Colors.NORMAL + " " + Colors.NORMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Colorize default color`() {
|
||||||
|
assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Colorize empty string`() {
|
||||||
|
assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Colorize null`() {
|
||||||
|
assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Color Formatting Tests")
|
||||||
|
inner class ColorFormattingTests {
|
||||||
|
@Test
|
||||||
|
fun `Make ASCII characters bold`() {
|
||||||
|
assertThat(ascii.bold(), "ascii.bold()").isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Make int bold`() {
|
||||||
|
assertThat(1.bold(), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Make long bold`() {
|
||||||
|
assertThat(2L.bold(), "bold(2L)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Make string bold`() {
|
||||||
|
assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Make text cyan`() {
|
||||||
|
assertThat(ascii.cyan()).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Make text green`() {
|
||||||
|
assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Make text red`() {
|
||||||
|
assertThat(ascii.red()).isEqualTo(ascii.colorize(Colors.RED))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Reversed text`() {
|
||||||
|
assertThat(ascii.reverseColor()).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Underline text`() {
|
||||||
|
assertThat(ascii.underline()).isEqualTo(ascii.colorize(Colors.UNDERLINE))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,25 +34,72 @@ package net.thauvin.erik.mobibot.commands
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.isEqualTo
|
import assertk.assertions.isEqualTo
|
||||||
import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime
|
import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class InfoTest {
|
class InfoTest {
|
||||||
@Test
|
@Nested
|
||||||
fun testToUptime() {
|
@DisplayName("Uptime Tests")
|
||||||
assertThat(
|
inner class UptimeTests {
|
||||||
547800300076L.toUptime(),
|
@Test
|
||||||
"upTime(full)"
|
fun `Years, Months, Weeks, Days, Hours and Minutes`() {
|
||||||
).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes")
|
assertThat(547800300076L.toUptime()).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes")
|
||||||
assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes")
|
}
|
||||||
assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes")
|
|
||||||
assertThat(
|
|
||||||
1320300000L.toUptime(),
|
|
||||||
"upTime(weeks days hours minutes)"
|
|
||||||
).isEqualTo("2 weeks 1 day 6 hours 45 minutes")
|
|
||||||
assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes")
|
|
||||||
assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute")
|
|
||||||
assertThat(59000L.toUptime(), "upTime(59 seconds)").isEqualTo("59 seconds")
|
|
||||||
assertThat(0L.toUptime(), "upTime(0 second)").isEqualTo("0 second")
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Hours and Minutes`() {
|
||||||
|
assertThat(24300000L.toUptime()).isEqualTo("6 hours 45 minutes")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Days, Hours and Minutes`() {
|
||||||
|
assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Weeks, Days, Hours and Minutes`() {
|
||||||
|
assertThat(1320300000L.toUptime()).isEqualTo("2 weeks 1 day 6 hours 45 minutes")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `1 Month`() {
|
||||||
|
assertThat(2592000000L.toUptime(), "upTime(1 month)").isEqualTo("1 month")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `3 Days`() {
|
||||||
|
assertThat(259200000L.toUptime(), "upTime(3 days)").isEqualTo("3 days")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `1 Week`() {
|
||||||
|
assertThat(604800000L.toUptime(), "upTime(1 week)").isEqualTo("1 week")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `2 Hours`() {
|
||||||
|
assertThat(7200000L.toUptime(), "upTime(2 hours)").isEqualTo("2 hours")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `45 Minutes`() {
|
||||||
|
assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `1 Minute`() {
|
||||||
|
assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `59 Seconds`() {
|
||||||
|
assertThat(59000L.toUptime(), "upTime(59 seconds)").isEqualTo("59 seconds")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `0 Second`() {
|
||||||
|
assertThat(0L.toUptime(), "upTime(0 second)").isEqualTo("0 second")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ import kotlin.test.Test
|
||||||
|
|
||||||
class RecapTest {
|
class RecapTest {
|
||||||
@Test
|
@Test
|
||||||
fun storeRecapTest() {
|
fun storeRecap() {
|
||||||
for (i in 1..20) {
|
for (i in 1..20) {
|
||||||
Recap.storeRecap("sender$i", "test $i", false)
|
Recap.storeRecap("sender$i", "test $i", false)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ class RecapTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
Recap.storeRecap("sender", "test action", true)
|
Recap.storeRecap("sender", "test action", true)
|
||||||
assertThat(Recap.recaps.last())
|
assertThat(Recap.recaps.last(), "Recap.recaps.last()")
|
||||||
.matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex())
|
.matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,45 +33,122 @@ package net.thauvin.erik.mobibot.commands.links
|
||||||
|
|
||||||
import assertk.all
|
import assertk.all
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.contains
|
import assertk.assertions.*
|
||||||
import assertk.assertions.isEqualTo
|
|
||||||
import assertk.assertions.isTrue
|
|
||||||
import assertk.assertions.size
|
|
||||||
import net.thauvin.erik.mobibot.Constants
|
import net.thauvin.erik.mobibot.Constants
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class LinksManagerTest {
|
class LinksManagerTest {
|
||||||
private val linksManager = LinksManager()
|
private val linksManager = LinksManager()
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun fetchTitle() {
|
@DisplayName("Fetch Page Title Tests")
|
||||||
assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog")
|
inner class FetchPageTitleTests {
|
||||||
assertThat(
|
@Test
|
||||||
linksManager.fetchTitle("https://www.google.com/foo"),
|
fun fetchPageTitle() {
|
||||||
"fetchTitle(Foo)"
|
assertThat(linksManager.fetchPageTitle("https://erik.thauvin.net/")).contains("Erik's Weblog")
|
||||||
).isEqualTo(Constants.NO_TITLE)
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun fetchPageNoTitle() {
|
||||||
|
assertThat(linksManager.fetchPageTitle("https://www.google.com/foo")).isEqualTo(Constants.NO_TITLE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun testMatches() {
|
@DisplayName("Match Tests")
|
||||||
assertThat(linksManager.matches("https://www.example.com/"), "matches(url)").isTrue()
|
inner class MatchTests {
|
||||||
assertThat(linksManager.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "matches(HTTP)").isTrue()
|
@Nested
|
||||||
}
|
@DisplayName("Link Tests")
|
||||||
|
inner class LinkTests {
|
||||||
|
@Test
|
||||||
|
@Suppress("HttpUrlsUsage")
|
||||||
|
fun matchInsecureLink() {
|
||||||
|
assertThat(linksManager.matches("http://erik.thauvin.net/blog/ Erik's Weblog")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun matchTagKeywordsTest() {
|
fun matchInvalidProtocol() {
|
||||||
linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3")
|
assertThat(linksManager.matches("ftp://erik.thauvin.net/blog/")).isFalse()
|
||||||
val tags = mutableListOf<String>()
|
}
|
||||||
|
|
||||||
linksManager.matchTagKeywords("Test title with key2", tags)
|
@Test
|
||||||
assertThat(tags, "tags").contains("key2")
|
fun matchLink() {
|
||||||
tags.clear()
|
assertThat(linksManager.matches("https://erik.thauvin.net/blog/")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
linksManager.matchTagKeywords("Test key3 title with key1", tags)
|
@Test
|
||||||
assertThat(tags, "tags(key1, key3)").all {
|
fun matchLinkWithAnchor() {
|
||||||
contains("key1")
|
assertThat(linksManager.matches("https://erik.thauvin.net/blog/search?tag=java#foo")).isTrue()
|
||||||
contains("key3")
|
}
|
||||||
size().isEqualTo(2)
|
|
||||||
|
@Test
|
||||||
|
fun matchLinkWithParams() {
|
||||||
|
assertThat(linksManager.matches("https://erik.thauvin.net/blog/search?tag=bld&cat=java")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchLinkWithSingleParam() {
|
||||||
|
assertThat(linksManager.matches("https://erik.thauvin.net/blog/search?tag=java")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchLinkWithTitle() {
|
||||||
|
assertThat(linksManager.matches("https://erik.thauvin.net/blog/ Erik's Weblog")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchLinkWithWhitespace() {
|
||||||
|
assertThat(linksManager.matches("https://erik.thauvin.net/blog/ ")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchMixedCaseLink() {
|
||||||
|
assertThat(linksManager.matches("https://Erik.Thauvin.Net/blog/")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchNonURLText() {
|
||||||
|
assertThat(linksManager.matches("This is just a text string")).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchNumericURL() {
|
||||||
|
assertThat(linksManager.matches("https://123.456.789.0/")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchSpecialCharacterLink() {
|
||||||
|
assertThat(linksManager.matches("https://erik.thauvin.net/blog/search?tag=java&name=%20foo")).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchUpperCaseLink() {
|
||||||
|
assertThat(linksManager.matches("HTTPS://ERIK.THAUVIN.NET/BLOG/")).isTrue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Tags Parsing Tests")
|
||||||
|
inner class TagsParsingTests {
|
||||||
|
@Test
|
||||||
|
fun matchTagSingleKeyword() {
|
||||||
|
linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3")
|
||||||
|
val tags = mutableListOf<String>()
|
||||||
|
linksManager.matchTagKeywords("Test title with key2", tags)
|
||||||
|
assertThat(tags, "tags").containsExactly("key2")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun matchTagKeywords() {
|
||||||
|
val tags = mutableListOf("key1", "key3")
|
||||||
|
linksManager.matchTagKeywords("Test key3 title with key1", tags)
|
||||||
|
assertThat(tags, "tags(key1, key3)").all {
|
||||||
|
containsExactlyInAnyOrder("key1", "key3")
|
||||||
|
size().isEqualTo(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,76 +36,103 @@ import assertk.assertThat
|
||||||
import assertk.assertions.isEqualTo
|
import assertk.assertions.isEqualTo
|
||||||
import assertk.assertions.prop
|
import assertk.assertions.prop
|
||||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class ViewTest {
|
class ViewTest {
|
||||||
@Test
|
companion object {
|
||||||
fun testParseArgs() {
|
init {
|
||||||
val view = View()
|
for (i in 1..10) {
|
||||||
|
LinksManager.entries.links.add(
|
||||||
for (i in 1..10) {
|
EntryLink(
|
||||||
LinksManager.entries.links.add(
|
"https://www.example.com/$i",
|
||||||
EntryLink(
|
"Example $i",
|
||||||
"https://www.example.com/$i",
|
"nick$i",
|
||||||
"Example $i",
|
"login$i",
|
||||||
"nick$i",
|
"#channel",
|
||||||
"login$i",
|
emptyList()
|
||||||
"#channel",
|
)
|
||||||
emptyList()
|
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Parse Args Tests")
|
||||||
|
inner class ParseArgsTests {
|
||||||
|
private val view = View()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Parse alphanumeric query`() {
|
||||||
|
assertThat(view.parseArgs("1a"), "parseArgs(1a)").all {
|
||||||
|
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("1a")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(view.parseArgs("1"), "parseArgs(1)").all {
|
@Test
|
||||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
fun `Parse empty arguments`() {
|
||||||
prop(Pair<Int, String>::second).isEqualTo("")
|
assertThat(view.parseArgs(""), "parseArgs()").all {
|
||||||
|
prop(Pair<Int, String>::first).isEqualTo(LinksManager.entries.links.size - View.MAX_ENTRIES)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all {
|
@Test
|
||||||
prop(Pair<Int, String>::first).isEqualTo(1)
|
fun `Parse first item`() {
|
||||||
prop(Pair<Int, String>::second).isEqualTo("foo")
|
assertThat(view.parseArgs("1"), "parseArgs(1)").all {
|
||||||
|
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all {
|
@Test
|
||||||
prop(Pair<Int, String>::first).isEqualTo(2)
|
fun `Parse fourth item with query needing trimming`() {
|
||||||
prop(Pair<Int, String>::second).isEqualTo("foo")
|
assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all {
|
||||||
|
prop(Pair<Int, String>::first).isEqualTo(3)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("foo bar")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all {
|
@Test
|
||||||
prop(Pair<Int, String>::first).isEqualTo(3)
|
fun `Parse overflowed item as query`() {
|
||||||
prop(Pair<Int, String>::second).isEqualTo("foo bar")
|
assertThat(view.parseArgs("${Int.MAX_VALUE}1"), "parseArgs(overflow)").all {
|
||||||
|
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("${Int.MAX_VALUE}1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(view.parseArgs("foo bar"), "parseArgs(foo bar)").all {
|
@Test
|
||||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
fun `Parse out of bounds item`() {
|
||||||
prop(Pair<Int, String>::second).isEqualTo("foo bar")
|
assertThat(view.parseArgs("20"), "parseArgs(20)").all {
|
||||||
|
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(view.parseArgs("${Int.MAX_VALUE}1"), "parseArgs(overflow)").all {
|
@Test
|
||||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
fun `Parse query only`() {
|
||||||
prop(Pair<Int, String>::second).isEqualTo("${Int.MAX_VALUE}1")
|
assertThat(view.parseArgs("foo bar"), "parseArgs(foo bar)").all {
|
||||||
|
prop(Pair<Int, String>::first).isEqualTo(0)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("foo bar")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(view.parseArgs("1a"), "parseArgs(1a)").all {
|
@Test
|
||||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
fun `Parse second item with query`() {
|
||||||
prop(Pair<Int, String>::second).isEqualTo("1a")
|
assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all {
|
||||||
|
prop(Pair<Int, String>::first).isEqualTo(1)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("foo")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(view.parseArgs("20"), "parseArgs(20)").all {
|
@Test
|
||||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
fun `Parse third item with query ignoring capitalization`() {
|
||||||
prop(Pair<Int, String>::second).isEqualTo("")
|
assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all {
|
||||||
}
|
prop(Pair<Int, String>::first).isEqualTo(2)
|
||||||
|
prop(Pair<Int, String>::second).isEqualTo("foo")
|
||||||
assertThat(view.parseArgs(""), "parseArgs()").all {
|
}
|
||||||
prop(Pair<Int, String>::first).isEqualTo(LinksManager.entries.links.size - View.MAX_ENTRIES)
|
|
||||||
prop(Pair<Int, String>::second).isEqualTo("")
|
|
||||||
}
|
|
||||||
|
|
||||||
LinksManager.entries.links.clear()
|
|
||||||
|
|
||||||
assertThat(view.parseArgs("4"), "parseArgs(4)").all {
|
|
||||||
prop(Pair<Int, String>::first).isEqualTo(0)
|
|
||||||
prop(Pair<Int, String>::second).isEqualTo("")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,60 +34,50 @@ package net.thauvin.erik.mobibot.commands.seen
|
||||||
import assertk.all
|
import assertk.all
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.*
|
import assertk.assertions.*
|
||||||
import org.junit.AfterClass
|
import org.junit.jupiter.api.MethodOrderer
|
||||||
import org.junit.BeforeClass
|
|
||||||
import org.junit.jupiter.api.Order
|
import org.junit.jupiter.api.Order
|
||||||
import kotlin.io.path.deleteIfExists
|
import org.junit.jupiter.api.TestMethodOrder
|
||||||
import kotlin.io.path.fileSize
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
|
||||||
class SeenTest {
|
class SeenTest {
|
||||||
|
companion object {
|
||||||
|
private val tmpFile = kotlin.io.path.createTempFile(SeenTest::class.java.simpleName, suffix = ".ser")
|
||||||
|
private val seen = Seen(tmpFile.toAbsolutePath().toString())
|
||||||
|
private const val NICK = "ErikT"
|
||||||
|
|
||||||
|
init {
|
||||||
|
tmpFile.toFile().deleteOnExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
fun loadTest() {
|
fun add() {
|
||||||
|
val last = seen.seenNicks[NICK]?.lastSeen
|
||||||
|
seen.add(NICK)
|
||||||
|
assertThat(seen).all {
|
||||||
|
prop(Seen::seenNicks).size().isEqualTo(1)
|
||||||
|
prop(Seen::seenNicks).key(NICK).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last)
|
||||||
|
prop(Seen::seenNicks).key(NICK).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(NICK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(2)
|
||||||
|
fun load() {
|
||||||
seen.clear()
|
seen.clear()
|
||||||
assertThat(seen::seenNicks).isEmpty()
|
assertThat(seen::seenNicks).isEmpty()
|
||||||
seen.load()
|
seen.load()
|
||||||
assertThat(seen::seenNicks).key(NICK).isNotNull()
|
assertThat(seen::seenNicks).key(NICK).isNotNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Order(2)
|
|
||||||
fun addTest() {
|
|
||||||
val last = seen.seenNicks[NICK]?.lastSeen
|
|
||||||
seen.add(NICK.lowercase())
|
|
||||||
assertThat(seen).all {
|
|
||||||
prop(Seen::seenNicks).size().isEqualTo(1)
|
|
||||||
prop(Seen::seenNicks).key(NICK).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last)
|
|
||||||
prop(Seen::seenNicks).key(NICK).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(NICK.lowercase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
@Order(3)
|
||||||
fun clearTest() {
|
fun clear() {
|
||||||
seen.clear()
|
seen.clear()
|
||||||
seen.save()
|
seen.save()
|
||||||
seen.load()
|
seen.load()
|
||||||
assertThat(seen::seenNicks).size().isEqualTo(0)
|
assertThat(seen::seenNicks).isEmpty()
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser")
|
|
||||||
private val seen = Seen(tmpFile.toAbsolutePath().toString())
|
|
||||||
private const val NICK = "ErikT"
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@BeforeClass
|
|
||||||
fun beforeClass() {
|
|
||||||
seen.add(NICK)
|
|
||||||
assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@AfterClass
|
|
||||||
fun afterClass() {
|
|
||||||
tmpFile.deleteIfExists()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ class TellMessageTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testTellMessage() {
|
fun validateTellMessage() {
|
||||||
val message = "Test message."
|
val message = "Test message."
|
||||||
val recipient = "recipient"
|
val recipient = "recipient"
|
||||||
val sender = "sender"
|
val sender = "sender"
|
||||||
|
|
|
@ -34,10 +34,8 @@ package net.thauvin.erik.mobibot.commands.tell
|
||||||
import assertk.all
|
import assertk.all
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.*
|
import assertk.assertions.*
|
||||||
import org.junit.AfterClass
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import kotlin.io.path.createTempFile
|
import kotlin.io.path.createTempFile
|
||||||
import kotlin.io.path.deleteIfExists
|
|
||||||
import kotlin.io.path.fileSize
|
import kotlin.io.path.fileSize
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
@ -49,13 +47,21 @@ class TellMessagesMgrTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val testFile = createTempFile(TellMessagesMgrTest::class.java.simpleName, suffix = ".ser")
|
||||||
|
|
||||||
|
init {
|
||||||
|
testFile.toFile().deleteOnExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
TellManager.save(testFile.toAbsolutePath().toString(), testMessages)
|
TellManager.save(testFile.toAbsolutePath().toString(), testMessages)
|
||||||
assertThat(testFile.fileSize()).isGreaterThan(0)
|
assertThat(testFile.fileSize()).isGreaterThan(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun cleanTest() {
|
fun clean() {
|
||||||
testMessages.add(TellMessage("sender", "recipient", "message").apply {
|
testMessages.add(TellMessage("sender", "recipient", "message").apply {
|
||||||
queued = LocalDateTime.now().minusDays(maxDays)
|
queued = LocalDateTime.now().minusDays(maxDays)
|
||||||
})
|
})
|
||||||
|
@ -66,7 +72,7 @@ class TellMessagesMgrTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun loadTest() {
|
fun load() {
|
||||||
val messages = TellManager.load(testFile.toAbsolutePath().toString())
|
val messages = TellManager.load(testFile.toAbsolutePath().toString())
|
||||||
for (i in messages.indices) {
|
for (i in messages.indices) {
|
||||||
assertThat(messages).index(i).all {
|
assertThat(messages).index(i).all {
|
||||||
|
@ -76,14 +82,4 @@ class TellMessagesMgrTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val testFile = createTempFile(suffix = ".ser")
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@AfterClass
|
|
||||||
fun afterClass() {
|
|
||||||
testFile.deleteIfExists()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink
|
import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags
|
import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class EntriesUtilsTest {
|
class EntriesUtilsTest {
|
||||||
|
@ -58,34 +60,38 @@ class EntriesUtilsTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun printCommentTest() {
|
@DisplayName("Print Tests")
|
||||||
assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment")
|
inner class PrintTests {
|
||||||
}
|
@Test
|
||||||
|
fun printComment() {
|
||||||
@Test
|
assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment")
|
||||||
fun printLinkTest() {
|
|
||||||
for (i in links.indices) {
|
|
||||||
assertThat(
|
|
||||||
printLink(i - 1, links[i]), "link $i"
|
|
||||||
).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(links.first().addComment(comment), "addComment()").isEqualTo(0)
|
@Test
|
||||||
assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]")
|
fun printLink() {
|
||||||
}
|
for (i in links.indices) {
|
||||||
|
assertThat(
|
||||||
|
printLink(i - 1, links[i]), "link $i"
|
||||||
|
).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
assertThat(links.first().addComment(comment), "addComment()").isEqualTo(0)
|
||||||
fun printTagsTest() {
|
assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]")
|
||||||
for (i in links.indices) {
|
}
|
||||||
assertThat(
|
|
||||||
printTags(i - 1, links[i]), "tag $i"
|
@Test
|
||||||
).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5")
|
fun printTags() {
|
||||||
|
for (i in links.indices) {
|
||||||
|
assertThat(
|
||||||
|
printTags(i - 1, links[i]), "tag $i"
|
||||||
|
).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun toLinkLabelTest() {
|
fun toLinkLabel() {
|
||||||
assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2")
|
assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ import assertk.assertThat
|
||||||
import assertk.assertions.*
|
import assertk.assertions.*
|
||||||
import com.rometools.rome.feed.synd.SyndCategory
|
import com.rometools.rome.feed.synd.SyndCategory
|
||||||
import com.rometools.rome.feed.synd.SyndCategoryImpl
|
import com.rometools.rome.feed.synd.SyndCategoryImpl
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
@ -46,7 +48,7 @@ class EntryLinkTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAddDeleteComment() {
|
fun `Add then delete comment`() {
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i < 5) {
|
while (i < 5) {
|
||||||
entryLink.addComment("c$i", "u$i")
|
entryLink.addComment("c$i", "u$i")
|
||||||
|
@ -63,7 +65,7 @@ class EntryLinkTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
val r = SecureRandom()
|
val r = SecureRandom()
|
||||||
while (entryLink.comments.size > 0) {
|
while (entryLink.comments.isNotEmpty()) {
|
||||||
entryLink.deleteComment(r.nextInt(entryLink.comments.size))
|
entryLink.deleteComment(r.nextInt(entryLink.comments.size))
|
||||||
}
|
}
|
||||||
assertThat(entryLink.comments, "hasComments()").isEmpty()
|
assertThat(entryLink.comments, "hasComments()").isEmpty()
|
||||||
|
@ -79,7 +81,7 @@ class EntryLinkTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testConstructor() {
|
fun `Validate EntryLink constructor`() {
|
||||||
val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" })
|
val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" })
|
||||||
val link = EntryLink("link", "title", "nick", "channel", Date(), tags)
|
val link = EntryLink("link", "title", "nick", "channel", Date(), tags)
|
||||||
assertThat(link, "link").all {
|
assertThat(link, "link").all {
|
||||||
|
@ -89,7 +91,7 @@ class EntryLinkTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMatches() {
|
fun `Validate EntryLink matches`() {
|
||||||
assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue()
|
assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue()
|
||||||
assertThat(entryLink.matches("skynx"), "match(nick)").isTrue()
|
assertThat(entryLink.matches("skynx"), "match(nick)").isTrue()
|
||||||
assertThat(entryLink.matches("www.mobitopia.org"), "matches(url)").isTrue()
|
assertThat(entryLink.matches("www.mobitopia.org"), "matches(url)").isTrue()
|
||||||
|
@ -98,29 +100,52 @@ class EntryLinkTest {
|
||||||
assertThat(entryLink.matches(null), "matches(null)").isFalse()
|
assertThat(entryLink.matches(null), "matches(null)").isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
@Test
|
@DisplayName("Validate Tags Test")
|
||||||
fun testTags() {
|
inner class ValidateTagsTest {
|
||||||
val tags: List<SyndCategory> = entryLink.tags
|
@Test
|
||||||
for ((i, tag) in tags.withIndex()) {
|
fun `Validate tags parsing in constructor`() {
|
||||||
assertThat(tag.name, "tag.name($i)").isEqualTo("tag${i + 1}")
|
val tags: List<SyndCategory> = entryLink.tags
|
||||||
|
for ((i, tag) in tags.withIndex()) {
|
||||||
|
assertThat(tag.name, "tag.name($i)").isEqualTo("tag${i + 1}")
|
||||||
|
}
|
||||||
|
assertThat(entryLink::tags).size().isEqualTo(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Validate attempting to remove channel tag`() {
|
||||||
|
val link = entryLink
|
||||||
|
link.setTags("+mobitopia")
|
||||||
|
link.setTags("-mobitopia") // can't remove the channel tag
|
||||||
|
assertThat(
|
||||||
|
link.formatTags(",")
|
||||||
|
).isEqualTo("tag1,tag2,tag3,tag4,tag5,mobitopia")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Validate formatting tags with spaces`() {
|
||||||
|
val link = entryLink
|
||||||
|
link.setTags("-tag4")
|
||||||
|
assertThat(
|
||||||
|
link.formatTags(" ", ",")
|
||||||
|
).isEqualTo(",tag1 tag2 tag3 tag5")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Validate setting blank tags`() {
|
||||||
|
val link = entryLink
|
||||||
|
val size = link.tags.size
|
||||||
|
link.setTags(" ")
|
||||||
|
assertThat(link.tags, "setTags(' ')").size().isEqualTo(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Validate setting empty tags`() {
|
||||||
|
val link = entryLink
|
||||||
|
val size = link.tags.size
|
||||||
|
link.setTags("")
|
||||||
|
assertThat(link.tags, "setTags(\"\")").size().isEqualTo(size)
|
||||||
|
|
||||||
}
|
}
|
||||||
assertThat(entryLink::tags).size().isEqualTo(5)
|
|
||||||
entryLink.setTags("-tag5, tag4")
|
|
||||||
entryLink.setTags("+mobitopia")
|
|
||||||
entryLink.setTags("-mobitopia")
|
|
||||||
assertThat(
|
|
||||||
entryLink.formatTags(","),
|
|
||||||
"formatTags(',')"
|
|
||||||
).isEqualTo("tag1,tag2,tag3,tag4,mobitopia")
|
|
||||||
entryLink.setTags("-tag4 tag5")
|
|
||||||
assertThat(
|
|
||||||
entryLink.formatTags(" ", ","), "formatTag(' ',',')"
|
|
||||||
).isEqualTo(",tag1 tag2 tag3 mobitopia tag5")
|
|
||||||
val size = entryLink.tags.size
|
|
||||||
entryLink.setTags("")
|
|
||||||
assertThat(entryLink.tags, "setTags('')").size().isEqualTo(size)
|
|
||||||
entryLink.setTags(" ")
|
|
||||||
assertThat(entryLink.tags, "setTags(' ')").size().isEqualTo(size)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,10 @@ import assertk.all
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.*
|
import assertk.assertions.*
|
||||||
import net.thauvin.erik.mobibot.Utils.today
|
import net.thauvin.erik.mobibot.Utils.today
|
||||||
|
import org.junit.jupiter.api.MethodOrderer
|
||||||
|
import org.junit.jupiter.api.Order
|
||||||
|
import org.junit.jupiter.api.TestMethodOrder
|
||||||
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.io.path.deleteIfExists
|
import kotlin.io.path.deleteIfExists
|
||||||
|
@ -42,19 +46,26 @@ import kotlin.io.path.fileSize
|
||||||
import kotlin.io.path.name
|
import kotlin.io.path.name
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
|
||||||
class FeedMgrTest {
|
class FeedMgrTest {
|
||||||
private val entries = Entries()
|
private val entries = Entries()
|
||||||
private val channel = "mobibot"
|
private val channel = "mobibot"
|
||||||
|
private var currentFile: Path
|
||||||
|
private var backlogFile: Path
|
||||||
|
|
||||||
init {
|
init {
|
||||||
entries.logsDir = "src/test/resources/"
|
entries.logsDir = "src/test/resources/"
|
||||||
entries.ircServer = "irc.example.com"
|
entries.ircServer = "irc.example.com"
|
||||||
entries.channel = channel
|
entries.channel = channel
|
||||||
entries.backlogs = "https://www.mobitopia.org/mobibot/logs"
|
entries.backlogs = "https://www.mobitopia.org/mobibot/logs"
|
||||||
|
|
||||||
|
currentFile = Paths.get("${entries.logsDir}test.xml")
|
||||||
|
backlogFile = Paths.get("${entries.logsDir}${today()}.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFeedMgr() {
|
@Order(1)
|
||||||
|
fun loadFeed() {
|
||||||
// Load the feed
|
// Load the feed
|
||||||
assertThat(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31")
|
assertThat(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31")
|
||||||
|
|
||||||
|
@ -87,26 +98,33 @@ class FeedMgrTest {
|
||||||
prop(EntryLink::nick).isEqualTo("Skynx")
|
prop(EntryLink::nick).isEqualTo("Skynx")
|
||||||
prop(EntryLink::date).isEqualTo(Date(1635638460000L))
|
prop(EntryLink::date).isEqualTo(Date(1635638460000L))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val currentFile = Paths.get("${entries.logsDir}test.xml")
|
@Test
|
||||||
val backlogFile = Paths.get("${entries.logsDir}${today()}.xml")
|
@Order(2)
|
||||||
|
fun saveFeed() {
|
||||||
// Save the feed
|
|
||||||
FeedsManager.saveFeed(entries, currentFile.name)
|
FeedsManager.saveFeed(entries, currentFile.name)
|
||||||
|
|
||||||
assertThat(currentFile, "currentFile").exists()
|
assertThat(currentFile, "currentFile").exists()
|
||||||
assertThat(backlogFile, "backlogFile").exists()
|
assertThat(backlogFile, "backlogFile").exists()
|
||||||
|
|
||||||
assertThat(currentFile.fileSize(), "currentFile == backlogFile").isEqualTo(backlogFile.fileSize())
|
assertThat(currentFile.fileSize(), "currentFile == backlogFile").isEqualTo(backlogFile.fileSize())
|
||||||
|
}
|
||||||
|
|
||||||
// Load the test feed
|
@Test
|
||||||
|
@Order(3)
|
||||||
|
fun loadTestFeed() {
|
||||||
entries.links.clear()
|
entries.links.clear()
|
||||||
FeedsManager.loadFeed(entries, currentFile.name)
|
FeedsManager.loadFeed(entries, currentFile.name)
|
||||||
|
|
||||||
entries.links.forEachIndexed { i, entryLink ->
|
entries.links.forEachIndexed { i, entryLink ->
|
||||||
assertThat(entryLink.title, "entryLink.title[${i + 1}]").isEqualTo("Example ${i + 1}")
|
assertThat(entryLink.title, "entryLink.title[${i + 1}]").isEqualTo("Example ${i + 1}")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(4)
|
||||||
|
fun deleteFeeds() {
|
||||||
assertThat(currentFile.deleteIfExists(), "currentFile.deleteIfExists()").isTrue()
|
assertThat(currentFile.deleteIfExists(), "currentFile.deleteIfExists()").isTrue()
|
||||||
assertThat(backlogFile.deleteIfExists(), "backlogFile.deleteIfExists()").isTrue()
|
assertThat(backlogFile.deleteIfExists(), "backlogFile.deleteIfExists()").isTrue()
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,10 +41,33 @@ import kotlin.test.Test
|
||||||
|
|
||||||
class CalcTest {
|
class CalcTest {
|
||||||
@Test
|
@Test
|
||||||
fun testCalculate() {
|
fun `Calculate basic addition`() {
|
||||||
assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}")
|
assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Calculate basic subtraction`() {
|
||||||
assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}")
|
assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Calculate mathematical constants`() {
|
||||||
assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}")
|
assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Calculate scientific notations`() {
|
||||||
|
assertThat(calculate("3e2 - 3e1"), "calculate(3e2-3e1 )").isEqualTo("3e2-3e1 = ${"270".bold()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Calculate trigonometric functions`() {
|
||||||
|
assertThat(calculate("3*sin(10)-cos(2)"), "calculate(3*sin(10)-cos(2)")
|
||||||
|
.isEqualTo("3*sin(10)-cos(2) = ${"-1.22".bold()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Invalid calculation show throw exception`() {
|
||||||
assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,30 +32,45 @@ package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import assertk.assertFailure
|
import assertk.assertFailure
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.contains
|
|
||||||
import assertk.assertions.hasNoCause
|
import assertk.assertions.hasNoCause
|
||||||
import assertk.assertions.isInstanceOf
|
import assertk.assertions.isInstanceOf
|
||||||
|
import assertk.assertions.contains
|
||||||
import net.thauvin.erik.mobibot.DisableOnCi
|
import net.thauvin.erik.mobibot.DisableOnCi
|
||||||
import net.thauvin.erik.mobibot.LocalProperties
|
import net.thauvin.erik.mobibot.LocalProperties
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class ChatGpt2Test : LocalProperties() {
|
class ChatGpt2Test : LocalProperties() {
|
||||||
@Test
|
@Test
|
||||||
fun testApiKey() {
|
fun apiKey() {
|
||||||
assertFailure { ChatGpt2.chat("1 gallon to liter", "", 0) }
|
assertFailure { ChatGpt2.chat("1 gallon to liter", "", 0) }
|
||||||
.isInstanceOf(ModuleException::class.java)
|
.isInstanceOf(ModuleException::class.java)
|
||||||
.hasNoCause()
|
.hasNoCause()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
@DisableOnCi
|
@DisplayName("Chat Tests")
|
||||||
fun testChat() {
|
inner class ChatTests {
|
||||||
val apiKey = getProperty(ChatGpt2.API_KEY_PROP)
|
private val apiKey = getProperty(ChatGpt2.API_KEY_PROP)
|
||||||
assertThat(
|
|
||||||
ChatGpt2.chat("how do I make an HTTP request in Javascript?", apiKey, 200)
|
|
||||||
).contains("XMLHttpRequest")
|
|
||||||
|
|
||||||
assertFailure { ChatGpt2.chat("1 liter to gallon", apiKey, -1) }
|
@Test
|
||||||
.isInstanceOf(ModuleException::class.java)
|
@DisableOnCi
|
||||||
|
fun chat() {
|
||||||
|
assertThat(
|
||||||
|
ChatGpt2.chat(
|
||||||
|
"return only the code for javascript function to make a request with XMLHttpRequest",
|
||||||
|
apiKey,
|
||||||
|
50
|
||||||
|
)
|
||||||
|
).contains("```javascript")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisableOnCi
|
||||||
|
fun chatFailure() {
|
||||||
|
assertFailure { ChatGpt2.chat("1 liter to gallon", apiKey, -1) }
|
||||||
|
.isInstanceOf(ModuleException::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,8 @@ import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice
|
||||||
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName
|
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName
|
||||||
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies
|
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies
|
||||||
import org.junit.jupiter.api.BeforeAll
|
import org.junit.jupiter.api.BeforeAll
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import java.util.logging.ConsoleHandler
|
import java.util.logging.ConsoleHandler
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
@ -49,30 +51,6 @@ class CryptoPricesTest {
|
||||||
loadCurrencies()
|
loadCurrencies()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Throws(ModuleException::class)
|
|
||||||
fun testMarketPrice() {
|
|
||||||
var price = currentPrice(listOf("BTC"))
|
|
||||||
assertThat(price, "currentPrice(BTC)").all {
|
|
||||||
prop(CryptoPrice::base).isEqualTo("BTC")
|
|
||||||
prop(CryptoPrice::currency).isEqualTo("USD")
|
|
||||||
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
price = currentPrice(listOf("ETH", "EUR"))
|
|
||||||
assertThat(price, "currentPrice(ETH, EUR)").all {
|
|
||||||
prop(CryptoPrice::base).isEqualTo("ETH")
|
|
||||||
prop(CryptoPrice::currency).isEqualTo("EUR")
|
|
||||||
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGetCurrencyName() {
|
|
||||||
assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar")
|
|
||||||
assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro")
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
|
@ -84,4 +62,44 @@ class CryptoPricesTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Current Price Tests")
|
||||||
|
inner class CurrentPriceTests {
|
||||||
|
@Test
|
||||||
|
@Throws(ModuleException::class)
|
||||||
|
fun currentPriceBitcoin() {
|
||||||
|
val price = currentPrice(listOf("BTC"))
|
||||||
|
assertThat(price, "currentPrice(BTC)").all {
|
||||||
|
prop(CryptoPrice::base).isEqualTo("BTC")
|
||||||
|
prop(CryptoPrice::currency).isEqualTo("USD")
|
||||||
|
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ModuleException::class)
|
||||||
|
fun currentPriceEthereum() {
|
||||||
|
val price = currentPrice(listOf("ETH", "EUR"))
|
||||||
|
assertThat(price, "currentPrice(ETH, EUR)").all {
|
||||||
|
prop(CryptoPrice::base).isEqualTo("ETH")
|
||||||
|
prop(CryptoPrice::currency).isEqualTo("EUR")
|
||||||
|
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Currency Name Tests")
|
||||||
|
inner class CurrencyNameTests {
|
||||||
|
@Test
|
||||||
|
fun getCurrencyNameUsd() {
|
||||||
|
assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getCurrencyNameEur() {
|
||||||
|
assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols
|
||||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||||
import net.thauvin.erik.mobibot.msg.Message
|
import net.thauvin.erik.mobibot.msg.Message
|
||||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class CurrencyConverterTest : LocalProperties() {
|
class CurrencyConverterTest : LocalProperties() {
|
||||||
|
@ -50,28 +52,49 @@ class CurrencyConverterTest : LocalProperties() {
|
||||||
loadSymbols(apiKey)
|
loadSymbols(apiKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun testConvertCurrency() {
|
@DisplayName("Currency Converter Tests")
|
||||||
val apiKey = getProperty(CurrencyConverter.API_KEY_PROP)
|
inner class CurrencyConverterTests {
|
||||||
assertThat(
|
private val apiKey = getProperty(CurrencyConverter.API_KEY_PROP)
|
||||||
convertCurrency(apiKey, "100 USD to EUR").msg,
|
|
||||||
"convertCurrency(100 USD to EUR)"
|
@Test
|
||||||
).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex())
|
fun `Convert CAD to USD`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
convertCurrency(apiKey, "1 USD to GBP").msg,
|
convertCurrency(apiKey, "100,000.00 CAD to USD").msg,
|
||||||
"convertCurrency(1 USD to BGP)"
|
"convertCurrency(100,000.00 GBP to USD)"
|
||||||
).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex())
|
).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex())
|
||||||
assertThat(
|
|
||||||
convertCurrency(apiKey, "100,000.00 CAD to USD").msg,
|
|
||||||
"convertCurrency(100,000.00 GBP to USD)"
|
|
||||||
).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex())
|
|
||||||
assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all {
|
|
||||||
prop(Message::msg).contains("You're kidding, right?")
|
|
||||||
isInstanceOf(PublicMessage::class.java)
|
|
||||||
}
|
}
|
||||||
assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all {
|
|
||||||
prop(Message::msg).contains("Invalid query.")
|
@Test
|
||||||
isInstanceOf(ErrorMessage::class.java)
|
fun `Convert USD to EUR`() {
|
||||||
|
assertThat(
|
||||||
|
convertCurrency(apiKey, "100 USD to EUR").msg,
|
||||||
|
"convertCurrency(100 USD to EUR)"
|
||||||
|
).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Convert USD to GBP`() {
|
||||||
|
assertThat(
|
||||||
|
convertCurrency(apiKey, "1 USD to GBP").msg,
|
||||||
|
"convertCurrency(1 USD to BGP)"
|
||||||
|
).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Convert USD to USD`() {
|
||||||
|
assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all {
|
||||||
|
prop(Message::msg).contains("You're kidding, right?")
|
||||||
|
isInstanceOf(PublicMessage::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Invalid Query should throw exception`() {
|
||||||
|
assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all {
|
||||||
|
prop(Message::msg).contains("Invalid query.")
|
||||||
|
isInstanceOf(ErrorMessage::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,19 +35,64 @@ package net.thauvin.erik.mobibot.modules
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.isEqualTo
|
import assertk.assertions.isEqualTo
|
||||||
import assertk.assertions.matches
|
import assertk.assertions.matches
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
|
import org.junit.jupiter.api.RepeatedTest
|
||||||
|
import kotlin.random.Random
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class DiceTest {
|
class DiceTest {
|
||||||
@Test
|
@Nested
|
||||||
fun testRoll() {
|
@DisplayName("Roll Tests")
|
||||||
assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002")
|
inner class RollTests {
|
||||||
assertThat(Dice.roll(2, 1), "roll(2d1)")
|
@Test
|
||||||
.isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002")
|
fun `Roll 1 die with 1 side`() {
|
||||||
assertThat(Dice.roll(5, 1), "roll(5d1)")
|
assertThat(Dice.roll(1, 1)).isEqualTo("\u00021\u0002")
|
||||||
.isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002")
|
}
|
||||||
assertThat(Dice.roll(2, 6), "roll(2d6)")
|
|
||||||
.matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex())
|
@Test
|
||||||
assertThat(Dice.roll(3, 7), "roll(3d7)")
|
fun `Roll 1 die with 6 sides`() {
|
||||||
.matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex())
|
assertThat(Dice.roll(1, 6)).matches("\u0002[1-6]\u0002".toRegex())
|
||||||
|
}
|
||||||
|
|
||||||
|
@RepeatedTest(5)
|
||||||
|
fun `Roll 1 die with random sides`() {
|
||||||
|
assertThat(Dice.roll(1, Random.nextInt(1, 11))).matches("\u0002([1-9]|10)\u0002".toRegex())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Roll 2 dice with 1 side`() {
|
||||||
|
assertThat(Dice.roll(2, 1)).isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Roll 2 dice with 6 sides`() {
|
||||||
|
assertThat(Dice.roll(2, 6))
|
||||||
|
.matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Roll 3 dice with 1 side`() {
|
||||||
|
assertThat(Dice.roll(4, 1))
|
||||||
|
.isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00024\u0002")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Roll 3 dice with 7 sides`() {
|
||||||
|
assertThat(Dice.roll(3, 7))
|
||||||
|
.matches(
|
||||||
|
"\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002"
|
||||||
|
.toRegex()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RepeatedTest(3)
|
||||||
|
fun `Roll 3 dice with random sides`() {
|
||||||
|
assertThat(Dice.roll(3, Random.nextInt(1, 6)))
|
||||||
|
.matches(
|
||||||
|
"\u0002[1-5]\u0002 \\+ \u0002[1-5]\u0002 \\+ \u0002[1-5]\u0002 = \u0002\\d{1,2}\u0002"
|
||||||
|
.toRegex()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,31 +35,53 @@ import assertk.assertThat
|
||||||
import assertk.assertions.*
|
import assertk.assertions.*
|
||||||
import net.thauvin.erik.mobibot.DisableOnCi
|
import net.thauvin.erik.mobibot.DisableOnCi
|
||||||
import net.thauvin.erik.mobibot.LocalProperties
|
import net.thauvin.erik.mobibot.LocalProperties
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class Gemini2Test : LocalProperties() {
|
class Gemini2Test : LocalProperties() {
|
||||||
@Test
|
@Nested
|
||||||
fun testApiKey() {
|
@DisplayName("Chat Tests")
|
||||||
assertFailure { Gemini2.chat("1 gallon to liter", "", 0) }
|
inner class ChatTests {
|
||||||
.isInstanceOf(ModuleException::class.java)
|
private val apiKey = getProperty(Gemini2.GEMINI_API_KEY)
|
||||||
.hasNoCause()
|
private val maxTokens = getProperty(Gemini2.MAX_TOKENS_PROP).toInt()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisableOnCi
|
||||||
|
fun chatHttpRequestInJavascript() {
|
||||||
|
assertThat(
|
||||||
|
Gemini2.chat(
|
||||||
|
"return only the code for a javascript function to make a request with XMLHttpRequest",
|
||||||
|
apiKey,
|
||||||
|
maxTokens
|
||||||
|
)
|
||||||
|
).isNotNull().contains("```javascript")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisableOnCi
|
||||||
|
fun chatEncodeUrlInJava() {
|
||||||
|
assertThat(
|
||||||
|
Gemini2.chat("how do I encode a URL in java?", apiKey, 60)
|
||||||
|
).isNotNull().contains("URLEncoder")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
@DisableOnCi
|
@DisplayName("API Keys Test")
|
||||||
fun chatPrompt() {
|
inner class ApiKeysTest {
|
||||||
val apiKey = getProperty(Gemini2.GEMINI_API_KEY)
|
@Test
|
||||||
val maxTokens = getProperty(Gemini2.MAX_TOKENS_PROP).toInt()
|
fun invalidApiKey() {
|
||||||
|
assertFailure { Gemini2.chat("1 liter to gallon", "foo", 40) }
|
||||||
|
.isInstanceOf(ModuleException::class.java)
|
||||||
|
.hasMessage(Gemini2.IO_ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
assertThat(
|
@Test
|
||||||
Gemini2.chat("how do I make an HTTP request in Javascript?", apiKey, maxTokens)
|
fun emptyApiKey() {
|
||||||
).isNotNull().contains("XMLHttpRequest")
|
assertFailure { Gemini2.chat("1 liter to gallon", "", 40) }
|
||||||
|
.isInstanceOf(ModuleException::class.java)
|
||||||
assertThat(
|
.hasMessage(Gemini2.API_KEY_ERROR)
|
||||||
Gemini2.chat("how do I encode a URL in java?", apiKey, 60)
|
}
|
||||||
).isNotNull().contains("URLEncoder")
|
|
||||||
|
|
||||||
assertFailure { Gemini2.chat("1 liter to gallon", "foo", 40) }
|
|
||||||
.isInstanceOf(ModuleException::class.java)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,48 +40,15 @@ import net.thauvin.erik.mobibot.LocalProperties
|
||||||
import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
|
import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
|
||||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||||
import net.thauvin.erik.mobibot.msg.Message
|
import net.thauvin.erik.mobibot.msg.Message
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class GoogleSearchTest : LocalProperties() {
|
class GoogleSearchTest : LocalProperties() {
|
||||||
@Test
|
|
||||||
fun testAPIKeys() {
|
|
||||||
assertThat(
|
|
||||||
searchGoogle("", "apikey", "cssKey").first(),
|
|
||||||
"searchGoogle(empty)"
|
|
||||||
).isInstanceOf(ErrorMessage::class.java)
|
|
||||||
|
|
||||||
assertFailure { searchGoogle("test", "", "apiKey") }
|
|
||||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
|
||||||
|
|
||||||
assertFailure { searchGoogle("test", "apiKey", "") }
|
|
||||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
|
||||||
|
|
||||||
assertFailure { searchGoogle("test", "apiKey", "cssKey") }
|
|
||||||
.isInstanceOf(ModuleException::class.java)
|
|
||||||
.hasMessage("API key not valid. Please pass a valid API key.")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisableOnCi
|
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun testSearchGoogle() {
|
fun sanitizedSearch(query: String, apiKey: String, cseKey: String): List<Message> {
|
||||||
val apiKey = getProperty(GoogleSearch.API_KEY_PROP)
|
|
||||||
val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var query = "mobibot"
|
return searchGoogle(query, apiKey, cseKey)
|
||||||
var messages = searchGoogle(query, apiKey, cseKey)
|
|
||||||
assertThat(messages, "searchGoogle($query)").all {
|
|
||||||
isNotEmpty()
|
|
||||||
index(0).prop(Message::msg).contains(query, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
query = "adadflkjl"
|
|
||||||
messages = searchGoogle(query, apiKey, cseKey)
|
|
||||||
assertThat(messages, "searchGoogle($query)").index(0).all {
|
|
||||||
isInstanceOf(ErrorMessage::class.java)
|
|
||||||
prop(Message::msg).isEqualTo("No results found.")
|
|
||||||
}
|
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
// Avoid displaying api keys in CI logs
|
// Avoid displaying api keys in CI logs
|
||||||
if ("true" == System.getenv("CI")) {
|
if ("true" == System.getenv("CI")) {
|
||||||
|
@ -91,4 +58,63 @@ class GoogleSearchTest : LocalProperties() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("API Keys Test")
|
||||||
|
inner class ApiKeysTest {
|
||||||
|
@Test
|
||||||
|
fun `API key should not be empty`() {
|
||||||
|
assertFailure { sanitizedSearch("test", "", "apiKey") }
|
||||||
|
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `CSE key should not empty`() {
|
||||||
|
assertFailure { sanitizedSearch("test", "apiKey", "") }
|
||||||
|
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Invalid API key should throw exception`() {
|
||||||
|
assertFailure { sanitizedSearch("test", "apiKey", "cssKey") }
|
||||||
|
.isInstanceOf(ModuleException::class.java)
|
||||||
|
.hasMessage("API key not valid. Please pass a valid API key.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Search Tests")
|
||||||
|
inner class SearchTests {
|
||||||
|
private val apiKey = getProperty(GoogleSearch.API_KEY_PROP)
|
||||||
|
private val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Query should not be empty`() {
|
||||||
|
assertThat(sanitizedSearch("", apiKey, cseKey).first()).isInstanceOf(ErrorMessage::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisableOnCi
|
||||||
|
@Throws(ModuleException::class)
|
||||||
|
fun `No results found`() {
|
||||||
|
val query = "adadflkjl"
|
||||||
|
val messages = sanitizedSearch(query, apiKey, cseKey)
|
||||||
|
assertThat(messages, "searchGoogle($query)").index(0).all {
|
||||||
|
isInstanceOf(ErrorMessage::class.java)
|
||||||
|
prop(Message::msg).isEqualTo("No results found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisableOnCi
|
||||||
|
@Throws(ModuleException::class)
|
||||||
|
fun `Search Google`() {
|
||||||
|
val query = "mobibot"
|
||||||
|
val messages = sanitizedSearch(query, apiKey, cseKey)
|
||||||
|
assertThat(messages, "searchGoogle($query)").all {
|
||||||
|
isNotEmpty()
|
||||||
|
index(0).prop(Message::msg).contains(query, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ import kotlin.test.Test
|
||||||
class JokeTest {
|
class JokeTest {
|
||||||
@Test
|
@Test
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun testRandomJoke() {
|
fun `Get a random joke`() {
|
||||||
val joke = randomJoke()
|
val joke = randomJoke()
|
||||||
assertThat(joke, "randomJoke()").all {
|
assertThat(joke, "randomJoke()").all {
|
||||||
size().isGreaterThan(0)
|
size().isGreaterThan(0)
|
||||||
|
|
|
@ -35,22 +35,32 @@ import assertk.assertions.any
|
||||||
import assertk.assertions.contains
|
import assertk.assertions.contains
|
||||||
import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup
|
import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup
|
||||||
import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois
|
import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class LookupTest {
|
class LookupTest {
|
||||||
@Test
|
@Nested
|
||||||
@Throws(Exception::class)
|
@DisplayName("Lookup Tests")
|
||||||
fun testLookup() {
|
inner class LookupTests {
|
||||||
var result = nslookup("apple.com")
|
@Test
|
||||||
assertThat(result, "lookup(apple.com)").contains("17.253.144.10")
|
@Throws(Exception::class)
|
||||||
|
fun lookupByHostname() {
|
||||||
|
val result = nslookup("apple.com")
|
||||||
|
assertThat(result, "lookup(apple.com)").contains("17.253.144.10")
|
||||||
|
}
|
||||||
|
|
||||||
result = nslookup("37.27.52.13")
|
@Test
|
||||||
assertThat(result, "lookup(37.27.52.13)").contains("nix4.thauvin.us")
|
@Throws(Exception::class)
|
||||||
|
fun lookupByIpAddress() {
|
||||||
|
val result = nslookup("37.27.52.13")
|
||||||
|
assertThat(result, "lookup(37.27.52.13)").contains("nix4.thauvin.us")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testWhois() {
|
fun whois() {
|
||||||
val result = whois("17.178.96.59", Lookup.WHOIS_HOST)
|
val result = whois("17.178.96.59", Lookup.WHOIS_HOST)
|
||||||
assertThat(result, "whois(17.178.96.59").any { it.contains("Apple Inc.") }
|
assertThat(result, "whois(17.178.96.59").any { it.contains("Apple Inc.") }
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ import kotlin.test.Test
|
||||||
class MastodonTest : LocalProperties() {
|
class MastodonTest : LocalProperties() {
|
||||||
@Test
|
@Test
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun testToot() {
|
fun `Toot on Mastodon`() {
|
||||||
val msg = "Testing Mastodon API from ${getHostName()}"
|
val msg = "Testing Mastodon API from ${getHostName()}"
|
||||||
assertThat(
|
assertThat(
|
||||||
toot(
|
toot(
|
||||||
|
|
|
@ -34,6 +34,8 @@ import assertk.all
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.*
|
import assertk.assertions.*
|
||||||
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
|
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
import org.junit.jupiter.params.provider.MethodSource
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -45,60 +47,64 @@ class ModuleExceptionTest {
|
||||||
const val MESSAGE = "message"
|
const val MESSAGE = "message"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun dataProviders(): List<ModuleException> {
|
fun moduleExceptions(): List<ModuleException> {
|
||||||
return listOf(
|
return listOf(
|
||||||
ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com")),
|
ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("foo")),
|
||||||
ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?")),
|
ModuleException(DEBUG_MESSAGE, MESSAGE, IllegalArgumentException("bar")),
|
||||||
ModuleException(DEBUG_MESSAGE, MESSAGE)
|
ModuleException(DEBUG_MESSAGE, MESSAGE)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@Nested
|
||||||
@MethodSource("dataProviders")
|
@DisplayName("Message Tests")
|
||||||
fun testGetDebugMessage(e: ModuleException) {
|
inner class MessageTests {
|
||||||
assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE)
|
@ParameterizedTest
|
||||||
}
|
@MethodSource("net.thauvin.erik.mobibot.modules.ModuleExceptionTest#moduleExceptions")
|
||||||
|
fun getDebugMessage(e: ModuleException) {
|
||||||
@ParameterizedTest
|
assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE)
|
||||||
@MethodSource("dataProviders")
|
|
||||||
fun testGetMessage(e: ModuleException) {
|
|
||||||
assertThat(e).hasMessage(MESSAGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSanitizeMessage() {
|
|
||||||
val apiKey = "1234567890"
|
|
||||||
var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me"))
|
|
||||||
assertThat(
|
|
||||||
e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))"
|
|
||||||
).isNotNull().all {
|
|
||||||
contains("xxxxxxxxxx", "userID=xx", "java.io.IOException")
|
|
||||||
doesNotContain(apiKey, "me")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e = ModuleException(DEBUG_MESSAGE, MESSAGE, null)
|
@ParameterizedTest
|
||||||
assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE)
|
@MethodSource("net.thauvin.erik.mobibot.modules.ModuleExceptionTest#moduleExceptions")
|
||||||
|
fun getMessage(e: ModuleException) {
|
||||||
e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException())
|
assertThat(e).hasMessage(MESSAGE)
|
||||||
assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE)
|
}
|
||||||
|
|
||||||
e = ModuleException(DEBUG_MESSAGE, apiKey)
|
@Test
|
||||||
assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull()
|
fun sanitizeMessage() {
|
||||||
.doesNotContain(apiKey)
|
val apiKey = "1234567890"
|
||||||
|
var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me"))
|
||||||
val msg: String? = null
|
assertThat(
|
||||||
e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg))
|
e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))"
|
||||||
assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull()
|
).isNotNull().all {
|
||||||
|
contains("xxxxxxxxxx", "userID=xx", "java.io.IOException")
|
||||||
e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey"))
|
doesNotContain(apiKey, "me")
|
||||||
assertThat(
|
}
|
||||||
e.sanitize(" ", apiKey, "foo").message,
|
|
||||||
"ModuleException(debugMessage, msg, IOException(foo is $apiKey))"
|
e = ModuleException(DEBUG_MESSAGE, MESSAGE, null)
|
||||||
).isNotNull().all {
|
assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE)
|
||||||
doesNotContain(apiKey)
|
|
||||||
endsWith("xxx is xxxxxxxxxx")
|
e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException())
|
||||||
|
assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE)
|
||||||
|
|
||||||
|
e = ModuleException(DEBUG_MESSAGE, apiKey)
|
||||||
|
assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull()
|
||||||
|
.doesNotContain(apiKey)
|
||||||
|
|
||||||
|
val msg: String? = null
|
||||||
|
e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg))
|
||||||
|
assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull()
|
||||||
|
|
||||||
|
e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey"))
|
||||||
|
assertThat(
|
||||||
|
e.sanitize(" ", apiKey, "foo").message,
|
||||||
|
"ModuleException(debugMessage, msg, IOException(foo is $apiKey))"
|
||||||
|
).isNotNull().all {
|
||||||
|
doesNotContain(apiKey)
|
||||||
|
endsWith("xxx is xxxxxxxxxx")
|
||||||
|
}
|
||||||
|
assertThat(e.sanitize(), "exception should be unchanged").isEqualTo(e)
|
||||||
}
|
}
|
||||||
assertThat(e.sanitize(), "exception should be unchanged").isEqualTo(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,14 +38,14 @@ import kotlin.test.Test
|
||||||
|
|
||||||
class PingTest {
|
class PingTest {
|
||||||
@Test
|
@Test
|
||||||
fun testPingsArray() {
|
fun `Get a radon ping`() {
|
||||||
assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRandomPing() {
|
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing())
|
assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Pings array should not be empty`() {
|
||||||
|
assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,17 +34,57 @@ package net.thauvin.erik.mobibot.modules
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.isEqualTo
|
import assertk.assertions.isEqualTo
|
||||||
import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw
|
import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class RockPaperScissorsTest {
|
class RockPaperScissorsTest {
|
||||||
@Test
|
@Nested
|
||||||
fun testWinLoseOrDraw() {
|
@DisplayName("Win, Lose or Draw Tests")
|
||||||
assertThat(winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win")
|
inner class WinLoseOrDrawTests {
|
||||||
assertThat(winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win")
|
@Test
|
||||||
assertThat(winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win")
|
fun `Paper versus Paper draws`() {
|
||||||
assertThat(winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose")
|
assertThat(winLoseOrDraw("paper", "paper")).isEqualTo("draw")
|
||||||
assertThat(winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose")
|
}
|
||||||
assertThat(winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose")
|
|
||||||
assertThat(winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw")
|
@Test
|
||||||
|
fun `Paper versus Rock wins`() {
|
||||||
|
assertThat(winLoseOrDraw("paper", "rock")).isEqualTo("win")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Paper versus Scissors loses`() {
|
||||||
|
assertThat(winLoseOrDraw("paper", "scissors")).isEqualTo("lose")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Rock versus Paper loses`() {
|
||||||
|
assertThat(winLoseOrDraw("rock", "paper")).isEqualTo("lose")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Rock versus Rock draws`() {
|
||||||
|
assertThat(winLoseOrDraw("rock", "rock")).isEqualTo("draw")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Rock versus Scissors wins`() {
|
||||||
|
assertThat(winLoseOrDraw("rock", "scissors")).isEqualTo("win")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Scissors versus Paper wins`() {
|
||||||
|
assertThat(winLoseOrDraw("scissors", "paper")).isEqualTo("win")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Scissors versus Rock loses`() {
|
||||||
|
assertThat(winLoseOrDraw("scissors", "rock")).isEqualTo("lose")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Scissors versus Scissors draws`() {
|
||||||
|
assertThat(winLoseOrDraw("scissors", "scissors")).isEqualTo("draw")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue