Moved to PircBotX and assertk.
This commit is contained in:
parent
2a46761dc5
commit
9fb870648e
83 changed files with 2347 additions and 2577 deletions
5
.idea/jarRepositories.xml
generated
5
.idea/jarRepositories.xml
generated
|
@ -31,5 +31,10 @@
|
||||||
<option name="name" value="MavenLocal" />
|
<option name="name" value="MavenLocal" />
|
||||||
<option name="url" value="file:$MAVEN_REPOSITORY$/" />
|
<option name="url" value="file:$MAVEN_REPOSITORY$/" />
|
||||||
</remote-repository>
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="maven" />
|
||||||
|
<option name="name" value="maven" />
|
||||||
|
<option name="url" value="https://jitpack.io" />
|
||||||
|
</remote-repository>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
7
.idea/mobibot.iml
generated
Normal file
7
.idea/mobibot.iml
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module version="4">
|
||||||
|
<component name="SonarLintModuleSettings">
|
||||||
|
<option name="idePathPrefix" value="bin/main" />
|
||||||
|
<option name="sqPathPrefix" value="src/main/kotlin" />
|
||||||
|
</component>
|
||||||
|
</module>
|
28
build.gradle
28
build.gradle
|
@ -2,12 +2,12 @@ plugins {
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'com.github.ben-manes.versions' version '0.39.0'
|
id 'com.github.ben-manes.versions' version '0.39.0'
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'io.gitlab.arturbosch.detekt' version '1.18.1'
|
id 'io.gitlab.arturbosch.detekt' version '1.19.0-RC1'
|
||||||
id 'jacoco'
|
id 'jacoco'
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'net.thauvin.erik.gradle.semver' version '1.0.4'
|
id 'net.thauvin.erik.gradle.semver' version '1.0.4'
|
||||||
id 'org.jetbrains.kotlin.jvm' version '1.6.0-RC'
|
id 'org.jetbrains.kotlin.jvm' version '1.6.0-RC2'
|
||||||
id 'org.jetbrains.kotlin.kapt' version '1.6.0-RC'
|
id 'org.jetbrains.kotlin.kapt' version '1.6.0-RC2'
|
||||||
id 'org.sonarqube' version '3.3'
|
id 'org.sonarqube' version '3.3'
|
||||||
id 'pmd'
|
id 'pmd'
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,13 @@ mainClassName = packageName + '.Mobibot'
|
||||||
|
|
||||||
ext.versions = [
|
ext.versions = [
|
||||||
log4j: '2.14.1',
|
log4j: '2.14.1',
|
||||||
pmd : '6.35.0',
|
pmd : '6.40.0',
|
||||||
]
|
]
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +36,13 @@ dependencies {
|
||||||
kapt(semverProcessor)
|
kapt(semverProcessor)
|
||||||
compileOnly(semverProcessor)
|
compileOnly(semverProcessor)
|
||||||
|
|
||||||
implementation 'pircbot:pircbot:1.5.0'
|
implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT'
|
||||||
compileOnly 'pircbot:pircbot:1.5.0:sources'
|
|
||||||
|
implementation 'org.apache.commons:commons-text:1.9'
|
||||||
|
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||||
|
implementation 'org.slf4j:slf4j-api:1.7.32'
|
||||||
|
implementation 'commons-codec:commons-codec:1.15'
|
||||||
|
implementation 'com.google.guava:guava:31.0.1-jre'
|
||||||
|
|
||||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
|
||||||
|
@ -44,11 +50,12 @@ dependencies {
|
||||||
implementation "org.apache.logging.log4j:log4j-api:$versions.log4j"
|
implementation "org.apache.logging.log4j:log4j-api:$versions.log4j"
|
||||||
implementation "org.apache.logging.log4j:log4j-core:$versions.log4j"
|
implementation "org.apache.logging.log4j:log4j-core:$versions.log4j"
|
||||||
implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j"
|
implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j"
|
||||||
implementation 'commons-cli:commons-cli:1.4'
|
|
||||||
|
implementation 'commons-cli:commons-cli:1.5.0'
|
||||||
implementation 'commons-net:commons-net:3.8.0'
|
implementation 'commons-net:commons-net:3.8.0'
|
||||||
|
|
||||||
implementation 'com.rometools:rome:1.16.0'
|
implementation 'com.rometools:rome:1.16.0'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
|
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
|
||||||
implementation 'net.aksingh:owm-japis:2.5.3.0'
|
implementation 'net.aksingh:owm-japis:2.5.3.0'
|
||||||
implementation 'net.objecthunter:exp4j:0.4.8'
|
implementation 'net.objecthunter:exp4j:0.4.8'
|
||||||
|
|
||||||
|
@ -59,7 +66,9 @@ dependencies {
|
||||||
implementation 'org.jsoup:jsoup:1.14.3'
|
implementation 'org.jsoup:jsoup:1.14.3'
|
||||||
implementation 'org.twitter4j:twitter4j-core:4.0.7'
|
implementation 'org.twitter4j:twitter4j-core:4.0.7'
|
||||||
|
|
||||||
testImplementation 'org.assertj:assertj-core:3.21.0'
|
testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25'
|
||||||
|
// testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0'
|
||||||
|
// testImplementation "org.mockito:mockito-core:4.0.0"
|
||||||
testImplementation 'org.testng:testng:7.4.0'
|
testImplementation 'org.testng:testng:7.4.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +126,7 @@ jar {
|
||||||
manifest.attributes('Main-Class': mainClassName,
|
manifest.attributes('Main-Class': mainClassName,
|
||||||
'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/'))
|
'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/'))
|
||||||
archiveVersion.set("")
|
archiveVersion.set("")
|
||||||
|
exclude('log4j2.xml')
|
||||||
}
|
}
|
||||||
|
|
||||||
clean {
|
clean {
|
||||||
|
|
|
@ -2,54 +2,76 @@
|
||||||
<SmellBaseline>
|
<SmellBaseline>
|
||||||
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
||||||
<CurrentIssues>
|
<CurrentIssues>
|
||||||
<ID>ComplexMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID>
|
<ID>ComplexMethod:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID>
|
||||||
<ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
<ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||||
<ID>LongMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID>
|
<ID>LongMethod:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID>
|
||||||
<ID>LongMethod:Mobibot.kt$Mobibot.Companion$ @JvmStatic fun main(args: Array<String>)</ID>
|
<ID>LongMethod:Mobibot.kt$Mobibot.Companion$@Throws(Exception::class) @JvmStatic fun main(args: Array<String>)</ID>
|
||||||
<ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID>
|
<ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: 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>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||||
<ID>LongParameterList:Addons.kt$Addons$(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID>
|
<ID>LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent )</ID>
|
||||||
<ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID>
|
|
||||||
<ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID>
|
|
||||||
<ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID>
|
|
||||||
<ID>LongParameterList:Mobibot.kt$Mobibot$( nick: String, list: List<String>, maxPerLine: Int, separator: String = " ", isPrivate: Boolean, isBold: Boolean = false, isIndent: Boolean = false )</ID>
|
|
||||||
<ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID>
|
<ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID>
|
||||||
|
<ID>MagicNumber:Comment.kt$Comment$3</ID>
|
||||||
|
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID>
|
||||||
|
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID>
|
||||||
|
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID>
|
||||||
|
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID>
|
||||||
|
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID>
|
||||||
|
<ID>MagicNumber:Cycle.kt$Cycle$10</ID>
|
||||||
|
<ID>MagicNumber:Cycle.kt$Cycle$1000L</ID>
|
||||||
|
<ID>MagicNumber:Ignore.kt$Ignore$8</ID>
|
||||||
|
<ID>MagicNumber:Mobibot.kt$Mobibot$8</ID>
|
||||||
|
<ID>MagicNumber:Modules.kt$Modules$7</ID>
|
||||||
|
<ID>MagicNumber:Recap.kt$Recap.Companion$10</ID>
|
||||||
|
<ID>MagicNumber:StockQuote.kt$StockQuote.Companion$10</ID>
|
||||||
|
<ID>MagicNumber:Tell.kt$Tell$50</ID>
|
||||||
|
<ID>MagicNumber:Tell.kt$Tell$7</ID>
|
||||||
|
<ID>MagicNumber:Twitter.kt$Twitter$1000L</ID>
|
||||||
|
<ID>MagicNumber:Twitter.kt$Twitter$60L</ID>
|
||||||
|
<ID>MagicNumber:TwitterOAuth.kt$TwitterOAuth$401</ID>
|
||||||
|
<ID>MagicNumber:Users.kt$Users$8</ID>
|
||||||
|
<ID>MagicNumber:Utils.kt$Utils$30</ID>
|
||||||
|
<ID>MagicNumber:Utils.kt$Utils$365</ID>
|
||||||
|
<ID>MagicNumber:Utils.kt$Utils$7</ID>
|
||||||
|
<ID>MagicNumber:View.kt$View$6</ID>
|
||||||
|
<ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID>
|
||||||
|
<ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID>
|
||||||
|
<ID>MagicNumber:Weather2.kt$Weather2.Companion$5</ID>
|
||||||
|
<ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID>
|
||||||
|
<ID>MagicNumber:WorldTime.kt$WorldTime$14</ID>
|
||||||
|
<ID>MagicNumber:WorldTime.kt$WorldTime$4</ID>
|
||||||
|
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3</ID>
|
||||||
|
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID>
|
||||||
|
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID>
|
||||||
|
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID>
|
||||||
<ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID>
|
<ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID>
|
||||||
<ID>NestedBlockDepth:Calc.kt$Calc$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID>
|
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID>
|
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID>
|
||||||
<ID>NestedBlockDepth:CryptoPrices.kt$CryptoPrices$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID>
|
|
||||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID>
|
|
||||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter$override fun helpResponse(sender: String, isPrivate: Boolean): Boolean</ID>
|
|
||||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @Suppress("MagicNumber") @JvmStatic fun convertCurrency(query: String): Message</ID>
|
|
||||||
<ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String</ID>
|
|
||||||
<ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID>
|
|
||||||
<ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr$// Daily backup private fun dailyBackup( bot: Mobibot, history: MutableList<String> )</ID>
|
|
||||||
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID>
|
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID>
|
||||||
<ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID>
|
<ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID>
|
||||||
<ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID>
|
<ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID>
|
||||||
<ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID>
|
<ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID>
|
||||||
<ID>NestedBlockDepth:Mobibot.kt$Mobibot$ fun connect()</ID>
|
<ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
<ID>NestedBlockDepth:StockQuote.kt$StockQuote$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID>
|
<ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
|
<ID>NestedBlockDepth:Posting.kt$Posting$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
<ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID>
|
<ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID>
|
||||||
<ID>NestedBlockDepth:Tell.kt$Tell$ @JvmOverloads fun send(nickname: String, isMessage: Boolean = false)</ID>
|
<ID>NestedBlockDepth:Tell.kt$Tell$ fun send(event: GenericUserEvent)</ID>
|
||||||
<ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID>
|
<ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
<ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?, logger: Logger)</ID>
|
<ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun load(file: String): List<TellMessage></ID>
|
||||||
|
<ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?)</ID>
|
||||||
<ID>NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$ @Throws(TwitterException::class, IOException::class) @JvmStatic fun main(args: Array<String>)</ID>
|
<ID>NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$ @Throws(TwitterException::class, IOException::class) @JvmStatic fun main(args: Array<String>)</ID>
|
||||||
<ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID>
|
<ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
<ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
<ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||||
<ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID>
|
<ID>ReturnCount:Addons.kt$Addons$ fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean</ID>
|
||||||
<ID>ReturnCount:Addons.kt$Addons$ fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID>
|
<ID>ReturnCount:Addons.kt$Addons$ fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean</ID>
|
||||||
<ID>ReturnCount:Addons.kt$Addons$ fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean</ID>
|
<ID>ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$ fun ModuleException.sanitize(vararg sanitize: String): ModuleException</ID>
|
||||||
|
<ID>SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException</ID>
|
||||||
|
<ID>SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException</ID>
|
||||||
<ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID>
|
<ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID>
|
||||||
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID>
|
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID>
|
||||||
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID>
|
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID>
|
||||||
<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>TooGenericExceptionCaught:CryptoPrices.kt$CryptoPrices$e: Exception</ID>
|
|
||||||
<ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID>
|
|
||||||
<ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: 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>
|
||||||
<ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID>
|
|
||||||
<ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID>
|
<ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID>
|
||||||
</CurrentIssues>
|
</CurrentIssues>
|
||||||
</SmellBaseline>
|
</SmellBaseline>
|
||||||
|
|
|
@ -1,16 +1,38 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Configuration status="WARN">
|
<Configuration status="warn">
|
||||||
<Appenders>
|
<Appenders>
|
||||||
<Console name="stderr" target="SYSTEM_ERR">
|
<Console name="stderr" target="SYSTEM_ERR">
|
||||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||||
</Console>
|
</Console>
|
||||||
|
<Console name="input" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="%d{UNIX_MILLIS} %msg%n"/>
|
||||||
|
<Filters>
|
||||||
|
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||||
|
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||||
|
</Filters>
|
||||||
|
</Console>
|
||||||
|
<Console name="output" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="%d{UNIX_MILLIS} >>>%msg%n"/>
|
||||||
|
<Filters>
|
||||||
|
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||||
|
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||||
|
</Filters>
|
||||||
|
</Console>
|
||||||
</Appenders>
|
</Appenders>
|
||||||
<Loggers>
|
<Loggers>
|
||||||
<Logger name="net.thauvin.erik.mobibot" level="warn" additivity="false">
|
<Root level="warn" additivity="false">
|
||||||
<AppenderRef ref="stderr"/>
|
|
||||||
</Logger>
|
|
||||||
<Root level="error">
|
|
||||||
<AppenderRef ref="stderr"/>
|
<AppenderRef ref="stderr"/>
|
||||||
</Root>
|
</Root>
|
||||||
|
<logger level="debug" name="org.pircbotx.InputParser" additivity="false">
|
||||||
|
<appender-ref ref="input"/>
|
||||||
|
<appender-ref ref="stderr" level="warn"/>
|
||||||
|
</logger>
|
||||||
|
<logger level="debug" name="org.pircbotx.output.OutputRaw" additivity="false">
|
||||||
|
<appender-ref ref="output"/>
|
||||||
|
<appender-ref ref="stderr" level="warn"/>
|
||||||
|
</logger>
|
||||||
|
<logger level="warn" name="net.thauvin.erik.mobibot" additivity="false">
|
||||||
|
<appender-ref ref="stderr"/>
|
||||||
|
</logger>
|
||||||
</Loggers>
|
</Loggers>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
|
|
|
@ -3,9 +3,13 @@ server=irc.freenode.net
|
||||||
#port=6667
|
#port=6667
|
||||||
login=mobibot
|
login=mobibot
|
||||||
nick=mobibot
|
nick=mobibot
|
||||||
|
#realname=mobibot
|
||||||
|
|
||||||
|
# Die command password, if any
|
||||||
|
#die=changeme
|
||||||
|
|
||||||
# NickServ password
|
# NickServ password
|
||||||
ident=changepwd
|
ident=changeme
|
||||||
#ident-nick=nickserv
|
#ident-nick=nickserv
|
||||||
#ident-msg=IDENTIFY changepwd
|
#ident-msg=IDENTIFY changepwd
|
||||||
|
|
||||||
|
@ -14,7 +18,6 @@ ignore=chanserv,nickserv
|
||||||
tags=mobile mobitopia
|
tags=mobile mobitopia
|
||||||
tags-keywords=android ios apple google
|
tags-keywords=android ios apple google
|
||||||
|
|
||||||
weblog=http://www.mobitopia.org/
|
|
||||||
feed=http://www.mobitopia.org/rss.xml
|
feed=http://www.mobitopia.org/rss.xml
|
||||||
backlogs=http://www.mobitopia.org/mobibot/logs
|
backlogs=http://www.mobitopia.org/mobibot/logs
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.modules;
|
package net.thauvin.erik.mobibot.modules;
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot;
|
|
||||||
import net.thauvin.erik.mobibot.Utils;
|
import net.thauvin.erik.mobibot.Utils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
@ -66,8 +66,8 @@ public final class War extends AbstractModule {
|
||||||
/**
|
/**
|
||||||
* The default constructor.
|
* The default constructor.
|
||||||
*/
|
*/
|
||||||
public War(final Mobibot bot) {
|
public War() {
|
||||||
super(bot);
|
super();
|
||||||
|
|
||||||
commands.add(WAR_CMD);
|
commands.add(WAR_CMD);
|
||||||
|
|
||||||
|
@ -79,10 +79,8 @@ public final class War extends AbstractModule {
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void commandResponse(@NotNull final String sender,
|
public void commandResponse(@NotNull final String channel, @NotNull final String cmd, @NotNull final String args,
|
||||||
@NotNull final String cmd,
|
@NotNull final GenericMessageEvent event) {
|
||||||
@NotNull final String args,
|
|
||||||
final boolean isPrivate) {
|
|
||||||
int i;
|
int i;
|
||||||
int y;
|
int y;
|
||||||
|
|
||||||
|
@ -90,20 +88,20 @@ public final class War extends AbstractModule {
|
||||||
i = RANDOM.nextInt(HEARTS.length);
|
i = RANDOM.nextInt(HEARTS.length);
|
||||||
y = RANDOM.nextInt(HEARTS.length);
|
y = RANDOM.nextInt(HEARTS.length);
|
||||||
|
|
||||||
getBot().send(sender + " drew: " + DECK[RANDOM.nextInt(DECK.length)][i]);
|
event.respond("you drew " + DECK[RANDOM.nextInt(DECK.length)][i]);
|
||||||
getBot().action("drew: " + DECK[RANDOM.nextInt(DECK.length)][y]);
|
event.getBot().sendIRC().action(channel, "drew " + DECK[RANDOM.nextInt(DECK.length)][y]);
|
||||||
|
|
||||||
if (i != y) {
|
if (i != y) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBot().send("This means " + bold("WAR") + '!');
|
event.respond("This means " + bold("WAR") + '!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < y) {
|
if (i < y) {
|
||||||
getBot().action("lost.");
|
event.getBot().sendIRC().action(channel, "lost.");
|
||||||
} else {
|
} else {
|
||||||
getBot().action("wins.");
|
event.getBot().sendIRC().action(channel, "wins.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@ package net.thauvin.erik.mobibot
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||||
import net.thauvin.erik.mobibot.modules.AbstractModule
|
import net.thauvin.erik.mobibot.modules.AbstractModule
|
||||||
|
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,7 +79,7 @@ class Addons {
|
||||||
if (isEnabled()) {
|
if (isEnabled()) {
|
||||||
commands.add(this)
|
commands.add(this)
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
if (isOp) {
|
if (isOpOnly) {
|
||||||
ops.add(name)
|
ops.add(name)
|
||||||
} else {
|
} else {
|
||||||
names.add(name)
|
names.add(name)
|
||||||
|
@ -90,17 +92,18 @@ class Addons {
|
||||||
/**
|
/**
|
||||||
* Execute a command or module.
|
* Execute a command or module.
|
||||||
*/
|
*/
|
||||||
fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean {
|
fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean {
|
||||||
for (command in commands) {
|
val cmds = if (event is PrivateMessageEvent) commands else commands.filter { it.isPublic }
|
||||||
|
for (command in cmds) {
|
||||||
if (command.name.startsWith(cmd)) {
|
if (command.name.startsWith(cmd)) {
|
||||||
command.commandResponse(sender, login, args, isOp, isPrivate)
|
command.commandResponse(channel, args, event)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val mods = if (isPrivate) modules.filter { it.isPrivateMsgEnabled } else modules
|
val mods = if (event is PrivateMessageEvent) modules.filter { it.isPrivateMsgEnabled } else modules
|
||||||
for (module in mods) {
|
for (module in mods) {
|
||||||
if (module.commands.contains(cmd)) {
|
if (module.commands.contains(cmd)) {
|
||||||
module.commandResponse(sender, cmd, args, isPrivate)
|
module.commandResponse(channel, cmd, args, event)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,10 +113,10 @@ class Addons {
|
||||||
/**
|
/**
|
||||||
* Match a command.
|
* Match a command.
|
||||||
*/
|
*/
|
||||||
fun match(sender: String, login: String, message: String, isOp: Boolean, isPrivate: Boolean): Boolean {
|
fun match(channel: String, event: GenericMessageEvent): Boolean {
|
||||||
for (command in commands) {
|
for (command in commands) {
|
||||||
if (command.matches(message)) {
|
if (command.matches(event.message)) {
|
||||||
command.commandResponse(sender, login, message, isOp, isPrivate)
|
command.commandResponse(channel, event.message, event)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,15 +126,15 @@ class Addons {
|
||||||
/**
|
/**
|
||||||
* Commands and Modules help.
|
* Commands and Modules help.
|
||||||
*/
|
*/
|
||||||
fun help(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): Boolean {
|
fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||||
for (command in commands) {
|
for (command in commands) {
|
||||||
if (command.isVisible && command.name.startsWith(topic)) {
|
if (command.isVisible && command.name.startsWith(topic)) {
|
||||||
return command.helpResponse(topic, sender, isOp, isPrivate)
|
return command.helpResponse(channel, topic, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (module in modules) {
|
for (module in modules) {
|
||||||
if (module.commands.contains(topic)) {
|
if (module.commands.contains(topic)) {
|
||||||
return module.helpResponse(sender, isPrivate)
|
return module.helpResponse(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -45,11 +45,6 @@ object Constants {
|
||||||
*/
|
*/
|
||||||
const val DEBUG_ARG = "debug"
|
const val DEBUG_ARG = "debug"
|
||||||
|
|
||||||
/**
|
|
||||||
* The debug command.
|
|
||||||
*/
|
|
||||||
const val DEBUG_CMD = "debug"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default IRC Port.
|
* Default IRC Port.
|
||||||
*/
|
*/
|
||||||
|
@ -58,12 +53,7 @@ object Constants {
|
||||||
/**
|
/**
|
||||||
* Default IRC Server.
|
* Default IRC Server.
|
||||||
*/
|
*/
|
||||||
const val DEFAULT_SERVER = "irc.freenode.net"
|
const val DEFAULT_SERVER = "irc.libera.chat"
|
||||||
|
|
||||||
/**
|
|
||||||
* The die command.
|
|
||||||
*/
|
|
||||||
const val DIE_CMD = "die"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Help command line argument.
|
* Help command line argument.
|
||||||
|
@ -75,11 +65,6 @@ object Constants {
|
||||||
*/
|
*/
|
||||||
const val HELP_CMD = "help"
|
const val HELP_CMD = "help"
|
||||||
|
|
||||||
/**
|
|
||||||
* The kill command.
|
|
||||||
*/
|
|
||||||
const val KILL_CMD = "kill"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The link command.
|
* The link command.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -36,38 +36,36 @@ import com.rometools.rome.io.SyndFeedInput
|
||||||
import com.rometools.rome.io.XmlReader
|
import com.rometools.rome.io.XmlReader
|
||||||
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.sendMessage
|
||||||
|
import net.thauvin.erik.mobibot.entries.FeedsMgr
|
||||||
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.NoticeMessage
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads an RSS feed.
|
* Reads an RSS feed.
|
||||||
*/
|
*/
|
||||||
class FeedReader(
|
class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable {
|
||||||
// Bot
|
private val logger: Logger = LoggerFactory.getLogger(FeedsMgr::class.java)
|
||||||
private val bot: Mobibot,
|
|
||||||
// Nick of the person who sent the message
|
|
||||||
private val sender: String,
|
|
||||||
// URL to fetch
|
|
||||||
private val url: String
|
|
||||||
) : Runnable {
|
|
||||||
/**
|
/**
|
||||||
* Fetches the Feed's items.
|
* Fetches the Feed's items.
|
||||||
*/
|
*/
|
||||||
override fun run() {
|
override fun run() {
|
||||||
with(bot) {
|
|
||||||
try {
|
try {
|
||||||
readFeed(url).forEach {
|
readFeed(url).forEach {
|
||||||
send(sender, it)
|
event.sendMessage("", it)
|
||||||
}
|
}
|
||||||
} catch (e: FeedException) {
|
} catch (e: FeedException) {
|
||||||
if (logger.isDebugEnabled) logger.debug("Unable to parse the feed at $url", e)
|
if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e)
|
||||||
send(sender, "An error has occurred while parsing the feed: ${e.message}", false)
|
event.sendMessage("An error has occurred while parsing the feed: ${e.message}")
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed at $url", e)
|
if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e)
|
||||||
send(sender, "An error has occurred while fetching the feed: ${e.message}", false)
|
event.sendMessage("An error has occurred while fetching the feed: ${e.message}")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,11 +79,11 @@ class FeedReader(
|
||||||
val feed = input.build(reader)
|
val feed = input.build(reader)
|
||||||
val items = feed.entries
|
val items = feed.entries
|
||||||
if (items.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
messages.add(PublicMessage("There is currently nothing to view."))
|
messages.add(NoticeMessage("There is currently nothing to view."))
|
||||||
} else {
|
} else {
|
||||||
items.take(maxItems).forEach {
|
items.take(maxItems).forEach {
|
||||||
messages.add(PublicMessage(it.title))
|
messages.add(NoticeMessage(it.title))
|
||||||
messages.add(PublicMessage(helpFormat(green(it.link), false)))
|
messages.add(NoticeMessage(helpFormat(green(it.link), false)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,23 +29,21 @@
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot
|
package net.thauvin.erik.mobibot
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
||||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.colorize
|
|
||||||
import net.thauvin.erik.mobibot.Utils.getIntProperty
|
import net.thauvin.erik.mobibot.Utils.getIntProperty
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendList
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||||
import net.thauvin.erik.mobibot.Utils.today
|
|
||||||
import net.thauvin.erik.mobibot.commands.AddLog
|
|
||||||
import net.thauvin.erik.mobibot.commands.ChannelFeed
|
import net.thauvin.erik.mobibot.commands.ChannelFeed
|
||||||
import net.thauvin.erik.mobibot.commands.Cycle
|
import net.thauvin.erik.mobibot.commands.Cycle
|
||||||
import net.thauvin.erik.mobibot.commands.Debug
|
|
||||||
import net.thauvin.erik.mobibot.commands.Die
|
import net.thauvin.erik.mobibot.commands.Die
|
||||||
import net.thauvin.erik.mobibot.commands.Ignore
|
import net.thauvin.erik.mobibot.commands.Ignore
|
||||||
import net.thauvin.erik.mobibot.commands.Info
|
import net.thauvin.erik.mobibot.commands.Info
|
||||||
import net.thauvin.erik.mobibot.commands.Kill
|
|
||||||
import net.thauvin.erik.mobibot.commands.Me
|
import net.thauvin.erik.mobibot.commands.Me
|
||||||
import net.thauvin.erik.mobibot.commands.Modules
|
import net.thauvin.erik.mobibot.commands.Modules
|
||||||
import net.thauvin.erik.mobibot.commands.Msg
|
import net.thauvin.erik.mobibot.commands.Msg
|
||||||
|
@ -61,8 +59,6 @@ import net.thauvin.erik.mobibot.commands.links.Posting
|
||||||
import net.thauvin.erik.mobibot.commands.links.Tags
|
import net.thauvin.erik.mobibot.commands.links.Tags
|
||||||
import net.thauvin.erik.mobibot.commands.links.View
|
import net.thauvin.erik.mobibot.commands.links.View
|
||||||
import net.thauvin.erik.mobibot.commands.tell.Tell
|
import net.thauvin.erik.mobibot.commands.tell.Tell
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesMgr
|
|
||||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
|
||||||
import net.thauvin.erik.mobibot.modules.Calc
|
import net.thauvin.erik.mobibot.modules.Calc
|
||||||
import net.thauvin.erik.mobibot.modules.CryptoPrices
|
import net.thauvin.erik.mobibot.modules.CryptoPrices
|
||||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter
|
import net.thauvin.erik.mobibot.modules.CurrencyConverter
|
||||||
|
@ -73,12 +69,9 @@ import net.thauvin.erik.mobibot.modules.Lookup
|
||||||
import net.thauvin.erik.mobibot.modules.Ping
|
import net.thauvin.erik.mobibot.modules.Ping
|
||||||
import net.thauvin.erik.mobibot.modules.RockPaperScissors
|
import net.thauvin.erik.mobibot.modules.RockPaperScissors
|
||||||
import net.thauvin.erik.mobibot.modules.StockQuote
|
import net.thauvin.erik.mobibot.modules.StockQuote
|
||||||
import net.thauvin.erik.mobibot.modules.Twitter
|
|
||||||
import net.thauvin.erik.mobibot.modules.War
|
import net.thauvin.erik.mobibot.modules.War
|
||||||
import net.thauvin.erik.mobibot.modules.Weather2
|
import net.thauvin.erik.mobibot.modules.Weather2
|
||||||
import net.thauvin.erik.mobibot.modules.WorldTime
|
import net.thauvin.erik.mobibot.modules.WorldTime
|
||||||
import net.thauvin.erik.mobibot.msg.Message
|
|
||||||
import net.thauvin.erik.pinboard.PinboardPoster
|
|
||||||
import net.thauvin.erik.semver.Version
|
import net.thauvin.erik.semver.Version
|
||||||
import org.apache.commons.cli.CommandLine
|
import org.apache.commons.cli.CommandLine
|
||||||
import org.apache.commons.cli.CommandLineParser
|
import org.apache.commons.cli.CommandLineParser
|
||||||
|
@ -87,10 +80,19 @@ import org.apache.commons.cli.HelpFormatter
|
||||||
import org.apache.commons.cli.Option
|
import org.apache.commons.cli.Option
|
||||||
import org.apache.commons.cli.Options
|
import org.apache.commons.cli.Options
|
||||||
import org.apache.commons.cli.ParseException
|
import org.apache.commons.cli.ParseException
|
||||||
import org.apache.logging.log4j.Level
|
import org.pircbotx.Configuration
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.pircbotx.PircBotX
|
||||||
import org.apache.logging.log4j.Logger
|
import org.pircbotx.hooks.ListenerAdapter
|
||||||
import org.jibble.pircbot.PircBot
|
import org.pircbotx.hooks.events.ActionEvent
|
||||||
|
import org.pircbotx.hooks.events.DisconnectEvent
|
||||||
|
import org.pircbotx.hooks.events.JoinEvent
|
||||||
|
import org.pircbotx.hooks.events.MessageEvent
|
||||||
|
import org.pircbotx.hooks.events.NickChangeEvent
|
||||||
|
import org.pircbotx.hooks.events.PartEvent
|
||||||
|
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
@ -100,427 +102,139 @@ import java.io.PrintStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
import java.util.Timer
|
|
||||||
import java.util.logging.ConsoleHandler
|
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements the #mobitopia bot.
|
|
||||||
*/
|
|
||||||
@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt")
|
@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt")
|
||||||
class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Properties) : PircBot() {
|
class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() {
|
||||||
|
// The bot configuration.
|
||||||
|
private val config: Configuration
|
||||||
|
|
||||||
// Commands and Modules
|
// Commands and Modules
|
||||||
private val addons = Addons()
|
private val addons = Addons()
|
||||||
|
|
||||||
|
// Tell module
|
||||||
|
private val tell: Tell
|
||||||
|
|
||||||
/** Main channel. */
|
/** Main channel. */
|
||||||
val channel: String
|
val channel: String
|
||||||
|
|
||||||
// IRC port
|
|
||||||
private val ircPort: Int
|
|
||||||
|
|
||||||
/** IRC server. */
|
|
||||||
val ircServer: String
|
|
||||||
|
|
||||||
/** Logger. */
|
/** Logger. */
|
||||||
val logger: Logger = LogManager.getLogger(Mobibot::class.java)
|
val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java)
|
||||||
|
|
||||||
/** Logger default level. */
|
|
||||||
val loggerLevel: Level
|
|
||||||
|
|
||||||
/** Log directory. */
|
|
||||||
val logsDir: String
|
|
||||||
|
|
||||||
// Pinboard posts handler
|
|
||||||
private val pinboard: PinboardPoster = PinboardPoster()
|
|
||||||
|
|
||||||
/** Tell command. */
|
|
||||||
val tell: Tell
|
|
||||||
|
|
||||||
/** Today's date. */
|
|
||||||
val today = today()
|
|
||||||
|
|
||||||
/** Twitter module. */
|
|
||||||
val twitter: Twitter
|
|
||||||
|
|
||||||
/** The backlogs URL. */
|
|
||||||
val backlogsUrl: String
|
|
||||||
|
|
||||||
// Ident message
|
|
||||||
private val identMsg: String
|
|
||||||
|
|
||||||
// Ident nick
|
|
||||||
private val identNick: String
|
|
||||||
|
|
||||||
// NickServ ident password
|
|
||||||
private val identPwd: String
|
|
||||||
|
|
||||||
// Is pinboard enabled?
|
|
||||||
private var isPinboardEnabled = false
|
|
||||||
|
|
||||||
/** Timer. */
|
|
||||||
val timer = Timer(true)
|
|
||||||
|
|
||||||
/** Weblog URL */
|
|
||||||
val weblogUrl: String
|
|
||||||
|
|
||||||
/** The current channel name. */
|
|
||||||
private val channelName: String
|
|
||||||
get() = channel.substring(1)
|
|
||||||
|
|
||||||
/** The enabled modules names. */
|
|
||||||
val modulesNames: List<String>
|
|
||||||
get() = addons.modulesNames
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an action to the current channel.
|
|
||||||
*/
|
|
||||||
fun action(action: String) {
|
|
||||||
action(channel, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an action to the channel.
|
|
||||||
*/
|
|
||||||
private fun action(channel: String, action: String) {
|
|
||||||
if (channel.isNotBlank() && action.isNotBlank()) {
|
|
||||||
sendAction(channel, action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds pin on pinboard.
|
|
||||||
*/
|
|
||||||
fun addPin(entry: EntryLink) {
|
|
||||||
if (isPinboardEnabled) {
|
|
||||||
PinboardUtils.addPin(pinboard, ircServer, entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to the server and joins the channel.
|
* Connects to the server and joins the channel.
|
||||||
*/
|
*/
|
||||||
fun connect() {
|
fun connect() {
|
||||||
try {
|
PircBotX(config).startBot()
|
||||||
connect(ircServer, ircPort)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("Unable to connect to $ircServer, will try again.", e)
|
|
||||||
}
|
|
||||||
var retries = 0
|
|
||||||
while (retries++ < MAX_RECONNECT && !isConnected) {
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
sleep(10)
|
|
||||||
try {
|
|
||||||
connect(ircServer, ircPort)
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
if (retries == MAX_RECONNECT) {
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("Unable to reconnect to $ircServer, after $MAX_RECONNECT retries.", ex)
|
|
||||||
}
|
|
||||||
System.err.println("An error has occurred while reconnecting; ${ex.message}")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
identify()
|
|
||||||
joinChannel()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes pin on pinboard.
|
|
||||||
*/
|
|
||||||
fun deletePin(index: Int, entry: EntryLink) {
|
|
||||||
if (isPinboardEnabled) {
|
|
||||||
PinboardUtils.deletePin(pinboard, entry)
|
|
||||||
}
|
|
||||||
if (twitter.isAutoPost) {
|
|
||||||
twitter.removeEntry(index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responds with the default help.
|
* Responds with the default help.
|
||||||
*/
|
*/
|
||||||
@Suppress("MagicNumber")
|
private fun helpDefault(event: GenericMessageEvent) {
|
||||||
fun helpDefault(sender: String, isOp: Boolean, isPrivate: Boolean) {
|
event.sendMessage("Type a URL on $channel to post it.")
|
||||||
send(sender, "Type a URL on $channel to post it.", isPrivate)
|
event.sendMessage("For more information on a specific command, type:")
|
||||||
send(sender, "For more information on a specific command, type:", isPrivate)
|
event.sendMessage(
|
||||||
send(
|
Utils.helpFormat(
|
||||||
sender,
|
Utils.buildCmdSyntax(
|
||||||
helpFormat(buildCmdSyntax("%c ${Constants.HELP_CMD} <command>", nick, isPrivate)),
|
"%c ${Constants.HELP_CMD} <command>",
|
||||||
isPrivate
|
event.bot().nick,
|
||||||
|
event is PrivateMessageEvent
|
||||||
)
|
)
|
||||||
send(sender, "The commands are:", isPrivate)
|
),
|
||||||
sendList(sender, addons.names, 8, isPrivate = isPrivate, isBold = true, isIndent = true)
|
)
|
||||||
if (isOp) {
|
event.sendMessage("The commands are:")
|
||||||
send(sender, "The op commands are:", isPrivate)
|
event.sendList(addons.names, 8, isBold = true, isIndent = true)
|
||||||
sendList(sender, addons.ops, 8, isPrivate = isPrivate, isBold = true, isIndent = true)
|
if (isChannelOp(channel, event)) {
|
||||||
|
event.sendMessage("The op commands are:")
|
||||||
|
event.sendList(addons.ops, 8, isBold = true, isIndent = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responds with the default, commands or modules help.
|
* Responds with the default, commands or modules help.
|
||||||
*/
|
*/
|
||||||
private fun helpResponse(sender: String, topic: String, isPrivate: Boolean) {
|
private fun helpResponse(event: GenericMessageEvent, topic: String) {
|
||||||
val isOp = isOp(sender)
|
if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) {
|
||||||
if (topic.isBlank() || !addons.help(sender, topic.lowercase().trim(), isOp, isPrivate)) {
|
helpDefault(event)
|
||||||
helpDefault(sender, isOp, isPrivate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun onAction(event: ActionEvent?) {
|
||||||
* Identifies the bot.
|
if (channel == event?.channel?.name) {
|
||||||
*/
|
storeRecap(event.user!!.nick, event.action, true)
|
||||||
private fun identify() {
|
|
||||||
// Identify with NickServ
|
|
||||||
if (identPwd.isNotBlank()) {
|
|
||||||
identify(identPwd)
|
|
||||||
}
|
|
||||||
// Identify with a specified nick
|
|
||||||
if (identNick.isNotBlank() && identMsg.isNotBlank()) {
|
|
||||||
sendMessage(identNick, identMsg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun onDisconnect(event: DisconnectEvent?) {
|
||||||
* Returns {@code true} if the specified sender is an Op on the [channel][.ircChannel].
|
with(event!!.getBot<PircBotX>()) {
|
||||||
*/
|
LinksMgr.twitter.notification("$nick disconnected from irc://$serverHostname")
|
||||||
fun isOp(sender: String): Boolean {
|
|
||||||
for (user in getUsers(channel)) {
|
|
||||||
if (user.nick == sender) {
|
|
||||||
return user.isOp
|
|
||||||
}
|
}
|
||||||
}
|
LinksMgr.twitter.shutdown()
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun onPrivateMessage(event: PrivateMessageEvent?) {
|
||||||
* Joins the bot's channel.
|
if (logger.isTraceEnabled) logger.trace("<<< ${event!!.user!!.nick}: ${event.message}")
|
||||||
*/
|
val cmds = event!!.message.trim().split(" ".toRegex(), 2)
|
||||||
private fun joinChannel() {
|
val cmd = cmds[0].lowercase()
|
||||||
joinChannel(channel)
|
val args = if (cmds.size > 1) {
|
||||||
twitter.notification("$name ${ReleaseInfo.VERSION} has joined $channel")
|
cmds[1].trim()
|
||||||
|
} else ""
|
||||||
|
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 onDisconnect() {
|
override fun onJoin(event: JoinEvent?) {
|
||||||
if (weblogUrl.isNotBlank()) {
|
with(event!!.getBot<PircBotX>()) {
|
||||||
version = weblogUrl
|
if (event.user!!.nick == nick) {
|
||||||
|
LinksMgr.twitter.notification("$nick has joined ${event.channel.name} on irc://$serverHostname")
|
||||||
|
} else {
|
||||||
|
tell.send(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@Suppress("MagicNumber")
|
|
||||||
sleep(5)
|
|
||||||
connect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessage(
|
override fun onMessage(event: MessageEvent?) {
|
||||||
channel: String,
|
val sender = event!!.user!!.nick
|
||||||
sender: String,
|
val message = event.message
|
||||||
login: String,
|
tell.send(event)
|
||||||
hostname: String,
|
if (message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command>
|
||||||
message: String
|
if (logger.isTraceEnabled) logger.trace(">>> $sender: $message")
|
||||||
) {
|
|
||||||
if (logger.isDebugEnabled) logger.debug(">>> $sender: $message")
|
|
||||||
tell.send(sender, true)
|
|
||||||
if (message.matches("(?i)${Pattern.quote(nick)}:.*".toRegex())) { // mobibot: <command>
|
|
||||||
val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2)
|
val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2)
|
||||||
val cmd = cmds[0].lowercase()
|
val cmd = cmds[0].lowercase()
|
||||||
val args = if (cmds.size > 1) {
|
val args = if (cmds.size > 1) {
|
||||||
cmds[1].trim()
|
cmds[1].trim()
|
||||||
} else ""
|
} else ""
|
||||||
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
|
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
|
||||||
helpResponse(sender, args, false)
|
helpResponse(event, args)
|
||||||
} else {
|
} else {
|
||||||
// Execute module or command
|
// Execute module or command
|
||||||
addons.exec(sender, login, cmd, args, isOp(sender), false)
|
addons.exec(channel, cmd, args, event)
|
||||||
}
|
}
|
||||||
} else {
|
} else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc.
|
||||||
// Links, e.g.: https://www.example.com/ or L1: , etc.
|
if (logger.isTraceEnabled) logger.trace(">>> $sender: $message")
|
||||||
addons.match(sender, login, message, isOp(sender), false)
|
|
||||||
}
|
}
|
||||||
storeRecap(sender, message, false)
|
storeRecap(sender, message, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrivateMessage(
|
override fun onNickChange(event: NickChangeEvent?) {
|
||||||
sender: String,
|
tell.send(event!!)
|
||||||
login: String,
|
|
||||||
hostname: String,
|
|
||||||
message: String
|
|
||||||
) {
|
|
||||||
if (logger.isDebugEnabled) logger.debug(">>> $sender : $message")
|
|
||||||
val cmds = message.trim().split(" ".toRegex(), 2)
|
|
||||||
val cmd = cmds[0].lowercase()
|
|
||||||
val args = if (cmds.size > 1) {
|
|
||||||
cmds[1].trim()
|
|
||||||
} else ""
|
|
||||||
val isOp = isOp(sender)
|
|
||||||
if (cmd.startsWith(Constants.HELP_CMD)) { // help
|
|
||||||
helpResponse(sender, args, true)
|
|
||||||
} else if (isOp && Constants.KILL_CMD == cmd) { // kill
|
|
||||||
twitter.notification("$name killed by $sender on $channel")
|
|
||||||
sendRawLine("QUIT :Poof!")
|
|
||||||
exitProcess(0)
|
|
||||||
} else if (isOp && Constants.DIE_CMD == cmd) { // die
|
|
||||||
send("$sender has just signed my death sentence.")
|
|
||||||
timer.cancel()
|
|
||||||
twitter.shutdown()
|
|
||||||
twitter.notification("$name stopped by $sender on $channel")
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
sleep(3)
|
|
||||||
quitServer("The Bot Is Out There!")
|
|
||||||
exitProcess(0)
|
|
||||||
} else if (!addons.exec(sender, login, cmd, args, isOp, true)) { // Execute command or module
|
|
||||||
helpDefault(sender, isOp, true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAction(sender: String, login: String, hostname: String, target: String, action: String) {
|
override fun onPart(event: PartEvent?) {
|
||||||
if (channel == target) {
|
with(event!!.getBot<PircBotX>()) {
|
||||||
storeRecap(sender, action, true)
|
if (event.user!!.nick == nick) {
|
||||||
|
LinksMgr.twitter.notification("$nick has left ${event.channel.name} on irc://$serverHostname")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onJoin(channel: String, sender: String, login: String, hostname: String) {
|
|
||||||
tell.send(sender)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNickChange(oldNick: String, login: String, hostname: String, newNick: String) {
|
|
||||||
tell.send(newNick)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a private message or notice.
|
|
||||||
*/
|
|
||||||
fun send(sender: String, message: String?, isPrivate: Boolean) {
|
|
||||||
if (message != null && sender.isNotBlank()) {
|
|
||||||
if (isPrivate) {
|
|
||||||
if (logger.isDebugEnabled) logger.debug("Sending message to $sender : $message")
|
|
||||||
sendMessage(sender, message)
|
|
||||||
} else {
|
|
||||||
if (logger.isDebugEnabled) logger.debug("Sending notice to $sender: $message")
|
|
||||||
sendNotice(sender, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a notice to the channel.
|
|
||||||
*/
|
|
||||||
fun send(notice: String?) {
|
|
||||||
notice?.let { send(channel, it, false) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a message.
|
|
||||||
*/
|
|
||||||
fun send(who: String, message: Message) {
|
|
||||||
send(if (message.isNotice) who else channel, message.msg, message.color, message.isPrivate)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a message.
|
|
||||||
*/
|
|
||||||
fun send(who: String, message: String, color: String, isPrivate: Boolean) {
|
|
||||||
send(who, colorize(message, color), isPrivate)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a formatted commands/modules, etc. list.
|
|
||||||
*/
|
|
||||||
@JvmOverloads
|
|
||||||
fun sendList(
|
|
||||||
nick: String,
|
|
||||||
list: List<String>,
|
|
||||||
maxPerLine: Int,
|
|
||||||
separator: String = " ",
|
|
||||||
isPrivate: Boolean,
|
|
||||||
isBold: Boolean = false,
|
|
||||||
isIndent: Boolean = false
|
|
||||||
) {
|
|
||||||
var i = 0
|
|
||||||
while (i < list.size) {
|
|
||||||
send(
|
|
||||||
nick,
|
|
||||||
helpFormat(
|
|
||||||
list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""),
|
|
||||||
isBold,
|
|
||||||
isIndent
|
|
||||||
),
|
|
||||||
isPrivate
|
|
||||||
)
|
|
||||||
i += maxPerLine
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the pinboard authentication.
|
|
||||||
*/
|
|
||||||
private fun setPinboardAuth(apiToken: String) {
|
|
||||||
if (apiToken.isNotBlank()) {
|
|
||||||
pinboard.apiToken = apiToken
|
|
||||||
isPinboardEnabled = true
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
val consoleHandler = ConsoleHandler()
|
|
||||||
consoleHandler.level = java.util.logging.Level.FINE
|
|
||||||
pinboard.logger.addHandler(consoleHandler)
|
|
||||||
pinboard.logger.level = java.util.logging.Level.FINE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shutdown the bot.
|
|
||||||
*/
|
|
||||||
fun shutdown(sender: String, now: Boolean = false) {
|
|
||||||
if (now) { // kill
|
|
||||||
twitter.notification("$name killed by $sender on $channel")
|
|
||||||
sendRawLine("QUIT :Poof!")
|
|
||||||
} else {
|
|
||||||
timer.cancel()
|
|
||||||
twitter.shutdown()
|
|
||||||
twitter.notification("$name stopped by $sender on $channel")
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
sleep(3)
|
|
||||||
quitServer("The Bot Is Out There!")
|
|
||||||
}
|
|
||||||
exitProcess(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sleeps for the specified number of seconds.
|
|
||||||
*/
|
|
||||||
fun sleep(secs: Int) {
|
|
||||||
try {
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
Thread.sleep(secs * 1000L)
|
|
||||||
} catch (ignore: InterruptedException) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates pin on pinboard.
|
|
||||||
*/
|
|
||||||
fun updatePin(oldUrl: String, entry: EntryLink) {
|
|
||||||
if (isPinboardEnabled) {
|
|
||||||
PinboardUtils.updatePin(pinboard, ircServer, oldUrl, entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the bot's version.
|
|
||||||
*/
|
|
||||||
override fun onVersion(sourceNick: String, sourceLogin: String, sourceHostname: String, target: String) {
|
|
||||||
sendRawLine("NOTICE $sourceNick :\u0001VERSION ${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}\u0001")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Maximum number of times the bot will try to reconnect, if disconnected
|
@Throws(Exception::class)
|
||||||
private const val MAX_RECONNECT = 10
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Truth is Out There!
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
// Set up the command line options
|
// Set up the command line options
|
||||||
|
@ -593,7 +307,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
|
||||||
val stdout = PrintStream(
|
val stdout = PrintStream(
|
||||||
BufferedOutputStream(
|
BufferedOutputStream(
|
||||||
FileOutputStream(
|
FileOutputStream(
|
||||||
logsDir + channel.substring(1) + '.' + today() + ".log", true
|
logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true
|
||||||
)
|
)
|
||||||
), true
|
), true
|
||||||
)
|
)
|
||||||
|
@ -615,11 +329,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the bot
|
// Start the bot
|
||||||
val bot = Mobibot(nickname, channel, logsDir, p)
|
Mobibot(nickname, channel, logsDir, p).connect()
|
||||||
|
|
||||||
// Connect
|
|
||||||
bot.connect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -629,97 +340,93 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
|
||||||
* Initialize the bot.
|
* Initialize the bot.
|
||||||
*/
|
*/
|
||||||
init {
|
init {
|
||||||
System.getProperties().setProperty("sun.net.client.defaultConnectTimeout", Constants.CONNECT_TIMEOUT.toString())
|
|
||||||
System.getProperties().setProperty("sun.net.client.defaultReadTimeout", Constants.CONNECT_TIMEOUT.toString())
|
|
||||||
|
|
||||||
name = nickname
|
|
||||||
ircServer = p.getProperty("server", Constants.DEFAULT_SERVER)
|
|
||||||
ircPort = p.getIntProperty("port", Constants.DEFAULT_PORT)
|
|
||||||
this.channel = channel
|
this.channel = channel
|
||||||
logsDir = logsDirPath
|
val ircServer = p.getProperty("server", Constants.DEFAULT_SERVER)
|
||||||
|
config = Configuration.Builder().apply {
|
||||||
|
name = nickname
|
||||||
|
login = p.getProperty("login", nickname)
|
||||||
|
realName = p.getProperty("realname", nickname)
|
||||||
|
addServer(
|
||||||
|
ircServer,
|
||||||
|
p.getIntProperty("port", Constants.DEFAULT_PORT)
|
||||||
|
)
|
||||||
|
addAutoJoinChannel(channel)
|
||||||
|
addListener(this@Mobibot)
|
||||||
|
version = "${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}"
|
||||||
|
isAutoNickChange = true
|
||||||
|
val identPwd = p.getProperty("ident")
|
||||||
|
if (!identPwd.isNullOrBlank()) {
|
||||||
|
nickservPassword = identPwd
|
||||||
|
}
|
||||||
|
val identNick = p.getProperty("ident-nick")
|
||||||
|
if (!identNick.isNullOrBlank()) {
|
||||||
|
nickservNick = identNick
|
||||||
|
}
|
||||||
|
val identMsg = p.getProperty("ident-msg")
|
||||||
|
if (!identMsg.isNullOrBlank()) {
|
||||||
|
nickservCustomMessage = identMsg
|
||||||
|
}
|
||||||
|
isAutoReconnect = true
|
||||||
|
//socketConnectTimeout = Constants.CONNECT_TIMEOUT
|
||||||
|
//socketTimeout = Constants.CONNECT_TIMEOUT
|
||||||
|
//messageDelay = StaticDelay(500)
|
||||||
|
}.buildConfiguration()
|
||||||
|
|
||||||
// Store the default logger level
|
// Load the current entries
|
||||||
loggerLevel = logger.level
|
with(LinksMgr) {
|
||||||
|
entries.channel = channel
|
||||||
|
entries.ircServer = ircServer
|
||||||
|
entries.logsDir = logsDirPath
|
||||||
|
entries.backlogs = p.getProperty("backlogs", "")
|
||||||
|
entries.load()
|
||||||
|
|
||||||
setVerbose(true)
|
// Set up pinboard
|
||||||
setAutoNickChange(true)
|
pinboard.setApiToken(p.getProperty("pinboard-api-token", ""))
|
||||||
login = p.getProperty("login", name)
|
|
||||||
// Set the real name
|
|
||||||
version = ReleaseInfo.PROJECT
|
|
||||||
// setMessageDelay(1000);
|
|
||||||
|
|
||||||
// Set NICKSERV identification
|
|
||||||
identPwd = p.getProperty("ident", "")
|
|
||||||
identNick = p.getProperty("ident-nick", "")
|
|
||||||
identMsg = p.getProperty("ident-msg", "")
|
|
||||||
|
|
||||||
// Set the URLs
|
|
||||||
weblogUrl = p.getProperty("weblog", "")
|
|
||||||
backlogsUrl = p.getProperty("backlogs", weblogUrl).appendIfMissing('/')
|
|
||||||
|
|
||||||
// Load the current entries and backlogs, if any
|
|
||||||
try {
|
|
||||||
LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel)
|
|
||||||
if (logger.isDebugEnabled) logger.debug("Last feed: ${LinksMgr.startDate}")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (logger.isErrorEnabled) logger.error("An error occurred while loading the logs.", e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the pinboard authentication
|
|
||||||
setPinboardAuth(p.getProperty("pinboard-api-token", ""))
|
|
||||||
|
|
||||||
// Load the commands
|
// Load the commands
|
||||||
addons.add(AddLog(this), p)
|
addons.add(ChannelFeed(channel.removePrefix("#")), p)
|
||||||
addons.add(ChannelFeed(this, channelName), p)
|
addons.add(Comment(), p)
|
||||||
addons.add(Cycle(this), p)
|
addons.add(Cycle(), p)
|
||||||
addons.add(Die(this), p)
|
addons.add(Die(), p)
|
||||||
addons.add(Debug(this), p)
|
addons.add(Ignore(), p)
|
||||||
addons.add(Ignore(this), p)
|
addons.add(LinksMgr(), p)
|
||||||
addons.add(Info(this), p)
|
addons.add(Me(), p)
|
||||||
addons.add(Kill(this), p)
|
addons.add(Msg(), p)
|
||||||
addons.add(Me(this), p)
|
addons.add(Nick(), p)
|
||||||
addons.add(Modules(this), p)
|
addons.add(Posting(), p)
|
||||||
addons.add(Msg(this), p)
|
addons.add(Recap(), p)
|
||||||
addons.add(Nick(this), p)
|
addons.add(Say(), p)
|
||||||
addons.add(Recap(this), p)
|
addons.add(Tags(), p)
|
||||||
addons.add(Say(this), p)
|
|
||||||
addons.add(Users(this), p)
|
|
||||||
addons.add(Versions(this), p)
|
|
||||||
|
|
||||||
// Tell command
|
// Tell command
|
||||||
tell = Tell(this)
|
tell = Tell("${logsDirPath}${nickname}.ser")
|
||||||
addons.add(tell, p)
|
addons.add(tell, p)
|
||||||
|
|
||||||
// Load the links commands
|
addons.add(LinksMgr.twitter, p)
|
||||||
addons.add(Comment(this), p)
|
addons.add(Users(), p)
|
||||||
addons.add(Posting(this), p)
|
addons.add(Versions(), p)
|
||||||
addons.add(Tags(this), p)
|
addons.add(View(), p)
|
||||||
addons.add(LinksMgr(this), p)
|
|
||||||
addons.add(View(this), p)
|
|
||||||
|
|
||||||
// Load the modules
|
// Load the modules
|
||||||
addons.add(Calc(this), p)
|
addons.add(Calc(), p)
|
||||||
addons.add(CryptoPrices(this), p)
|
addons.add(CryptoPrices(), p)
|
||||||
addons.add(CurrencyConverter(this), p)
|
addons.add(CurrencyConverter(), p)
|
||||||
addons.add(Dice(this), p)
|
addons.add(Dice(), p)
|
||||||
addons.add(GoogleSearch(this), p)
|
addons.add(GoogleSearch(), p)
|
||||||
addons.add(Joke(this), p)
|
addons.add(Info(tell), p)
|
||||||
addons.add(Lookup(this), p)
|
addons.add(Joke(), p)
|
||||||
addons.add(Ping(this), p)
|
addons.add(Lookup(), p)
|
||||||
addons.add(RockPaperScissors(this), p)
|
addons.add(Modules(addons.modulesNames), p)
|
||||||
addons.add(StockQuote(this), p)
|
addons.add(Ping(), p)
|
||||||
addons.add(War(this), p)
|
addons.add(RockPaperScissors(), p)
|
||||||
addons.add(Weather2(this), p)
|
addons.add(StockQuote(), p)
|
||||||
addons.add(WorldTime(this), p)
|
addons.add(Weather2(), p)
|
||||||
|
addons.add(WorldTime(), p)
|
||||||
// Twitter module
|
addons.add(War(), p)
|
||||||
twitter = Twitter(this)
|
|
||||||
addons.add(twitter, p)
|
|
||||||
|
|
||||||
// Sort the addons
|
// Sort the addons
|
||||||
addons.sort()
|
addons.sort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Save the entries
|
|
||||||
LinksMgr.saveEntries(this, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* PinboardUtils.kt
|
* Pinboard.kt
|
||||||
*
|
*
|
||||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
@ -45,12 +45,14 @@ import java.util.Date
|
||||||
/**
|
/**
|
||||||
* Handles posts to pinboard.in.
|
* Handles posts to pinboard.in.
|
||||||
*/
|
*/
|
||||||
object PinboardUtils {
|
class Pinboard {
|
||||||
|
private val poster = PinboardPoster()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a pin.
|
* Adds a pin.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
fun addPin(ircServer: String, entry: EntryLink) {
|
||||||
fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) {
|
if (poster.apiToken.isNotBlank()) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch {
|
launch {
|
||||||
poster.addPin(
|
poster.addPin(
|
||||||
|
@ -63,46 +65,40 @@ object PinboardUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the pinboard API token.
|
||||||
|
*/
|
||||||
|
fun setApiToken(apiToken: String) {
|
||||||
|
poster.apiToken = apiToken
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a pin.
|
* Deletes a pin.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
fun deletePin(entry: EntryLink) {
|
||||||
fun deletePin(poster: PinboardPoster, entry: EntryLink) {
|
if (poster.apiToken.isNotBlank()) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch {
|
launch {
|
||||||
poster.deletePin(entry.link)
|
poster.deletePin(entry.link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a pin.
|
* Updates a pin.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) {
|
||||||
fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) {
|
if (poster.apiToken.isNotBlank()) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch {
|
launch {
|
||||||
with(entry) {
|
with(entry) {
|
||||||
if (oldUrl != link) {
|
if (oldUrl != link) {
|
||||||
poster.deletePin(oldUrl)
|
poster.deletePin(oldUrl)
|
||||||
poster.addPin(
|
}
|
||||||
link,
|
poster.addPin(link, title, entry.postedBy(ircServer), pinboardTags, date.toTimestamp())
|
||||||
title,
|
|
||||||
entry.postedBy(ircServer),
|
|
||||||
pinboardTags,
|
|
||||||
date.toTimestamp()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
poster.addPin(
|
|
||||||
link,
|
|
||||||
title,
|
|
||||||
entry.postedBy(ircServer),
|
|
||||||
pinboardTags,
|
|
||||||
date.toTimestamp(),
|
|
||||||
replace = true,
|
|
||||||
shared = true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,8 +108,7 @@ object PinboardUtils {
|
||||||
/**
|
/**
|
||||||
* Format a date to a UTC timestamp.
|
* Format a date to a UTC timestamp.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
private fun Date.toTimestamp(): String {
|
||||||
fun Date.toTimestamp(): String {
|
|
||||||
return ZonedDateTime.ofInstant(
|
return ZonedDateTime.ofInstant(
|
||||||
this.toInstant().truncatedTo(ChronoUnit.SECONDS),
|
this.toInstant().truncatedTo(ChronoUnit.SECONDS),
|
||||||
ZoneId.systemDefault()
|
ZoneId.systemDefault()
|
|
@ -95,7 +95,6 @@ object TwitterOAuth {
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
} catch (te: TwitterException) {
|
} catch (te: TwitterException) {
|
||||||
@Suppress("MagicNumber")
|
|
||||||
if (401 == te.statusCode) {
|
if (401 == te.statusCode) {
|
||||||
println("Unable to get the access token.")
|
println("Unable to get the access token.")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -32,10 +32,11 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot
|
package net.thauvin.erik.mobibot
|
||||||
|
|
||||||
|
import net.thauvin.erik.mobibot.modules.Twitter
|
||||||
import java.util.TimerTask
|
import java.util.TimerTask
|
||||||
|
|
||||||
class TwitterTimer(var bot: Mobibot, private var index: Int) : TimerTask() {
|
class TwitterTimer(private var twitter: Twitter, private var index: Int) : TimerTask() {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
bot.twitter.postEntry(index)
|
twitter.postEntry(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,13 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot
|
package net.thauvin.erik.mobibot
|
||||||
|
|
||||||
|
import net.thauvin.erik.mobibot.msg.Message
|
||||||
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
||||||
import org.jibble.pircbot.Colors
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
import org.pircbotx.Colors
|
||||||
|
import org.pircbotx.PircBotX
|
||||||
|
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
@ -85,6 +89,13 @@ object Utils {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun bold(s: String?): String = colorize(s, Colors.BOLD)
|
fun bold(s: String?): String = colorize(s, Colors.BOLD)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the [PircBotX] instance.
|
||||||
|
*/
|
||||||
|
fun GenericMessageEvent.bot(): PircBotX {
|
||||||
|
return getBot() as PircBotX
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's
|
* Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's
|
||||||
* nick.
|
* nick.
|
||||||
|
@ -159,6 +170,14 @@ object Utils {
|
||||||
return if (isIndent) s.prependIndent() else s
|
return if (isIndent) s.prependIndent() else s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the specified user is an operator on the [channel].
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isChannelOp(channel: String, event: GenericMessageEvent): Boolean {
|
||||||
|
return event.bot().userChannelDao.getChannel(channel).isOp(event.user)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obfuscates the given string.
|
* Obfuscates the given string.
|
||||||
*/
|
*/
|
||||||
|
@ -203,6 +222,56 @@ object Utils {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun reverseColor(s: String?): String = colorize(s, Colors.REVERSE)
|
fun reverseColor(s: String?): String = colorize(s, Colors.REVERSE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a formatted commands/modules, etc. list.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun GenericMessageEvent.sendList(
|
||||||
|
list: List<String>,
|
||||||
|
maxPerLine: Int,
|
||||||
|
separator: String = " ",
|
||||||
|
isBold: Boolean = false,
|
||||||
|
isIndent: Boolean = false
|
||||||
|
) {
|
||||||
|
var i = 0
|
||||||
|
while (i < list.size) {
|
||||||
|
sendMessage(
|
||||||
|
helpFormat(
|
||||||
|
list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""),
|
||||||
|
isBold,
|
||||||
|
isIndent
|
||||||
|
),
|
||||||
|
)
|
||||||
|
i += maxPerLine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a [message].
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun GenericMessageEvent.sendMessage(channel: String, message: Message) {
|
||||||
|
if (message.isNotice) {
|
||||||
|
bot().sendIRC().notice(user.nick, colorize(message.msg, message.color))
|
||||||
|
} else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) {
|
||||||
|
respondPrivateMessage(colorize(message.msg, message.color))
|
||||||
|
} else {
|
||||||
|
bot().sendIRC().message(channel, colorize(message.msg, message.color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a response as a private message or notice.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun GenericMessageEvent.sendMessage(message: String) {
|
||||||
|
if (this is PrivateMessageEvent) {
|
||||||
|
respondPrivateMessage(message)
|
||||||
|
} else {
|
||||||
|
bot().sendIRC().notice(user.nick, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns today's date.
|
* Returns today's date.
|
||||||
*/
|
*/
|
||||||
|
@ -258,7 +327,6 @@ object Utils {
|
||||||
/**
|
/**
|
||||||
* Converts milliseconds to year month week day hour and minutes.
|
* Converts milliseconds to year month week day hour and minutes.
|
||||||
*/
|
*/
|
||||||
@Suppress("MagicNumber")
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun uptime(uptime: Long): String {
|
fun uptime(uptime: Long): String {
|
||||||
val info = StringBuilder()
|
val info = StringBuilder()
|
||||||
|
|
|
@ -32,31 +32,31 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||||
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
|
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
abstract class AbstractCommand(val bot: Mobibot) {
|
abstract class AbstractCommand {
|
||||||
abstract val name: String
|
abstract val name: String
|
||||||
abstract val help: List<String>
|
abstract val help: List<String>
|
||||||
abstract val isOp: Boolean
|
abstract val isOpOnly: Boolean
|
||||||
abstract val isPublic: Boolean
|
abstract val isPublic: Boolean
|
||||||
abstract val isVisible: Boolean
|
abstract val isVisible: Boolean
|
||||||
|
|
||||||
val properties: MutableMap<String, String> = ConcurrentHashMap()
|
val properties: MutableMap<String, String> = ConcurrentHashMap()
|
||||||
|
|
||||||
abstract fun commandResponse(
|
abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent)
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
open fun helpResponse(command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean {
|
open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||||
if (!this.isOp || this.isOp == isOp) {
|
if (!isOpOnly || isOpOnly == isChannelOp(channel, event)) {
|
||||||
for (h in help) {
|
for (h in help) {
|
||||||
bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivate), isPrivate)
|
event.sendMessage(
|
||||||
|
buildCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
* AddLog.kt
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* Neither the name of this project nor the names of its contributors may be
|
|
||||||
* used to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.history
|
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesMgr
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class AddLog(bot: Mobibot) : AbstractCommand(bot) {
|
|
||||||
override val name = "addlog"
|
|
||||||
override val help = emptyList<String>()
|
|
||||||
override val isOp = true
|
|
||||||
override val isPublic = false
|
|
||||||
override val isVisible = false
|
|
||||||
|
|
||||||
override fun commandResponse(
|
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (isOp) {
|
|
||||||
if (args.isNotBlank()) {
|
|
||||||
// e.g: 2014-04-01
|
|
||||||
val backlog = File("${bot.logsDir}$args${EntriesMgr.XML_EXT}")
|
|
||||||
if (backlog.exists()) {
|
|
||||||
history.add(0, args)
|
|
||||||
} else {
|
|
||||||
bot.send(sender, "The specified log could not be found.", isPrivate)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
bot.sendList(sender, history, 4, isPrivate = isPrivate, isIndent = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -35,13 +35,14 @@ package net.thauvin.erik.mobibot.commands
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.thauvin.erik.mobibot.FeedReader
|
import net.thauvin.erik.mobibot.FeedReader
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) {
|
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"))
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
@ -53,22 +54,16 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) {
|
||||||
initProperties(FEED_PROP)
|
initProperties(FEED_PROP)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
with(properties[FEED_PROP]) {
|
with(properties[FEED_PROP]) {
|
||||||
if (!isNullOrBlank()) {
|
if (!isNullOrBlank()) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch {
|
launch {
|
||||||
FeedReader(bot, sender, this@with).run()
|
FeedReader(this@with, event).run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "There is no feed setup for this channel.", false)
|
event.sendMessage("There is no feed setup for this channel.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,35 +32,33 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Cycle(bot: Mobibot) : AbstractCommand(bot) {
|
class Cycle : AbstractCommand() {
|
||||||
@Suppress("MagicNumber")
|
|
||||||
private val wait = 10
|
private val wait = 10
|
||||||
override val name = "cycle"
|
override val name = "cycle"
|
||||||
override val help = listOf("To have the bot leave the channel and come back:", helpFormat("%c $name"))
|
override val help = listOf("To have the bot leave the channel and come back:", helpFormat("%c $name"))
|
||||||
override val isOp = true
|
override val isOpOnly = true
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
with(event.bot()) {
|
||||||
login: String,
|
if (isChannelOp(channel, event)) {
|
||||||
args: String,
|
runBlocking {
|
||||||
isOp: Boolean,
|
sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!")
|
||||||
isPrivate: Boolean
|
userChannelDao.getChannel(channel).send().part()
|
||||||
) {
|
delay(wait * 1000L)
|
||||||
with(bot) {
|
sendIRC().joinChannel(channel)
|
||||||
if (isOp) {
|
}
|
||||||
send("$sender has just asked me to leave. I'll be back!")
|
|
||||||
sleep(wait)
|
|
||||||
partChannel(channel)
|
|
||||||
sleep(wait)
|
|
||||||
joinChannel(channel)
|
|
||||||
} else {
|
} else {
|
||||||
helpDefault(sender, isOp, isPrivate)
|
helpResponse(channel, args, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* Debug.kt
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* Neither the name of this project nor the names of its contributors may be
|
|
||||||
* used to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Constants
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
import org.apache.logging.log4j.core.config.Configurator
|
|
||||||
|
|
||||||
class Debug(bot: Mobibot) : AbstractCommand(bot) {
|
|
||||||
override val name = Constants.DEBUG_CMD
|
|
||||||
override val help = emptyList<String>()
|
|
||||||
override val isOp = true
|
|
||||||
override val isPublic = false
|
|
||||||
override val isVisible = false
|
|
||||||
|
|
||||||
override fun commandResponse(sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean) {
|
|
||||||
if (isOp) {
|
|
||||||
with(bot) {
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
Configurator.setLevel(logger.name, loggerLevel)
|
|
||||||
} else {
|
|
||||||
Configurator.setLevel(logger.name, Level.DEBUG)
|
|
||||||
}
|
|
||||||
send(sender, "Debug logging is " + if (logger.isDebugEnabled) "enabled." else "disabled.", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -32,25 +32,35 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Die(bot: Mobibot) : AbstractCommand(bot) {
|
class Die : AbstractCommand() {
|
||||||
override val name = "die"
|
override val name = "die"
|
||||||
override val help = emptyList<String>()
|
override val help = emptyList<String>()
|
||||||
override val isOp = true
|
override val isOpOnly = true
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = false
|
override val isVisible = false
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
with(event.bot()) {
|
||||||
login: String,
|
if (isChannelOp(channel, event) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) {
|
||||||
args: String,
|
sendIRC().message(channel, "${event.user?.nick} has just signed my death sentence.")
|
||||||
isOp: Boolean,
|
stopBotReconnect()
|
||||||
isPrivate: Boolean
|
sendIRC().quitServer("The Bot is Out There!")
|
||||||
) {
|
|
||||||
if (isOp) {
|
|
||||||
bot.send("$sender has just signed my death sentence.")
|
|
||||||
bot.shutdown(sender)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Max days property.
|
||||||
|
*/
|
||||||
|
const val DIE_PROP = "die"
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
initProperties(DIE_PROP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -32,13 +32,17 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||||
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.sendList
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Ignore(bot: Mobibot) : AbstractCommand(bot) {
|
class Ignore : AbstractCommand() {
|
||||||
private val me = "me"
|
private val me = "me"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -58,7 +62,7 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]"))
|
arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]"))
|
||||||
)
|
)
|
||||||
|
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
@ -73,56 +77,45 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
val isMe = args.trim().equals(me, true)
|
val isMe = args.trim().equals(me, true)
|
||||||
if (isMe || !isOp) {
|
if (isMe || !isChannelOp(channel, event)) {
|
||||||
val nick = sender.lowercase()
|
val nick = event.user.nick.lowercase()
|
||||||
ignoreNick(bot, nick, isMe, isPrivate)
|
ignoreNick(nick, isMe, event)
|
||||||
} else {
|
} else {
|
||||||
ignoreOp(bot, sender, args, isPrivate)
|
ignoreOp(args, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun helpResponse(
|
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||||
command: String,
|
return if (isChannelOp(channel, event)) {
|
||||||
sender: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
): Boolean {
|
|
||||||
return if (isOp) {
|
|
||||||
for (h in helpOp) {
|
for (h in helpOp) {
|
||||||
bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivate), isPrivate)
|
event.sendMessage(buildCmdSyntax(h, event.bot().nick, true))
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
super.helpResponse(command, sender, isOp, isPrivate)
|
super.helpResponse(channel, topic, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ignoreNick(bot: Mobibot, sender: String, isMe: Boolean, isPrivate: Boolean) {
|
private fun ignoreNick(sender: String, isMe: Boolean, event: GenericMessageEvent) {
|
||||||
if (isMe) {
|
if (isMe) {
|
||||||
if (ignored.remove(sender)) {
|
if (ignored.remove(sender)) {
|
||||||
bot.send(sender, "You are no longer ignored.", isPrivate)
|
event.sendMessage("You are no longer ignored.")
|
||||||
} else {
|
} else {
|
||||||
ignored.add(sender)
|
ignored.add(sender)
|
||||||
bot.send(sender, "You are now ignored.", isPrivate)
|
event.sendMessage("You are now ignored.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ignored.contains(sender)) {
|
if (ignored.contains(sender)) {
|
||||||
bot.send(sender, "You are currently ignored.", isPrivate)
|
event.sendMessage("You are currently ignored.")
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "You are not currently ignored.", isPrivate)
|
event.sendMessage("You are not currently ignored.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ignoreOp(bot: Mobibot, sender: String, args: String, isPrivate: Boolean) {
|
private fun ignoreOp(args: String, event: GenericMessageEvent) {
|
||||||
if (args.isNotEmpty()) {
|
if (args.isNotEmpty()) {
|
||||||
val nicks = args.lowercase().split(" ")
|
val nicks = args.lowercase().split(" ")
|
||||||
for (nick in nicks) {
|
for (nick in nicks) {
|
||||||
|
@ -138,11 +131,10 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ignored.size > 0) {
|
if (ignored.size > 0) {
|
||||||
bot.send(sender, "The following nicks are ignored:", isPrivate)
|
event.sendMessage("The following nicks are ignored:")
|
||||||
@Suppress("MagicNumber")
|
event.sendList(ignored.sorted(), 8, isIndent = true)
|
||||||
bot.sendList(sender, ignored.sorted(), 8, isPrivate = isPrivate, isIndent = true)
|
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "No one is currently ${bold("ignored")}.", isPrivate)
|
event.sendMessage("No one is currently ${bold("ignored")}.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,50 +31,46 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
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.capitalise
|
||||||
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.sendList
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.uptime
|
import net.thauvin.erik.mobibot.Utils.uptime
|
||||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||||
|
import net.thauvin.erik.mobibot.commands.tell.Tell
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.lang.management.ManagementFactory
|
import java.lang.management.ManagementFactory
|
||||||
|
|
||||||
class Info(bot: Mobibot?) : AbstractCommand(bot!!) {
|
class Info(private val tell: Tell) : AbstractCommand() {
|
||||||
private val allVersions = listOf(
|
private val allVersions = listOf(
|
||||||
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${green(ReleaseInfo.WEBSITE)})",
|
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${green(ReleaseInfo.WEBSITE)})",
|
||||||
"Written by ${ReleaseInfo.AUTHOR} (${green(ReleaseInfo.AUTHOR_URL)})"
|
"Written by ${ReleaseInfo.AUTHOR} (${green(ReleaseInfo.AUTHOR_URL)})"
|
||||||
)
|
)
|
||||||
override val name = "info"
|
override val name = "info"
|
||||||
override val help = listOf("To view information about the bot:", helpFormat("%c $name"))
|
override val help = listOf("To view information about the bot:", helpFormat("%c $name"))
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
event.sendList(allVersions, 1)
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
with(bot) {
|
|
||||||
sendList(sender, allVersions, 1, isPrivate = isPrivate)
|
|
||||||
val info = StringBuilder()
|
val info = StringBuilder()
|
||||||
info.append("Uptime: ")
|
info.append("Uptime: ")
|
||||||
.append(uptime(ManagementFactory.getRuntimeMXBean().uptime))
|
.append(uptime(ManagementFactory.getRuntimeMXBean().uptime))
|
||||||
.append(" [Entries: ")
|
.append(" [Entries: ")
|
||||||
.append(LinksMgr.entries.size)
|
.append(LinksMgr.entries.links.size)
|
||||||
if (isOp) {
|
if (isChannelOp(channel, event)) {
|
||||||
if (tell.isEnabled()) {
|
if (tell.isEnabled()) {
|
||||||
info.append(", Messages: ").append(tell.size())
|
info.append(", Messages: ").append(tell.size())
|
||||||
}
|
}
|
||||||
if (twitter.isAutoPost) {
|
if (LinksMgr.twitter.isAutoPost) {
|
||||||
info.append(", Twitter: ").append(twitter.entriesCount())
|
info.append(", Twitter: ").append(LinksMgr.twitter.entriesCount())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info.append(", Recap: ").append(Recap.recaps.size).append(']')
|
info.append(", Recap: ").append(Recap.recaps.size).append(']')
|
||||||
send(sender, info.toString(), isPrivate)
|
event.sendMessage(info.toString())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,27 +32,21 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Me(bot: Mobibot) : AbstractCommand(bot) {
|
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>"))
|
||||||
override val isOp = true
|
override val isOpOnly = true
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (isChannelOp(channel, event)) {
|
||||||
login: String,
|
event.bot().sendIRC().action(channel, args)
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (isOp) {
|
|
||||||
bot.action(args)
|
|
||||||
} else {
|
|
||||||
bot.helpDefault(sender, isOp, isPrivate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,35 +32,29 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
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.sendList
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Modules(bot: Mobibot) : AbstractCommand(bot) {
|
class Modules(private val modulesNames: List<String>) : AbstractCommand() {
|
||||||
override val name = "modules"
|
override val name = "modules"
|
||||||
override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name"))
|
override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name"))
|
||||||
override val isOp = true
|
override val isOpOnly = true
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (isChannelOp(channel, event)) {
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
with(bot) {
|
|
||||||
if (isOp) {
|
|
||||||
if (modulesNames.isEmpty()) {
|
if (modulesNames.isEmpty()) {
|
||||||
send(sender, "There are no enabled modules.", isPrivate)
|
event.respondPrivateMessage("There are no enabled modules.")
|
||||||
} else {
|
} else {
|
||||||
send(sender, "The enabled modules are: ", isPrivate)
|
event.respondPrivateMessage("The enabled modules are: ")
|
||||||
@Suppress("MagicNumber")
|
event.sendList(modulesNames, 7, isIndent = true)
|
||||||
sendList(sender, modulesNames, 7, isPrivate = isPrivate, isIndent = true)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpDefault(sender, isOp, isPrivate)
|
helpResponse(channel, args, event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,35 +32,30 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Msg(bot: Mobibot) : AbstractCommand(bot) {
|
class Msg : AbstractCommand() {
|
||||||
override val name = "msg"
|
override val name = "msg"
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
"To have the bot send a private message to someone:",
|
"To have the bot send a private message to someone:",
|
||||||
helpFormat("%c $name <nick> <text>")
|
helpFormat("%c $name <nick> <text>")
|
||||||
)
|
)
|
||||||
override val isOp = true
|
override val isOpOnly = true
|
||||||
override val isPublic = true
|
override val isPublic = false
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (isChannelOp(channel, event)) {
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (isOp) {
|
|
||||||
val msg = args.split(" ", limit = 2)
|
val msg = args.split(" ", limit = 2)
|
||||||
if (args.length > 2) {
|
if (args.length > 2) {
|
||||||
bot.send(msg[0], msg[1], isPrivate)
|
event.bot().sendIRC().message(msg[0], msg[1])
|
||||||
|
event.respondPrivateMessage("A message was sent to ${msg[0]}")
|
||||||
} else {
|
} else {
|
||||||
helpResponse(name, sender, isOp, isPrivate)
|
helpResponse(channel, args, event)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
bot.helpDefault(sender, isOp, isPrivate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,27 +32,21 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Nick(bot: Mobibot) : AbstractCommand(bot) {
|
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>"))
|
||||||
override val isOp = true
|
override val isOpOnly = true
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (isChannelOp(channel, event)) {
|
||||||
login: String,
|
event.bot().sendIRC().changeNick(args)
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (isOp) {
|
|
||||||
bot.changeNick(args)
|
|
||||||
} else {
|
|
||||||
bot.helpDefault(sender, isOp, isPrivate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,19 +32,20 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class Recap(bot: Mobibot) : AbstractCommand(bot) {
|
class Recap : AbstractCommand() {
|
||||||
override val name = "recap"
|
override val name = "recap"
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
"To list the last 10 public channel messages:",
|
"To list the last 10 public channel messages:",
|
||||||
helpFormat("%c $name")
|
helpFormat("%c $name")
|
||||||
)
|
)
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
@ -61,26 +62,19 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
LocalDateTime.now(Clock.systemUTC()).toUtcDateTime()
|
LocalDateTime.now(Clock.systemUTC()).toUtcDateTime()
|
||||||
+ " - $sender" + (if (isAction) " " else ": ") + message
|
+ " - $sender" + (if (isAction) " " else ": ") + message
|
||||||
)
|
)
|
||||||
@Suppress("MagicNumber")
|
|
||||||
if (recaps.size > 10) {
|
if (recaps.size > 10) {
|
||||||
recaps.removeFirst()
|
recaps.removeFirst()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (recaps.isNotEmpty()) {
|
if (recaps.isNotEmpty()) {
|
||||||
for (r in recaps) {
|
for (r in recaps) {
|
||||||
bot.send(sender, r, isPrivate)
|
event.sendMessage(r)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "Sorry, nothing to recap.", isPrivate)
|
event.sendMessage("Sorry, nothing to recap.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,28 +32,22 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Say(bot: Mobibot) : AbstractCommand(bot) {
|
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>"))
|
||||||
override val isOp = true
|
override val isOpOnly = true
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (isChannelOp(channel, event)) {
|
||||||
login: String,
|
event.bot().sendIRC().message(channel, args)
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (isOp) {
|
|
||||||
bot.send(args)
|
|
||||||
} else {
|
|
||||||
bot.helpDefault(sender, isOp, isPrivate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,36 +32,29 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendList
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Users(bot: Mobibot) : AbstractCommand(bot) {
|
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"))
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
val nicks = mutableListOf<String>()
|
val nicks = mutableListOf<String>()
|
||||||
with(bot) {
|
val ch = event.bot().userChannelDao.getChannel(channel)
|
||||||
getUsers(channel).forEach { user ->
|
ch.users.forEach {
|
||||||
if (isOp(user.nick)) {
|
if (it.channelsOpIn.contains(ch)) {
|
||||||
nicks.add("@${user.nick}")
|
nicks.add("@${it.nick}")
|
||||||
} else {
|
} else {
|
||||||
nicks.add(user.nick)
|
nicks.add(it.nick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
event.sendList(nicks, 8)
|
||||||
@Suppress("MagicNumber")
|
|
||||||
sendList(sender, nicks.sorted(), 8, isPrivate = isPrivate, isIndent = true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,14 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.commands
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||||
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.sendList
|
||||||
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class Versions(bot: Mobibot) : AbstractCommand(bot) {
|
class Versions : AbstractCommand() {
|
||||||
private val allVersions = listOf(
|
private val allVersions = listOf(
|
||||||
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})",
|
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})",
|
||||||
"Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version")
|
"Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version")
|
||||||
|
@ -45,21 +47,13 @@ class Versions(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
)
|
)
|
||||||
override val name = "versions"
|
override val name = "versions"
|
||||||
override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name"))
|
override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name"))
|
||||||
override val isOp = true
|
override val isOpOnly = true
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (isChannelOp(channel, event)) {
|
||||||
login: String,
|
event.sendList(allVersions, 1)
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (isOp) {
|
|
||||||
bot.sendList(sender, allVersions, 1, isPrivate = isPrivate)
|
|
||||||
} else {
|
|
||||||
bot.helpDefault(sender, false, isPrivate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,14 +33,16 @@
|
||||||
package net.thauvin.erik.mobibot.commands.links
|
package net.thauvin.erik.mobibot.commands.links
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Constants
|
import net.thauvin.erik.mobibot.Constants
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
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.sendMessage
|
||||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
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
|
||||||
|
|
||||||
class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
class Comment : AbstractCommand() {
|
||||||
override val name = COMMAND
|
override val name = COMMAND
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
"To add a comment:",
|
"To add a comment:",
|
||||||
|
@ -51,7 +53,7 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
"To delete a comment, use its label and a minus sign: ",
|
"To delete a comment, use its label and a minus sign: ",
|
||||||
helpFormat("${Constants.LINK_CMD}1.1:-")
|
helpFormat("${Constants.LINK_CMD}1.1:-")
|
||||||
)
|
)
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
@ -59,29 +61,22 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
const val COMMAND = "comment"
|
const val COMMAND = "comment"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
val cmds = args.substring(1).split("[.:]".toRegex(), 3)
|
val cmds = args.substring(1).split("[.:]".toRegex(), 3)
|
||||||
val index = cmds[0].toInt() - 1
|
val entryIndex = cmds[0].toInt() - 1
|
||||||
|
|
||||||
if (index < LinksMgr.entries.size) {
|
if (entryIndex < LinksMgr.entries.links.size && LinksMgr.isUpToDate(event)) {
|
||||||
val entry: EntryLink = LinksMgr.entries[index]
|
val entry: EntryLink = LinksMgr.entries.links[entryIndex]
|
||||||
val commentIndex = cmds[1].toInt() - 1
|
val commentIndex = cmds[1].toInt() - 1
|
||||||
if (commentIndex < entry.comments.size) {
|
if (commentIndex < entry.comments.size) {
|
||||||
when (val cmd = cmds[2].trim()) {
|
when (val cmd = cmds[2].trim()) {
|
||||||
"" -> showComment(bot, entry, index, commentIndex) // L1.1:
|
"" -> showComment(entry, entryIndex, commentIndex, event) // L1.1:
|
||||||
"-" -> deleteComment(bot, sender, isOp, entry, index, commentIndex) // L11:-
|
"-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:-
|
||||||
else -> {
|
else -> {
|
||||||
if (cmd.startsWith('?')) { // L1.1:?<author>
|
if (cmd.startsWith('?')) { // L1.1:?<author>
|
||||||
changeAuthor(bot, cmd, sender, isOp, entry, index, commentIndex)
|
changeAuthor(channel, cmd, entry, entryIndex, commentIndex, event)
|
||||||
} else { // L1.1:<comment>
|
} else { // L1.1:<comment>
|
||||||
setComment(bot, cmd, sender, entry, index, commentIndex)
|
setComment(cmd, entry, entryIndex, commentIndex, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,20 +84,11 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun helpResponse(
|
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||||
command: String,
|
if (super.helpResponse(channel, topic, event)) {
|
||||||
sender: String,
|
if (isChannelOp(channel, event)) {
|
||||||
isOp: Boolean,
|
event.sendMessage("To change a comment's author:")
|
||||||
isPrivate: Boolean
|
event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?<nick>"))
|
||||||
): Boolean {
|
|
||||||
if (super.helpResponse(command, sender, isOp, isPrivate)) {
|
|
||||||
if (isOp) {
|
|
||||||
bot.send(sender, "To change a comment's author:", isPrivate)
|
|
||||||
bot.send(
|
|
||||||
sender,
|
|
||||||
helpFormat("${Constants.LINK_CMD}1.1:?<nick>"),
|
|
||||||
isPrivate
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -114,49 +100,52 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeAuthor(
|
private fun changeAuthor(
|
||||||
bot: Mobibot,
|
channel: String,
|
||||||
cmd: String,
|
cmd: String,
|
||||||
sender: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
entry: EntryLink,
|
entry: EntryLink,
|
||||||
index: Int,
|
entryIndex: Int,
|
||||||
commentIndex: Int
|
commentIndex: Int,
|
||||||
|
event: GenericMessageEvent
|
||||||
) {
|
) {
|
||||||
if (isOp && cmd.length > 1) {
|
if (isChannelOp(channel, event) && cmd.length > 1) {
|
||||||
val comment = entry.getComment(commentIndex)
|
val comment = entry.getComment(commentIndex)
|
||||||
comment.nick = cmd.substring(1)
|
comment.nick = cmd.substring(1)
|
||||||
bot.send(EntriesUtils.buildComment(index, commentIndex, comment))
|
event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment))
|
||||||
LinksMgr.saveEntries(bot, false)
|
LinksMgr.entries.save()
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "Please ask a channel op to change the author of this comment for you.", false)
|
event.sendMessage("Please ask a channel op to change the author of this comment for you.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteComment(
|
private fun deleteComment(
|
||||||
bot: Mobibot,
|
channel: String,
|
||||||
sender: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
entry: EntryLink,
|
entry: EntryLink,
|
||||||
index: Int,
|
entryIndex: Int,
|
||||||
commentIndex: Int
|
commentIndex: Int,
|
||||||
|
event: GenericMessageEvent
|
||||||
) {
|
) {
|
||||||
if (isOp || sender == entry.getComment(commentIndex).nick) {
|
if (isChannelOp(channel, event) || event.user.nick == entry.getComment(commentIndex).nick) {
|
||||||
entry.deleteComment(commentIndex)
|
entry.deleteComment(commentIndex)
|
||||||
bot.send("Comment ${EntriesUtils.buildLinkCmd(index)}.${commentIndex + 1} removed.")
|
event.sendMessage("Comment ${EntriesUtils.buildLinkLabel(entryIndex)}.${commentIndex + 1} removed.")
|
||||||
LinksMgr.saveEntries(bot, false)
|
LinksMgr.entries.save()
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "Please ask a channel op to delete this comment for you.", false)
|
event.sendMessage("Please ask a channel op to delete this comment for you.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setComment(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int) {
|
private fun setComment(
|
||||||
entry.setComment(commentIndex, cmd, sender)
|
cmd: String,
|
||||||
val comment = entry.getComment(commentIndex)
|
entry: EntryLink,
|
||||||
bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false)
|
entryIndex: Int,
|
||||||
LinksMgr.saveEntries(bot, false)
|
commentIndex: Int,
|
||||||
|
event: GenericMessageEvent
|
||||||
|
) {
|
||||||
|
entry.setComment(commentIndex, cmd, event.user.nick)
|
||||||
|
event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, entry.getComment(commentIndex)))
|
||||||
|
LinksMgr.entries.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showComment(bot: Mobibot, entry: EntryLink, index: Int, commentIndex: Int) {
|
private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) {
|
||||||
bot.send(EntriesUtils.buildComment(index, commentIndex, entry.getComment(commentIndex)))
|
event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, entry.getComment(commentIndex)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,25 +33,29 @@
|
||||||
package net.thauvin.erik.mobibot.commands.links
|
package net.thauvin.erik.mobibot.commands.links
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Constants
|
import net.thauvin.erik.mobibot.Constants
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Pinboard
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.today
|
import net.thauvin.erik.mobibot.Utils.today
|
||||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||||
import net.thauvin.erik.mobibot.commands.Ignore
|
import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesMgr
|
import net.thauvin.erik.mobibot.entries.Entries
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||||
|
import net.thauvin.erik.mobibot.modules.Twitter
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
class LinksMgr : AbstractCommand() {
|
||||||
private val keywords: MutableList<String> = mutableListOf()
|
|
||||||
private val defaultTags: MutableList<String> = mutableListOf()
|
private val defaultTags: MutableList<String> = mutableListOf()
|
||||||
|
private val keywords: MutableList<String> = mutableListOf()
|
||||||
|
|
||||||
override val name = Constants.LINK_CMD
|
override val name = Constants.LINK_CMD
|
||||||
override val help = emptyList<String>()
|
override val help = emptyList<String>()
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = false
|
override val isPublic = false
|
||||||
override val isVisible = false
|
override val isVisible = false
|
||||||
|
|
||||||
|
@ -65,52 +69,35 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
const val TAGS_PROP = "tags"
|
const val TAGS_PROP = "tags"
|
||||||
const val TAG_MATCH = ", *| +"
|
const val TAG_MATCH = ", *| +"
|
||||||
|
|
||||||
// Entries array
|
/** Entries array **/
|
||||||
@JvmField
|
val entries = Entries()
|
||||||
val entries = mutableListOf<EntryLink>()
|
|
||||||
|
|
||||||
// History/backlogs array
|
/** Pinboard handler. **/
|
||||||
@JvmField
|
val pinboard = Pinboard()
|
||||||
val history = mutableListOf<String>()
|
|
||||||
|
|
||||||
|
/** Twitter handler. **/
|
||||||
|
val twitter = Twitter()
|
||||||
|
|
||||||
|
/** Let the user know if the entries are too old to be modified. **/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var startDate: String = today()
|
fun isUpToDate(event: GenericMessageEvent): Boolean {
|
||||||
private set
|
if (entries.lastPubDate != today()) {
|
||||||
|
event.sendMessage("The links are too old to be updated.")
|
||||||
/**
|
return false
|
||||||
* Saves the entries.
|
|
||||||
*
|
|
||||||
* @param isDayBackup Set the `true` if the daily backup file should also be created.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun saveEntries(bot: Mobibot, isDayBackup: Boolean) {
|
|
||||||
EntriesMgr.saveEntries(bot, entries, history, isDayBackup)
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
@JvmStatic
|
|
||||||
fun startup(current: String, backlogs: String, channel: String) {
|
|
||||||
startDate = EntriesMgr.loadEntries(current, channel, entries)
|
|
||||||
if (today() != startDate) {
|
|
||||||
entries.clear()
|
|
||||||
startDate = today()
|
|
||||||
}
|
|
||||||
EntriesMgr.loadBacklogs(backlogs, history)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
val cmds = args.split(" ".toRegex(), 2)
|
val cmds = args.split(" ".toRegex(), 2)
|
||||||
|
val sender = event.user.nick
|
||||||
|
val botNick = event.bot().nick
|
||||||
|
val login = event.user.login
|
||||||
|
|
||||||
if (Ignore.isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(bot.nick))) {
|
if (isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(botNick))) {
|
||||||
val link = cmds[0].trim()
|
val link = cmds[0].trim()
|
||||||
if (!isDupEntry(bot, sender, link, isPrivate)) {
|
if (!isDupEntry(link, event)) {
|
||||||
val isBackup = saveDayBackup(bot)
|
|
||||||
var title = ""
|
var title = ""
|
||||||
val tags = ArrayList<String>(defaultTags)
|
val tags = ArrayList<String>(defaultTags)
|
||||||
if (cmds.size == 2) {
|
if (cmds.size == 2) {
|
||||||
|
@ -129,37 +116,32 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
matchTagKeywords(title, tags)
|
matchTagKeywords(title, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.add(EntryLink(link, title, sender, login, bot.channel, tags))
|
// Links are old, clear them
|
||||||
val index: Int = entries.size - 1
|
if (entries.lastPubDate != today()) {
|
||||||
val entry: EntryLink = entries[index]
|
entries.links.clear()
|
||||||
bot.send(EntriesUtils.buildLink(index, entry))
|
}
|
||||||
|
|
||||||
// Add Entry to pinboard.
|
val entry = EntryLink(link, title, sender, login, channel, tags)
|
||||||
bot.addPin(entry)
|
entries.links.add(entry)
|
||||||
|
val index = entries.links.lastIndexOf(entry)
|
||||||
|
event.sendMessage(EntriesUtils.buildLink(index, entry))
|
||||||
|
|
||||||
|
pinboard.addPin(event.bot().serverHostname, entry)
|
||||||
|
|
||||||
// Queue link for posting to Twitter.
|
// Queue link for posting to Twitter.
|
||||||
bot.twitter.queueEntry(index)
|
twitter.queueEntry(index)
|
||||||
|
|
||||||
saveEntries(bot, isBackup)
|
entries.save()
|
||||||
|
|
||||||
if (Constants.NO_TITLE == entry.title) {
|
if (Constants.NO_TITLE == entry.title) {
|
||||||
bot.send(sender, "Please specify a title, by typing:", isPrivate)
|
event.sendMessage("Please specify a title, by typing:")
|
||||||
bot.send(
|
event.sendMessage(helpFormat("${EntriesUtils.buildLinkLabel(index)}:|This is the title"))
|
||||||
sender,
|
|
||||||
helpFormat("${EntriesUtils.buildLinkCmd(index)}:|This is the title"),
|
|
||||||
isPrivate
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun helpResponse(
|
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false
|
||||||
command: String,
|
|
||||||
sender: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
): Boolean = false
|
|
||||||
|
|
||||||
override fun matches(message: String): Boolean {
|
override fun matches(message: String): Boolean {
|
||||||
return message.matches(LINK_MATCH.toRegex())
|
return message.matches(LINK_MATCH.toRegex())
|
||||||
|
@ -180,12 +162,12 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
return Constants.NO_TITLE
|
return Constants.NO_TITLE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isDupEntry(bot: Mobibot, sender: String, link: String, isPrivate: Boolean): Boolean {
|
private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean {
|
||||||
synchronized(entries) {
|
synchronized(entries) {
|
||||||
for (i in entries.indices) {
|
for (i in entries.links.indices) {
|
||||||
if (link == entries[i].link) {
|
if (link == entries.links[i].link) {
|
||||||
val entry: EntryLink = entries[i]
|
val entry: EntryLink = entries.links[i]
|
||||||
bot.send(sender, bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry), isPrivate)
|
event.sendMessage(bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,17 +184,6 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveDayBackup(bot: Mobibot): Boolean {
|
|
||||||
if (today() != startDate) {
|
|
||||||
saveEntries(bot, true)
|
|
||||||
entries.clear()
|
|
||||||
startDate = today()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setProperty(key: String, value: String) {
|
override fun setProperty(key: String, value: String) {
|
||||||
super.setProperty(key, value)
|
super.setProperty(key, value)
|
||||||
if (KEYWORDS_PROP == key) {
|
if (KEYWORDS_PROP == key) {
|
||||||
|
|
|
@ -33,15 +33,18 @@
|
||||||
package net.thauvin.erik.mobibot.commands.links
|
package net.thauvin.erik.mobibot.commands.links
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Constants
|
import net.thauvin.erik.mobibot.Constants
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
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.sendMessage
|
||||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries
|
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
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
|
||||||
|
|
||||||
class Posting(bot: Mobibot) : AbstractCommand(bot) {
|
class Posting : AbstractCommand() {
|
||||||
override val name = "posting"
|
override val name = "posting"
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
"Post a URL, by saying it on a line on its own:",
|
"Post a URL, by saying it on a line on its own:",
|
||||||
|
@ -55,30 +58,27 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
"To edit a comment, see: ",
|
"To edit a comment, see: ",
|
||||||
helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}")
|
helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}")
|
||||||
)
|
)
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
val cmds = args.substring(1).split(":", limit = 2)
|
val cmds = args.substring(1).split(":", limit = 2)
|
||||||
val index = cmds[0].toInt() - 1
|
val entryIndex = cmds[0].toInt() - 1
|
||||||
|
|
||||||
if (index < entries.size) {
|
if (entryIndex < entries.links.size) {
|
||||||
when (val cmd = cmds[1].trim()) {
|
val cmd = cmds[1].trim()
|
||||||
"" -> showEntry(index)
|
if (cmd.isBlank()) {
|
||||||
"-" -> removeEntry(sender, login, isOp, index) // L1:-
|
showEntry(entryIndex, event) // L1:
|
||||||
else -> {
|
} else if (LinksMgr.isUpToDate(event)) {
|
||||||
|
if (cmd == "-") {
|
||||||
|
removeEntry(channel, entryIndex, event) // L1:-
|
||||||
|
} else {
|
||||||
when (cmd[0]) {
|
when (cmd[0]) {
|
||||||
'|' -> changeTitle(cmd, index) // L1:|<title>
|
'|' -> changeTitle(cmd, entryIndex, event) // L1:|<title>
|
||||||
'=' -> changeUrl(cmd, login, isOp, index) // L1:=<url>
|
'=' -> changeUrl(channel, cmd, entryIndex, event) // L1:=<url>
|
||||||
'?' -> changeAuthor(cmd, sender, isOp, index) // L1:?<author>
|
'?' -> changeAuthor(channel, cmd, entryIndex, event) // L1:?<author>
|
||||||
else -> addComment(cmd, sender, index) // L1:<comment>
|
else -> addComment(cmd, entryIndex, event) // L1:<comment>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,73 +89,75 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex())
|
return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addComment(cmd: String, sender: String, index: Int) {
|
private fun addComment(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||||
val entry: EntryLink = entries[index]
|
val entry: EntryLink = entries.links[entryIndex]
|
||||||
val commentIndex = entry.addComment(cmd, sender)
|
val commentIndex = entry.addComment(cmd, event.user.nick)
|
||||||
val comment = entry.getComment(commentIndex)
|
val comment = entry.getComment(commentIndex)
|
||||||
bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false)
|
event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment))
|
||||||
LinksMgr.saveEntries(bot, false)
|
entries.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeTitle(cmd: String, index: Int) {
|
private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||||
if (cmd.length > 1) {
|
if (cmd.length > 1) {
|
||||||
val entry: EntryLink = entries[index]
|
val entry: EntryLink = entries.links[entryIndex]
|
||||||
entry.title = cmd.substring(1).trim()
|
entry.title = cmd.substring(1).trim()
|
||||||
bot.updatePin(entry.link, entry)
|
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||||
bot.send(EntriesUtils.buildLink(index, entry))
|
event.sendMessage(EntriesUtils.buildLink(entryIndex, entry))
|
||||||
LinksMgr.saveEntries(bot, false)
|
entries.save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeUrl(cmd: String, login: String, isOp: Boolean, index: Int) {
|
private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||||
val entry: EntryLink = entries[index]
|
val entry: EntryLink = entries.links[entryIndex]
|
||||||
if (entry.login == login || isOp) {
|
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||||
val link = cmd.substring(1)
|
val link = cmd.substring(1)
|
||||||
if (link.matches(LinksMgr.LINK_MATCH.toRegex())) {
|
if (link.matches(LinksMgr.LINK_MATCH.toRegex())) {
|
||||||
val oldLink = entry.link
|
val oldLink = entry.link
|
||||||
entry.link = link
|
entry.link = link
|
||||||
bot.updatePin(oldLink, entry)
|
LinksMgr.pinboard.updatePin(event.bot().serverHostname, oldLink, entry)
|
||||||
bot.send(EntriesUtils.buildLink(index, entry))
|
event.sendMessage(EntriesUtils.buildLink(entryIndex, entry))
|
||||||
LinksMgr.saveEntries(bot, false)
|
entries.save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeAuthor(cmd: String, sender: String, isOp: Boolean, index: Int) {
|
private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) {
|
||||||
if (isOp) {
|
if (isChannelOp(channel, event)) {
|
||||||
if (cmd.length > 1) {
|
if (cmd.length > 1) {
|
||||||
val entry: EntryLink = entries[index]
|
val entry: EntryLink = entries.links[index]
|
||||||
entry.nick = cmd.substring(1)
|
entry.nick = cmd.substring(1)
|
||||||
bot.send(EntriesUtils.buildLink(index, entry))
|
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||||
LinksMgr.saveEntries(bot, false)
|
event.sendMessage(EntriesUtils.buildLink(index, entry))
|
||||||
|
entries.save()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "Please ask a channel op to change the author of this link for you.", false)
|
event.sendMessage("Please ask a channel op to change the author of this link for you.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeEntry(sender: String, login: String, isOp: Boolean, index: Int) {
|
private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) {
|
||||||
val entry: EntryLink = entries[index]
|
val entry: EntryLink = entries.links[index]
|
||||||
if (entry.login == login || isOp) {
|
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||||
bot.deletePin(index, entry)
|
LinksMgr.pinboard.deletePin(entry)
|
||||||
entries.removeAt(index)
|
LinksMgr.twitter.removeEntry(index)
|
||||||
bot.send("Entry ${EntriesUtils.buildLinkCmd(index)} removed.")
|
entries.links.removeAt(index)
|
||||||
LinksMgr.saveEntries(bot, false)
|
event.sendMessage("Entry ${EntriesUtils.buildLinkLabel(index)} removed.")
|
||||||
|
entries.save()
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "Please ask a channel op to remove this entry for you.", false)
|
event.sendMessage("Please ask a channel op to remove this entry for you.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showEntry(index: Int) {
|
private fun showEntry(index: Int, event: GenericMessageEvent) {
|
||||||
val entry: EntryLink = entries[index]
|
val entry: EntryLink = entries.links[index]
|
||||||
bot.send(EntriesUtils.buildLink(index, entry))
|
event.sendMessage(EntriesUtils.buildLink(index, entry))
|
||||||
if (entry.tags.isNotEmpty()) {
|
if (entry.tags.isNotEmpty()) {
|
||||||
bot.send(EntriesUtils.buildTags(index, entry))
|
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||||
}
|
}
|
||||||
if (entry.comments.isNotEmpty()) {
|
if (entry.comments.isNotEmpty()) {
|
||||||
val comments = entry.comments
|
val comments = entry.comments
|
||||||
for (i in comments.indices) {
|
for (i in comments.indices) {
|
||||||
bot.send(EntriesUtils.buildComment(index, i, comments[i]))
|
event.sendMessage(EntriesUtils.buildComment(index, i, comments[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,19 +33,22 @@
|
||||||
package net.thauvin.erik.mobibot.commands.links
|
package net.thauvin.erik.mobibot.commands.links
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Constants
|
import net.thauvin.erik.mobibot.Constants
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
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.sendMessage
|
||||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
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
|
||||||
|
|
||||||
class Tags(bot: Mobibot) : AbstractCommand(bot) {
|
class Tags : AbstractCommand() {
|
||||||
override val name = COMMAND
|
override val name = COMMAND
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
"To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:",
|
"To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:",
|
||||||
helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]")
|
helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]")
|
||||||
)
|
)
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
@ -53,33 +56,27 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
const val COMMAND = "tags"
|
const val COMMAND = "tags"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
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
|
||||||
|
|
||||||
if (index < LinksMgr.entries.size) {
|
if (index < LinksMgr.entries.links.size && LinksMgr.isUpToDate(event)) {
|
||||||
val cmd = cmds[1].trim()
|
val cmd = cmds[1].trim()
|
||||||
val entry: EntryLink = LinksMgr.entries[index]
|
val entry: EntryLink = LinksMgr.entries.links[index]
|
||||||
if (cmd.isNotEmpty()) {
|
if (cmd.isNotEmpty()) {
|
||||||
if (entry.login == login || isOp) {
|
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||||
entry.setTags(cmd)
|
entry.setTags(cmd)
|
||||||
bot.updatePin(entry.link, entry)
|
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||||
bot.send(EntriesUtils.buildTags(index, entry))
|
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||||
LinksMgr.saveEntries(bot, false)
|
LinksMgr.entries.save()
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "Please ask a channel op to change the tags for you.", isPrivate)
|
event.sendMessage("Please ask a channel op to change the tags for you.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (entry.tags.isNotEmpty()) {
|
if (entry.tags.isNotEmpty()) {
|
||||||
bot.send(EntriesUtils.buildTags(index, entry))
|
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "The entry has no tags. Why don't add some?", isPrivate)
|
event.sendMessage("The entry has no tags. Why don't add some?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,23 +32,25 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands.links
|
package net.thauvin.erik.mobibot.commands.links
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries
|
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
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.events.PrivateMessageEvent
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
class View(bot: Mobibot) : AbstractCommand(bot) {
|
class View : AbstractCommand() {
|
||||||
@Suppress("MagicNumber")
|
private val maxEntries = 6
|
||||||
private val maxEntries = 8
|
|
||||||
override val name = VIEW_CMD
|
override val name = VIEW_CMD
|
||||||
override val help = listOf(
|
override val help = listOf(
|
||||||
"To list or search the current URL posts:",
|
"To list or search the current URL posts:",
|
||||||
helpFormat("%c $name [<start>] [<query>]")
|
helpFormat("%c $name [<start>] [<query>]")
|
||||||
)
|
)
|
||||||
override val isOp = false
|
override val isOpOnly = false
|
||||||
override val isPublic = true
|
override val isPublic = true
|
||||||
override val isVisible = true
|
override val isVisible = true
|
||||||
|
|
||||||
|
@ -56,22 +58,16 @@ class View(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
const val VIEW_CMD = "view"
|
const val VIEW_CMD = "view"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (entries.links.isNotEmpty()) {
|
||||||
login: String,
|
showPosts(args, event)
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (entries.size != 0) {
|
|
||||||
showPosts(bot, args, sender)
|
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "There is currently nothing to view. Why don't you post something?", isPrivate)
|
event.sendMessage("There is currently nothing to view. Why don't you post something?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showPosts(bot: Mobibot, args: String, sender: String) {
|
private fun showPosts(args: String, event: GenericMessageEvent) {
|
||||||
val max = entries.size
|
val max = entries.links.size
|
||||||
var lcArgs = args.lowercase()
|
var lcArgs = args.lowercase()
|
||||||
var i = 0
|
var i = 0
|
||||||
if (lcArgs.isEmpty() && max > maxEntries) {
|
if (lcArgs.isEmpty() && max > maxEntries) {
|
||||||
|
@ -97,25 +93,32 @@ class View(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
var entry: EntryLink
|
var entry: EntryLink
|
||||||
var sent = 0
|
var sent = 0
|
||||||
while (i < max && sent < maxEntries) {
|
while (i < max && sent < maxEntries) {
|
||||||
entry = entries[i]
|
entry = entries.links[i]
|
||||||
if (lcArgs.isNotBlank()) {
|
if (lcArgs.isNotBlank()) {
|
||||||
if (entry.matches(lcArgs)) {
|
if (entry.matches(lcArgs)) {
|
||||||
bot.send(sender, EntriesUtils.buildLink(i, entry, true), false)
|
event.sendMessage(EntriesUtils.buildLink(i, entry, true))
|
||||||
sent++
|
sent++
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, EntriesUtils.buildLink(i, entry, true), false)
|
event.sendMessage(EntriesUtils.buildLink(i, entry, true))
|
||||||
sent++
|
sent++
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
if (sent == maxEntries && i < max) {
|
if (sent == maxEntries && i < max) {
|
||||||
bot.send(
|
event.sendMessage("To view more, try: ")
|
||||||
sender, "To view more, try: " + bold("${bot.nick}: $name ${i + 1} $lcArgs"), false
|
event.sendMessage(
|
||||||
|
helpFormat(
|
||||||
|
buildCmdSyntax(
|
||||||
|
"%c $name ${i + 1} $lcArgs",
|
||||||
|
event.bot().nick,
|
||||||
|
event is PrivateMessageEvent
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sent == 0) {
|
if (sent == 0) {
|
||||||
bot.send(sender, "No matches. Please try again.", false)
|
event.sendMessage("No matches. Please try again.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,84 +31,88 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.commands.tell
|
package net.thauvin.erik.mobibot.commands.tell
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||||
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.plural
|
import net.thauvin.erik.mobibot.Utils.plural
|
||||||
import net.thauvin.erik.mobibot.Utils.reverseColor
|
import net.thauvin.erik.mobibot.Utils.reverseColor
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.toIntOrDefault
|
import net.thauvin.erik.mobibot.Utils.toIntOrDefault
|
||||||
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
||||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||||
import net.thauvin.erik.mobibot.commands.links.View
|
import net.thauvin.erik.mobibot.commands.links.View
|
||||||
|
import org.pircbotx.PircBotX
|
||||||
|
import org.pircbotx.hooks.events.MessageEvent
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.pircbotx.hooks.types.GenericUserEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Tell` command.
|
* The `Tell` command.
|
||||||
*/
|
*/
|
||||||
class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
class Tell(private val serialObject: String) : AbstractCommand() {
|
||||||
// Messages queue
|
// Messages queue
|
||||||
private val messages: MutableList<TellMessage> = mutableListOf()
|
private val messages: MutableList<TellMessage> = mutableListOf()
|
||||||
|
|
||||||
// Serialized object file
|
|
||||||
private val serializedObject: String
|
|
||||||
|
|
||||||
// Maximum number of days to keep messages
|
// Maximum number of days to keep messages
|
||||||
@Suppress("MagicNumber")
|
|
||||||
private var maxDays = 7
|
private var maxDays = 7
|
||||||
|
|
||||||
// Message maximum queue size
|
// Message maximum queue size
|
||||||
@Suppress("MagicNumber")
|
|
||||||
private var maxSize = 50
|
private var maxSize = 50
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans the messages queue.
|
* Cleans the messages queue.
|
||||||
*/
|
*/
|
||||||
private fun clean(): Boolean {
|
private fun clean(): Boolean {
|
||||||
if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.")
|
// if (bot.logger.isDebugEnabled) bot.logger.debug("Cleaning the messages.")
|
||||||
return TellMessagesMgr.clean(messages, maxDays.toLong())
|
return TellMessagesMgr.clean(messages, maxDays.toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete message.
|
// Delete message.
|
||||||
private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) {
|
private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
val split = args.split(" ")
|
val split = args.split(" ")
|
||||||
if (split.size == 2) {
|
if (split.size == 2) {
|
||||||
val id = split[1]
|
val id = split[1]
|
||||||
var deleted = false
|
var deleted = false
|
||||||
if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) {
|
if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) {
|
||||||
for (message in messages) {
|
for (message in messages) {
|
||||||
if (message.sender.equals(sender, ignoreCase = true) && message.isReceived) {
|
if (message.sender.equals(event.user.nick, ignoreCase = true) && message.isReceived) {
|
||||||
messages.remove(message)
|
messages.remove(message)
|
||||||
deleted = true
|
deleted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
save()
|
save()
|
||||||
bot.send(sender, "Delivered messages have been deleted.", isPrivate)
|
event.sendMessage("Delivered messages have been deleted.")
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "No delivered messages were found.", isPrivate)
|
event.sendMessage("No delivered messages were found.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var found = false
|
var found = false
|
||||||
for (message in messages) {
|
for (message in messages) {
|
||||||
found = (message.id == id)
|
found = (message.id == id)
|
||||||
if (found && (message.sender.equals(sender, ignoreCase = true) || bot.isOp(sender))) {
|
if (found && (message.sender.equals(event.user.nick, ignoreCase = true) || isChannelOp(
|
||||||
|
channel,
|
||||||
|
event
|
||||||
|
))
|
||||||
|
) {
|
||||||
messages.remove(message)
|
messages.remove(message)
|
||||||
save()
|
save()
|
||||||
bot.send(sender, "Your message was deleted from the queue.", isPrivate)
|
event.sendMessage("Your message was deleted from the queue.")
|
||||||
deleted = true
|
deleted = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!deleted) {
|
if (!deleted) {
|
||||||
if (found) {
|
if (found) {
|
||||||
bot.send(sender, "Only messages that you sent can be deleted.", isPrivate)
|
event.sendMessage("Only messages that you sent can be deleted.")
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "The specified message [ID $id] could not be found.", isPrivate)
|
event.sendMessage("The specified message [ID $id] could not be found.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(args, sender, isOp, isPrivate)
|
helpResponse(channel, args, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,30 +128,24 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
helpFormat("%c $name ${View.VIEW_CMD}"),
|
helpFormat("%c $name ${View.VIEW_CMD}"),
|
||||||
"Messages are kept for ${bold(maxDays)}" + " day".plural(maxDays.toLong()) + '.'
|
"Messages are kept for ${bold(maxDays)}" + " day".plural(maxDays.toLong()) + '.'
|
||||||
)
|
)
|
||||||
override val isOp: Boolean = false
|
override val isOpOnly: Boolean = false
|
||||||
override val isPublic: Boolean = isEnabled()
|
override val isPublic: Boolean = isEnabled()
|
||||||
override val isVisible: Boolean = isEnabled()
|
override val isVisible: Boolean = isEnabled()
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (isEnabled()) {
|
if (isEnabled()) {
|
||||||
if (args.isBlank()) {
|
if (args.isBlank()) {
|
||||||
helpResponse(args, sender, isOp, isPrivate)
|
helpResponse(channel, args, event)
|
||||||
} else if (args.startsWith(View.VIEW_CMD)) {
|
} else if (args.startsWith(View.VIEW_CMD)) {
|
||||||
if (bot.isOp(sender) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) {
|
if (isChannelOp(channel, event) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) {
|
||||||
viewAll(sender, isPrivate)
|
viewAll(event)
|
||||||
} else {
|
} else {
|
||||||
viewMessages(sender, isPrivate)
|
viewMessages(event)
|
||||||
}
|
}
|
||||||
} else if (args.startsWith("$TELL_DEL_KEYWORD ")) {
|
} else if (args.startsWith("$TELL_DEL_KEYWORD ")) {
|
||||||
deleteMessage(sender, args, isOp, isPrivate)
|
deleteMessage(channel, args, event)
|
||||||
} else {
|
} else {
|
||||||
newMessage(sender, args, isOp, isPrivate)
|
newMessage(channel, args, event)
|
||||||
}
|
}
|
||||||
if (clean()) {
|
if (clean()) {
|
||||||
save()
|
save()
|
||||||
|
@ -169,21 +167,19 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New message.
|
// New message.
|
||||||
private fun newMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) {
|
private fun newMessage(channel: String, args: String, event: GenericMessageEvent) {
|
||||||
val split = args.split(" ".toRegex(), 2)
|
val split = args.split(" ".toRegex(), 2)
|
||||||
if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) {
|
if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) {
|
||||||
if (messages.size < maxSize) {
|
if (messages.size < maxSize) {
|
||||||
val message = TellMessage(sender, split[0], split[1].trim())
|
val message = TellMessage(event.user.nick, split[0], split[1].trim())
|
||||||
messages.add(message)
|
messages.add(message)
|
||||||
save()
|
save()
|
||||||
bot.send(
|
event.sendMessage("Message [ID ${message.id}] was queued for ${bold(message.recipient)}")
|
||||||
sender, "Message [ID ${message.id}] was queued for ${bold(message.recipient)}", isPrivate
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "Sorry, the messages queue is currently full.", isPrivate)
|
event.sendMessage("Sorry, the messages queue is currently full.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(args, sender, isOp, isPrivate)
|
helpResponse(channel, args, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,34 +187,30 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
* Saves the messages queue.
|
* Saves the messages queue.
|
||||||
*/
|
*/
|
||||||
private fun save() {
|
private fun save() {
|
||||||
TellMessagesMgr.save(serializedObject, messages, bot.logger)
|
TellMessagesMgr.save(serialObject, messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks and sends messages.
|
* Checks and sends messages.
|
||||||
*/
|
*/
|
||||||
@JvmOverloads
|
fun send(event: GenericUserEvent) {
|
||||||
fun send(nickname: String, isMessage: Boolean = false) {
|
val nickname = event.user.nick
|
||||||
if (isEnabled() && nickname != bot.nick) {
|
if (isEnabled() && nickname != event.getBot<PircBotX>().nick) {
|
||||||
messages.stream().filter { message: TellMessage -> message.isMatch(nickname) }
|
messages.stream().filter { message: TellMessage -> message.isMatch(nickname) }
|
||||||
.forEach { message: TellMessage ->
|
.forEach { message: TellMessage ->
|
||||||
if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) {
|
if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) {
|
||||||
if (message.sender == nickname) {
|
if (message.sender == nickname) {
|
||||||
if (!isMessage) {
|
if (event !is MessageEvent) {
|
||||||
bot.send(
|
event.user.send().message(
|
||||||
nickname,
|
"${bold("You")} wanted me to remind you: ${reverseColor(message.message)}"
|
||||||
"${bold("You")} wanted me to remind you: ${reverseColor(message.message)}",
|
|
||||||
true
|
|
||||||
)
|
)
|
||||||
message.isReceived = true
|
message.isReceived = true
|
||||||
message.isNotified = true
|
message.isNotified = true
|
||||||
save()
|
save()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bot.send(
|
event.user.send().message(
|
||||||
nickname,
|
"${message.sender} wanted me to tell you: ${reverseColor(message.message)}"
|
||||||
"${message.sender} wanted me to tell you: ${reverseColor(message.message)}",
|
|
||||||
true
|
|
||||||
)
|
)
|
||||||
message.isReceived = true
|
message.isReceived = true
|
||||||
save()
|
save()
|
||||||
|
@ -226,11 +218,9 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
} else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived
|
} else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived
|
||||||
&& !message.isNotified
|
&& !message.isNotified
|
||||||
) {
|
) {
|
||||||
bot.send(
|
event.user.send().message(
|
||||||
nickname,
|
"Your message ${reverseColor("[ID ${message.id}]")} was sent to "
|
||||||
"Your message ${reverseColor("[ID " + message.id + ']')} was sent to "
|
+ "${bold(message.recipient)} on ${message.receptionDate}"
|
||||||
+ "${bold(message.recipient)} on ${message.receptionDate.toUtcDateTime()}",
|
|
||||||
true
|
|
||||||
)
|
)
|
||||||
message.isNotified = true
|
message.isNotified = true
|
||||||
save()
|
save()
|
||||||
|
@ -247,66 +237,55 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
fun size(): Int = messages.size
|
fun size(): Int = messages.size
|
||||||
|
|
||||||
// View all messages.
|
// View all messages.
|
||||||
private fun viewAll(sender: String, isPrivate: Boolean) {
|
private fun viewAll(event: GenericMessageEvent) {
|
||||||
if (messages.isNotEmpty()) {
|
if (messages.isNotEmpty()) {
|
||||||
for (message in messages) {
|
for (message in messages) {
|
||||||
bot.send(
|
event.sendMessage(
|
||||||
sender, bold(message.sender) + ARROW + bold(message.recipient)
|
"${bold(message.sender)}$ARROW${bold(message.recipient)} [ID: ${message.id}, " +
|
||||||
+ " [ID: " + message.id + ", "
|
(if (message.isReceived) "DELIVERED]" else "QUEUED]")
|
||||||
+ (if (message.isReceived) "DELIVERED" else "QUEUED") + ']',
|
|
||||||
isPrivate
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "There are no messages in the queue.", isPrivate)
|
event.sendMessage("There are no messages in the queue.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// View messages.
|
// View messages.
|
||||||
private fun viewMessages(sender: String, isPrivate: Boolean) {
|
private fun viewMessages(event: GenericMessageEvent) {
|
||||||
var hasMessage = false
|
var hasMessage = false
|
||||||
for (message in messages) {
|
for (message in messages) {
|
||||||
if (message.isMatch(sender)) {
|
if (message.isMatch(event.user.nick)) {
|
||||||
if (!hasMessage) {
|
if (!hasMessage) {
|
||||||
hasMessage = true
|
hasMessage = true; event.sendMessage("Here are your messages: ")
|
||||||
bot.send(sender, "Here are your messages: ", isPrivate)
|
|
||||||
}
|
}
|
||||||
if (message.isReceived) {
|
if (message.isReceived) {
|
||||||
bot.send(
|
event.sendMessage(
|
||||||
sender,
|
bold(message.sender) + ARROW + bold(message.recipient) +
|
||||||
bold(message.sender) + ARROW + bold(message.recipient)
|
" [${message.receptionDate.toUtcDateTime()}, ID: ${bold(message.id)}, DELIVERED]"
|
||||||
+ " [${message.receptionDate.toUtcDateTime()}, ID: "
|
|
||||||
+ bold(message.id) + ", DELIVERED]",
|
|
||||||
isPrivate
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
bot.send(
|
event.sendMessage(
|
||||||
sender,
|
bold(message.sender) + ARROW + bold(message.recipient) +
|
||||||
bold(message.sender) + ARROW + bold(message.recipient)
|
" [${message.queued.toUtcDateTime()}, ID: ${bold(message.id)}, QUEUED]"
|
||||||
+ " [${message.queued.toUtcDateTime()}, ID: "
|
|
||||||
+ bold(message.id) + ", QUEUED]",
|
|
||||||
isPrivate
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
bot.send(sender, helpFormat(message.message), isPrivate)
|
event.sendMessage(helpFormat(message.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasMessage) {
|
if (!hasMessage) {
|
||||||
bot.send(sender, "You have no messages in the queue.", isPrivate)
|
event.sendMessage("You have no messages in the queue.")
|
||||||
} else {
|
} else {
|
||||||
bot.send(sender, "To delete one or all delivered messages:", isPrivate)
|
event.sendMessage("To delete one or all delivered messages:")
|
||||||
bot.send(
|
event.sendMessage(
|
||||||
sender,
|
|
||||||
helpFormat(
|
helpFormat(
|
||||||
buildCmdSyntax(
|
buildCmdSyntax(
|
||||||
"%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>",
|
"%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>",
|
||||||
bot.nick,
|
event.user.nick,
|
||||||
isPrivate
|
true
|
||||||
)
|
)
|
||||||
),
|
|
||||||
isPrivate
|
|
||||||
)
|
)
|
||||||
bot.send(sender, help.last(), isPrivate)
|
)
|
||||||
|
event.sendMessage(help.last())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,9 +303,6 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
// Arrow
|
// Arrow
|
||||||
private const val ARROW = " --> "
|
private const val ARROW = " --> "
|
||||||
|
|
||||||
// Serialized object file extension
|
|
||||||
private const val SER_EXT = ".ser"
|
|
||||||
|
|
||||||
// All keyword
|
// All keyword
|
||||||
private const val TELL_ALL_KEYWORD = "all"
|
private const val TELL_ALL_KEYWORD = "all"
|
||||||
|
|
||||||
|
@ -341,8 +317,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
||||||
initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP)
|
initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP)
|
||||||
|
|
||||||
// Load the message queue
|
// Load the message queue
|
||||||
serializedObject = bot.logsDir + bot.name + SER_EXT
|
messages.addAll(TellMessagesMgr.load(serialObject))
|
||||||
messages.addAll(TellMessagesMgr.load(serializedObject, bot.logger))
|
|
||||||
if (clean()) {
|
if (clean()) {
|
||||||
save()
|
save()
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,12 +66,12 @@ class TellMessage internal constructor(
|
||||||
var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
|
var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true) if a notification was sent.
|
* Returns {@code true} if a notification was sent.
|
||||||
*/
|
*/
|
||||||
var isNotified = false
|
var isNotified = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true) if the message was received.
|
* Returns {@code true} if the message was received.
|
||||||
*/
|
*/
|
||||||
var isReceived = false
|
var isReceived = false
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|
|
@ -31,10 +31,10 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.commands.tell
|
package net.thauvin.erik.mobibot.commands.tell
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.FileNotFoundException
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.ObjectInputStream
|
import java.io.ObjectInputStream
|
||||||
import java.io.ObjectOutputStream
|
import java.io.ObjectOutputStream
|
||||||
|
@ -42,11 +42,14 @@ import java.nio.file.Files
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Tell Messages Manager.
|
* The Tell Messages Manager.
|
||||||
*/
|
*/
|
||||||
object TellMessagesMgr {
|
object TellMessagesMgr {
|
||||||
|
val logger: Logger = LoggerFactory.getLogger(TellMessagesMgr::class.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans the messages queue.
|
* Cleans the messages queue.
|
||||||
*/
|
*/
|
||||||
|
@ -58,30 +61,30 @@ object TellMessagesMgr {
|
||||||
/**
|
/**
|
||||||
* Loads the messages.
|
* Loads the messages.
|
||||||
*/
|
*/
|
||||||
|
fun load(file: String): List<TellMessage> {
|
||||||
fun load(file: String, logger: Logger): List<TellMessage> {
|
val serialFile = Paths.get(file)
|
||||||
|
if (serialFile.exists()) {
|
||||||
try {
|
try {
|
||||||
ObjectInputStream(
|
ObjectInputStream(
|
||||||
BufferedInputStream(Files.newInputStream(Paths.get(file)))
|
BufferedInputStream(Files.newInputStream(serialFile))
|
||||||
).use { input ->
|
).use { input ->
|
||||||
if (logger.isDebugEnabled) logger.debug("Loading the messages.")
|
if (logger.isDebugEnabled) logger.debug("Loading the messages.")
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return input.readObject() as List<TellMessage>
|
return input.readObject() as List<TellMessage>
|
||||||
}
|
}
|
||||||
} catch (ignore: FileNotFoundException) {
|
|
||||||
// Do nothing
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
logger.error("An IO error occurred loading the messages queue.", e)
|
logger.error("An IO error occurred loading the messages queue.", e)
|
||||||
} catch (e: ClassNotFoundException) {
|
} catch (e: ClassNotFoundException) {
|
||||||
logger.error("An error occurred loading the messages queue.", e)
|
logger.error("An error occurred loading the messages queue.", e)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return listOf()
|
return listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the messages.
|
* Saves the messages.
|
||||||
*/
|
*/
|
||||||
fun save(file: String, messages: List<TellMessage?>?, logger: Logger) {
|
fun save(file: String, messages: List<TellMessage?>?) {
|
||||||
try {
|
try {
|
||||||
BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos ->
|
BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos ->
|
||||||
ObjectOutputStream(bos).use { output ->
|
ObjectOutputStream(bos).use { output ->
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Kill.kt
|
* Entries.kt
|
||||||
*
|
*
|
||||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
@ -30,26 +30,26 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.commands
|
package net.thauvin.erik.mobibot.entries
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.today
|
||||||
|
|
||||||
class Kill(bot: Mobibot) : AbstractCommand(bot) {
|
class Entries(
|
||||||
override val name = "kill"
|
var channel: String = "",
|
||||||
override val help = emptyList<String>()
|
var ircServer: String = "",
|
||||||
override val isOp = true
|
var logsDir: String = "",
|
||||||
override val isPublic = false
|
var backlogs: String = ""
|
||||||
override val isVisible = false
|
|
||||||
|
|
||||||
override fun commandResponse(
|
|
||||||
sender: String,
|
|
||||||
login: String,
|
|
||||||
args: String,
|
|
||||||
isOp: Boolean,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
) {
|
||||||
if (isOp) {
|
val links = mutableListOf<EntryLink>()
|
||||||
bot.shutdown(sender, true)
|
|
||||||
}
|
var lastPubDate = today()
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
lastPubDate = FeedsMgr.loadFeed(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save() {
|
||||||
|
lastPubDate = today()
|
||||||
|
FeedsMgr.saveFeed(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,262 +0,0 @@
|
||||||
/*
|
|
||||||
* EntriesMgr.kt
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* Neither the name of this project nor the names of its contributors may be
|
|
||||||
* used to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package net.thauvin.erik.mobibot.entries
|
|
||||||
|
|
||||||
import com.rometools.rome.feed.synd.SyndContentImpl
|
|
||||||
import com.rometools.rome.feed.synd.SyndEntry
|
|
||||||
import com.rometools.rome.feed.synd.SyndEntryImpl
|
|
||||||
import com.rometools.rome.feed.synd.SyndFeed
|
|
||||||
import com.rometools.rome.feed.synd.SyndFeedImpl
|
|
||||||
import com.rometools.rome.io.FeedException
|
|
||||||
import com.rometools.rome.io.SyndFeedInput
|
|
||||||
import com.rometools.rome.io.SyndFeedOutput
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
import java.io.OutputStreamWriter
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.util.Calendar
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages the feed entries.
|
|
||||||
*/
|
|
||||||
object EntriesMgr {
|
|
||||||
/**
|
|
||||||
* The name of the file containing the current entries.
|
|
||||||
*/
|
|
||||||
const val CURRENT_XML = "current.xml"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the file containing the backlog entries.
|
|
||||||
*/
|
|
||||||
const val NAV_XML = "nav.xml"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The .xml extension
|
|
||||||
*/
|
|
||||||
const val XML_EXT = ".xml"
|
|
||||||
|
|
||||||
// Maximum number of backlogs to keep
|
|
||||||
private const val maxBacklogs = 10
|
|
||||||
|
|
||||||
// Daily backup
|
|
||||||
private fun dailyBackup(
|
|
||||||
bot: Mobibot,
|
|
||||||
history: MutableList<String>
|
|
||||||
) {
|
|
||||||
if (bot.backlogsUrl.isNotBlank()) {
|
|
||||||
if (!history.contains(bot.today)) {
|
|
||||||
history.add(bot.today)
|
|
||||||
while (history.size > maxBacklogs) {
|
|
||||||
history.removeFirst()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OutputStreamWriter(
|
|
||||||
Files.newOutputStream(Paths.get(bot.logsDir + NAV_XML)), StandardCharsets.UTF_8
|
|
||||||
).use { fw ->
|
|
||||||
val output = SyndFeedOutput()
|
|
||||||
val rss: SyndFeed = SyndFeedImpl()
|
|
||||||
val items: MutableList<SyndEntry> = mutableListOf()
|
|
||||||
var item: SyndEntry
|
|
||||||
with(rss) {
|
|
||||||
feedType = "rss_2.0"
|
|
||||||
title = "${bot.channel} IRC Links Backlogs"
|
|
||||||
description = "Backlogs of Links from ${bot.ircServer} on ${bot.channel}"
|
|
||||||
link = bot.backlogsUrl
|
|
||||||
publishedDate = Calendar.getInstance().time
|
|
||||||
}
|
|
||||||
var date: String
|
|
||||||
items.clear()
|
|
||||||
for (i in history.size - 1 downTo 0) {
|
|
||||||
date = history[i]
|
|
||||||
item = SyndEntryImpl()
|
|
||||||
with(item) {
|
|
||||||
link = bot.backlogsUrl + date + ".xml"
|
|
||||||
title = date
|
|
||||||
description = SyndContentImpl().apply { value = "Links for $date" }
|
|
||||||
}
|
|
||||||
items.add(item)
|
|
||||||
}
|
|
||||||
rss.entries = items
|
|
||||||
if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the backlog feed.")
|
|
||||||
output.output(rss, fw)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (bot.logger.isErrorEnabled) {
|
|
||||||
bot.logger.warn("Unable to generate the backlogs feed. No property configured.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the backlogs.
|
|
||||||
*/
|
|
||||||
@Throws(IOException::class, FeedException::class)
|
|
||||||
fun loadBacklogs(file: String, history: MutableList<String>) {
|
|
||||||
history.clear()
|
|
||||||
val input = SyndFeedInput()
|
|
||||||
InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8).use { reader ->
|
|
||||||
val feed = input.build(reader)
|
|
||||||
val items = feed.entries
|
|
||||||
for (i in items.indices.reversed()) {
|
|
||||||
history.add(items[i].title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the current entries.
|
|
||||||
*/
|
|
||||||
@Throws(IOException::class, FeedException::class)
|
|
||||||
fun loadEntries(file: String, channel: String, entries: MutableList<EntryLink>): String {
|
|
||||||
entries.clear()
|
|
||||||
val input = SyndFeedInput()
|
|
||||||
var today: String
|
|
||||||
InputStreamReader(
|
|
||||||
Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8
|
|
||||||
).use { reader ->
|
|
||||||
val feed = input.build(reader)
|
|
||||||
today = feed.publishedDate.toIsoLocalDate()
|
|
||||||
val items = feed.entries
|
|
||||||
var entry: EntryLink
|
|
||||||
for (i in items.indices.reversed()) {
|
|
||||||
with(items[i]) {
|
|
||||||
entry = EntryLink(
|
|
||||||
link,
|
|
||||||
title,
|
|
||||||
author.substring(author.lastIndexOf('(') + 1, author.length - 1),
|
|
||||||
channel,
|
|
||||||
publishedDate,
|
|
||||||
categories
|
|
||||||
)
|
|
||||||
var split: List<String>
|
|
||||||
for (comment in description.value.split("<br/>")) {
|
|
||||||
split = comment.split(": ".toRegex(), 2)
|
|
||||||
if (split.size == 2) {
|
|
||||||
entry.addComment(comment = split[1].trim(), nick = split[0].trim())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entries.add(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return today
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the entries.
|
|
||||||
*/
|
|
||||||
fun saveEntries(
|
|
||||||
bot: Mobibot,
|
|
||||||
entries: List<EntryLink>,
|
|
||||||
history: MutableList<String>,
|
|
||||||
isDayBackup: Boolean
|
|
||||||
) {
|
|
||||||
if (bot.logger.isDebugEnabled) bot.logger.debug("Saving the feeds...")
|
|
||||||
if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val output = SyndFeedOutput()
|
|
||||||
val rss: SyndFeed = SyndFeedImpl()
|
|
||||||
val items: MutableList<SyndEntry> = mutableListOf()
|
|
||||||
var item: SyndEntry
|
|
||||||
OutputStreamWriter(
|
|
||||||
Files.newOutputStream(Paths.get(bot.logsDir + CURRENT_XML)), StandardCharsets.UTF_8
|
|
||||||
).use { fw ->
|
|
||||||
with(rss) {
|
|
||||||
feedType = "rss_2.0"
|
|
||||||
title = bot.channel + " IRC Links"
|
|
||||||
description = "Links from ${bot.ircServer} on ${bot.channel}"
|
|
||||||
link = bot.weblogUrl
|
|
||||||
publishedDate = Calendar.getInstance().time
|
|
||||||
language = "en"
|
|
||||||
}
|
|
||||||
val buff: StringBuilder = StringBuilder()
|
|
||||||
for (i in entries.size - 1 downTo 0) {
|
|
||||||
with(entries[i]) {
|
|
||||||
buff.setLength(0)
|
|
||||||
buff.append("Posted by <b>")
|
|
||||||
.append(nick)
|
|
||||||
.append("</b> on <a href=\"irc://")
|
|
||||||
.append(bot.ircServer).append('/')
|
|
||||||
.append(channel)
|
|
||||||
.append("\"><b>")
|
|
||||||
.append(channel)
|
|
||||||
.append("</b></a>")
|
|
||||||
if (comments.size > 0) {
|
|
||||||
buff.append(" <br/><br/>")
|
|
||||||
for (j in comments.indices) {
|
|
||||||
if (j > 0) {
|
|
||||||
buff.append(" <br/>")
|
|
||||||
}
|
|
||||||
buff.append(comments[j].nick).append(": ").append(comments[j].comment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item = SyndEntryImpl()
|
|
||||||
item.link = link
|
|
||||||
item.description = SyndContentImpl().apply { value = buff.toString() }
|
|
||||||
item.title = title
|
|
||||||
item.publishedDate = date
|
|
||||||
item.author = "${bot.channel.substring(1)}@${bot.ircServer} ($nick)"
|
|
||||||
item.categories = tags
|
|
||||||
items.add(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rss.entries = items
|
|
||||||
if (bot.logger.isDebugEnabled) bot.logger.debug("Writing the entries feed.")
|
|
||||||
output.output(rss, fw)
|
|
||||||
}
|
|
||||||
OutputStreamWriter(
|
|
||||||
Files.newOutputStream(
|
|
||||||
Paths.get(
|
|
||||||
bot.logsDir + bot.today + XML_EXT
|
|
||||||
)
|
|
||||||
), StandardCharsets.UTF_8
|
|
||||||
).use { fw -> output.output(rss, fw) }
|
|
||||||
if (isDayBackup) {
|
|
||||||
dailyBackup(bot, history)
|
|
||||||
}
|
|
||||||
} catch (e: FeedException) {
|
|
||||||
if (bot.logger.isWarnEnabled) bot.logger.warn("Unable to generate the entries feed.", e)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
if (bot.logger.isWarnEnabled)
|
|
||||||
bot.logger.warn("An IO error occurred while generating the entries feed.", e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (bot.logger.isWarnEnabled) {
|
|
||||||
bot.logger.warn("Unable to generate the entries feed. A required property is missing.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -40,22 +40,22 @@ import net.thauvin.erik.mobibot.Utils.green
|
||||||
*/
|
*/
|
||||||
object EntriesUtils {
|
object EntriesUtils {
|
||||||
/**
|
/**
|
||||||
* Build link cmd based on its index. e.g: L1
|
* Build link label based on its index. e.g: L1
|
||||||
*/
|
*/
|
||||||
fun buildLinkCmd(index: Int): String = Constants.LINK_CMD + (index + 1)
|
fun buildLinkLabel(index: Int): String = Constants.LINK_CMD + (index + 1)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an entry's comment for display on the channel.
|
* Builds an entry's comment for display on the channel.
|
||||||
*/
|
*/
|
||||||
fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String =
|
fun buildComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String =
|
||||||
("${buildLinkCmd(entryIndex)}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}")
|
("${buildLinkLabel(entryIndex)}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an entry's link for display on the channel.
|
* Builds an entry's link for display on the channel.
|
||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String {
|
fun buildLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String {
|
||||||
val buff = StringBuilder().append(buildLinkCmd(entryIndex)).append(": ")
|
val buff = StringBuilder().append(buildLinkLabel(entryIndex)).append(": ")
|
||||||
.append('[').append(entry.nick).append(']')
|
.append('[').append(entry.nick).append(']')
|
||||||
if (isView && entry.comments.isNotEmpty()) {
|
if (isView && entry.comments.isNotEmpty()) {
|
||||||
buff.append("[+").append(entry.comments.size).append(']')
|
buff.append("[+").append(entry.comments.size).append(']')
|
||||||
|
@ -76,5 +76,5 @@ object EntriesUtils {
|
||||||
* Build an entry's tags/categories for display on the channel.
|
* Build an entry's tags/categories for display on the channel.
|
||||||
*/
|
*/
|
||||||
fun buildTags(entryIndex: Int, entry: EntryLink): String =
|
fun buildTags(entryIndex: Int, entry: EntryLink): String =
|
||||||
buildLinkCmd(entryIndex) + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ")
|
buildLinkLabel(entryIndex) + "${Constants.TAG_CMD}: " + entry.pinboardTags.replace(",", ", ")
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,16 +109,25 @@ class EntryLink : Serializable {
|
||||||
*/
|
*/
|
||||||
fun addComment(comment: String, nick: String): Int {
|
fun addComment(comment: String, nick: String): Int {
|
||||||
comments.add(EntryComment(comment, nick))
|
comments.add(EntryComment(comment, nick))
|
||||||
return comments.size - 1
|
return comments.lastIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a specific comment.
|
* Deletes a specific comment.
|
||||||
*/
|
*/
|
||||||
fun deleteComment(index: Int) {
|
fun deleteComment(index: Int): Boolean {
|
||||||
if (index < comments.size) {
|
if (index < comments.size) {
|
||||||
comments.removeAt(index)
|
comments.removeAt(index)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a comment.
|
||||||
|
*/
|
||||||
|
fun deleteComment(entryComment: EntryComment): Boolean {
|
||||||
|
return comments.remove(entryComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,7 +163,7 @@ class EntryLink : Serializable {
|
||||||
* Sets a comment.
|
* Sets a comment.
|
||||||
*/
|
*/
|
||||||
fun setComment(index: Int, comment: String?, nick: String?) {
|
fun setComment(index: Int, comment: String?, nick: String?) {
|
||||||
if (index < comments.size && (comment != null) && !nick.isNullOrBlank()) {
|
if (index < comments.size && !comment.isNullOrBlank() && !nick.isNullOrBlank()) {
|
||||||
comments[index] = EntryComment(comment, nick)
|
comments[index] = EntryComment(comment, nick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
190
src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt
Normal file
190
src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsMgr.kt
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* FeedsMgr.kt
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* Neither the name of this project nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package net.thauvin.erik.mobibot.entries
|
||||||
|
|
||||||
|
import com.rometools.rome.feed.synd.SyndContentImpl
|
||||||
|
import com.rometools.rome.feed.synd.SyndEntry
|
||||||
|
import com.rometools.rome.feed.synd.SyndEntryImpl
|
||||||
|
import com.rometools.rome.feed.synd.SyndFeed
|
||||||
|
import com.rometools.rome.feed.synd.SyndFeedImpl
|
||||||
|
import com.rometools.rome.io.FeedException
|
||||||
|
import com.rometools.rome.io.SyndFeedInput
|
||||||
|
import com.rometools.rome.io.SyndFeedOutput
|
||||||
|
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||||
|
import net.thauvin.erik.mobibot.Utils.today
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.io.OutputStreamWriter
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.Calendar
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the RSS feeds.
|
||||||
|
*/
|
||||||
|
class FeedsMgr private constructor() {
|
||||||
|
companion object {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(FeedsMgr::class.java)
|
||||||
|
|
||||||
|
// The file containing the current entries.
|
||||||
|
private const val currentXml = "current.xml"
|
||||||
|
|
||||||
|
// The .xml extension.
|
||||||
|
private const val dotXml = ".xml"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the current feed.
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class, FeedException::class)
|
||||||
|
fun loadFeed(entries: Entries, currentFile: String = currentXml): String {
|
||||||
|
entries.links.clear()
|
||||||
|
val xml = Paths.get("${entries.logsDir}${currentFile}")
|
||||||
|
var pubDate = today()
|
||||||
|
if (xml.exists()) {
|
||||||
|
val input = SyndFeedInput()
|
||||||
|
InputStreamReader(
|
||||||
|
Files.newInputStream(xml), StandardCharsets.UTF_8
|
||||||
|
).use { reader ->
|
||||||
|
val feed = input.build(reader)
|
||||||
|
pubDate = feed.publishedDate.toIsoLocalDate()
|
||||||
|
val items = feed.entries
|
||||||
|
var entry: EntryLink
|
||||||
|
for (i in items.indices.reversed()) {
|
||||||
|
with(items[i]) {
|
||||||
|
entry = EntryLink(
|
||||||
|
link,
|
||||||
|
title,
|
||||||
|
author.substring(author.lastIndexOf('(') + 1, author.length - 1),
|
||||||
|
entries.channel,
|
||||||
|
publishedDate,
|
||||||
|
categories
|
||||||
|
)
|
||||||
|
var split: List<String>
|
||||||
|
for (comment in description.value.split("<br/>")) {
|
||||||
|
split = comment.split(": ".toRegex(), 2)
|
||||||
|
if (split.size == 2) {
|
||||||
|
entry.addComment(comment = split[1].trim(), nick = split[0].trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries.links.add(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create an empty feed.
|
||||||
|
saveFeed(entries)
|
||||||
|
}
|
||||||
|
return pubDate
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the feeds.
|
||||||
|
*/
|
||||||
|
fun saveFeed(entries: Entries, currentFile: String = currentXml) {
|
||||||
|
if (logger.isDebugEnabled) logger.debug("Saving the feeds...")
|
||||||
|
if (entries.logsDir.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
val output = SyndFeedOutput()
|
||||||
|
val rss: SyndFeed = SyndFeedImpl()
|
||||||
|
val items: MutableList<SyndEntry> = mutableListOf()
|
||||||
|
var item: SyndEntry
|
||||||
|
OutputStreamWriter(
|
||||||
|
Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8
|
||||||
|
).use { fw ->
|
||||||
|
with(rss) {
|
||||||
|
feedType = "rss_2.0"
|
||||||
|
title = "${entries.channel} IRC Links"
|
||||||
|
description = "Links from ${entries.ircServer} on ${entries.channel}"
|
||||||
|
if (entries.backlogs.isNotBlank()) link = entries.backlogs
|
||||||
|
publishedDate = Calendar.getInstance().time
|
||||||
|
language = "en"
|
||||||
|
}
|
||||||
|
val buff: StringBuilder = StringBuilder()
|
||||||
|
for (i in entries.links.indices.reversed()) {
|
||||||
|
with(entries.links[i]) {
|
||||||
|
buff.setLength(0)
|
||||||
|
buff.append("Posted by <b>")
|
||||||
|
.append(nick)
|
||||||
|
.append("</b> on <a href=\"irc://")
|
||||||
|
.append(entries.ircServer).append('/')
|
||||||
|
.append(channel)
|
||||||
|
.append("\"><b>")
|
||||||
|
.append(channel)
|
||||||
|
.append("</b></a>")
|
||||||
|
if (comments.size > 0) {
|
||||||
|
buff.append(" <br/><br/>")
|
||||||
|
for (j in comments.indices) {
|
||||||
|
if (j > 0) {
|
||||||
|
buff.append(" <br/>")
|
||||||
|
}
|
||||||
|
buff.append(comments[j].nick).append(": ").append(comments[j].comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item = SyndEntryImpl()
|
||||||
|
item.link = link
|
||||||
|
item.description = SyndContentImpl().apply { value = buff.toString() }
|
||||||
|
item.title = title
|
||||||
|
item.publishedDate = date
|
||||||
|
item.author = "${channel.substring(1)}@${entries.ircServer} ($nick)"
|
||||||
|
item.categories = tags
|
||||||
|
items.add(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rss.entries = items
|
||||||
|
if (logger.isDebugEnabled) logger.debug("Writing the entries feed.")
|
||||||
|
output.output(rss, fw)
|
||||||
|
}
|
||||||
|
OutputStreamWriter(
|
||||||
|
Files.newOutputStream(
|
||||||
|
Paths.get(
|
||||||
|
entries.logsDir + today() + dotXml
|
||||||
|
)
|
||||||
|
), StandardCharsets.UTF_8
|
||||||
|
).use { fw -> output.output(rss, fw) }
|
||||||
|
} catch (e: FeedException) {
|
||||||
|
if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
if (logger.isWarnEnabled)
|
||||||
|
logger.warn("An IO error occurred while generating the entries feed.", e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (logger.isWarnEnabled) {
|
||||||
|
logger.warn("Unable to generate the entries feed. A required property is missing.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,13 +31,16 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
|
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Module` abstract class.
|
* The `Module` abstract class.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractModule(val bot: Mobibot) {
|
abstract class AbstractModule {
|
||||||
/**
|
/**
|
||||||
* The module's commands, if any.
|
* The module's commands, if any.
|
||||||
*/
|
*/
|
||||||
|
@ -51,12 +54,7 @@ abstract class AbstractModule(val bot: Mobibot) {
|
||||||
/**
|
/**
|
||||||
* Responds to a command.
|
* Responds to a command.
|
||||||
*/
|
*/
|
||||||
abstract fun commandResponse(
|
abstract fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)
|
||||||
sender: String,
|
|
||||||
cmd: String,
|
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the module's property keys.
|
* Returns the module's property keys.
|
||||||
|
@ -74,9 +72,9 @@ abstract class AbstractModule(val bot: Mobibot) {
|
||||||
/**
|
/**
|
||||||
* Responds with the module's help.
|
* Responds with the module's help.
|
||||||
*/
|
*/
|
||||||
open fun helpResponse(sender: String, isPrivate: Boolean): Boolean {
|
open fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||||
for (h in help) {
|
for (h in help) {
|
||||||
bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate)
|
event.sendMessage(buildCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,35 +33,32 @@ package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.objecthunter.exp4j.ExpressionBuilder
|
import net.objecthunter.exp4j.ExpressionBuilder
|
||||||
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
|
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Calc module.
|
* The Calc module.
|
||||||
*/
|
*/
|
||||||
class Calc(bot: Mobibot) : AbstractModule(bot) {
|
class Calc : AbstractModule() {
|
||||||
override fun commandResponse(
|
private val logger: Logger = LoggerFactory.getLogger(Calc::class.java)
|
||||||
sender: String,
|
|
||||||
cmd: String,
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (args.isNotBlank()) {
|
if (args.isNotBlank()) {
|
||||||
with(bot) {
|
|
||||||
try {
|
try {
|
||||||
send(calculate(args))
|
event.respond(calculate(args))
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
||||||
send("No idea. This is the kind of math I don't get.")
|
event.respond("No idea. This is the kind of math I don't get.")
|
||||||
} catch (e: UnknownFunctionOrVariableException) {
|
} catch (e: UnknownFunctionOrVariableException) {
|
||||||
if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e)
|
if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e)
|
||||||
send("No idea. I must've some form of Dyscalculia.")
|
event.respond("No idea. I must've some form of Dyscalculia.")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,20 +34,24 @@ package net.thauvin.erik.mobibot.modules
|
||||||
import net.thauvin.erik.crypto.CryptoException
|
import net.thauvin.erik.crypto.CryptoException
|
||||||
import net.thauvin.erik.crypto.CryptoPrice
|
import net.thauvin.erik.crypto.CryptoPrice
|
||||||
import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice
|
import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Cryptocurrency Prices module.
|
* The Cryptocurrency Prices module.
|
||||||
*/
|
*/
|
||||||
class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) {
|
class CryptoPrices : ThreadedModule() {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the cryptocurrency market price from [Coinbase](https://developers.coinbase.com/api/v2#get-spot-price).
|
* Returns the cryptocurrency market price from [Coinbase](https://developers.coinbase.com/api/v2#get-spot-price).
|
||||||
*/
|
*/
|
||||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
val debugMessage = "crypto($cmd $args)"
|
val debugMessage = "crypto($cmd $args)"
|
||||||
with(bot) {
|
|
||||||
if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) {
|
if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) {
|
||||||
try {
|
try {
|
||||||
val price = currentPrice(args.split(' '))
|
val price = currentPrice(args.split(' '))
|
||||||
|
@ -56,18 +60,18 @@ class CryptoPrices(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
} catch (ignore: IllegalArgumentException) {
|
} catch (ignore: IllegalArgumentException) {
|
||||||
price.amount
|
price.amount
|
||||||
}
|
}
|
||||||
send(sender, PublicMessage("${price.base}: $amount [${price.currency}]"))
|
event.respond("${price.base} current price is $amount [${price.currency}]")
|
||||||
} catch (e: CryptoException) {
|
} catch (e: CryptoException) {
|
||||||
if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e)
|
if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e)
|
||||||
send(e.message)
|
event.sendMessage(e.message!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: IOException) {
|
||||||
if (logger.isErrorEnabled) logger.error(debugMessage, e)
|
if (logger.isErrorEnabled) logger.error(debugMessage, e)
|
||||||
send("An error has occurred while retrieving the cryptocurrency market price.")
|
event.sendMessage("An IO error has occurred while retrieving the cryptocurrency market price.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -31,16 +31,21 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendList
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.today
|
import net.thauvin.erik.mobibot.Utils.today
|
||||||
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.jdom2.JDOMException
|
import org.jdom2.JDOMException
|
||||||
import org.jdom2.input.SAXBuilder
|
import org.jdom2.input.SAXBuilder
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
|
@ -51,26 +56,22 @@ import javax.xml.XMLConstants
|
||||||
/**
|
/**
|
||||||
* The CurrencyConverter module.
|
* The CurrencyConverter module.
|
||||||
*/
|
*/
|
||||||
class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
|
class CurrencyConverter : ThreadedModule() {
|
||||||
override fun commandResponse(
|
private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java)
|
||||||
sender: String,
|
|
||||||
cmd: String,
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (pubDate != today()) {
|
if (pubDate != today()) {
|
||||||
EXCHANGE_RATES.clear()
|
EXCHANGE_RATES.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.commandResponse(sender, cmd, args, isPrivate)
|
super.commandResponse(channel, cmd, args, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the specified currencies.
|
* Converts the specified currencies.
|
||||||
*/
|
*/
|
||||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
bot.apply {
|
|
||||||
if (EXCHANGE_RATES.isEmpty()) {
|
if (EXCHANGE_RATES.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
loadRates()
|
loadRates()
|
||||||
|
@ -80,55 +81,43 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EXCHANGE_RATES.isEmpty()) {
|
if (EXCHANGE_RATES.isEmpty()) {
|
||||||
send(sender, EMPTY_RATE_TABLE, true)
|
event.respond(EMPTY_RATE_TABLE)
|
||||||
} else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) {
|
} else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) {
|
||||||
val msg = convertCurrency(args)
|
val msg = convertCurrency(args)
|
||||||
send(sender, msg)
|
event.respond(msg.msg)
|
||||||
if (msg.isError) {
|
if (msg.isError) {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
}
|
}
|
||||||
} else if (args.contains(CURRENCY_RATES_KEYWORD)) {
|
} else if (args.contains(CURRENCY_RATES_KEYWORD)) {
|
||||||
send(sender, "The reference rates for ${bold(pubDate)} are:", isPrivate)
|
event.sendMessage("The reference rates for ${bold(pubDate)} are:")
|
||||||
@Suppress("MagicNumber")
|
event.sendList(currencyRates(), 3, " ", isIndent = true)
|
||||||
sendList(sender, currencyRates(), 3, " ", isPrivate, isIndent = true)
|
|
||||||
} else {
|
} else {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun helpResponse(sender: String, isPrivate: Boolean): Boolean {
|
override fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||||
with(bot) {
|
|
||||||
if (EXCHANGE_RATES.isEmpty()) {
|
if (EXCHANGE_RATES.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
loadRates()
|
loadRates()
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
if (logger.isDebugEnabled) logger.debug(e.debugMessage, e)
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (EXCHANGE_RATES.isEmpty()) {
|
if (EXCHANGE_RATES.isEmpty()) {
|
||||||
send(sender, EMPTY_RATE_TABLE, isPrivate)
|
event.sendMessage(EMPTY_RATE_TABLE)
|
||||||
} else {
|
} else {
|
||||||
send(sender, "To convert from one currency to another:", isPrivate)
|
val nick = event.bot().nick
|
||||||
send(
|
event.sendMessage("To convert from one currency to another:")
|
||||||
sender,
|
event.sendMessage(helpFormat(buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)))
|
||||||
helpFormat(
|
event.sendMessage("For a listing of current reference rates:")
|
||||||
buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)
|
event.sendMessage(
|
||||||
),
|
|
||||||
isPrivate
|
|
||||||
)
|
|
||||||
send(sender, "For a listing of current reference rates:", isPrivate)
|
|
||||||
send(
|
|
||||||
sender,
|
|
||||||
helpFormat(
|
helpFormat(
|
||||||
buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled)
|
buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled)
|
||||||
),
|
|
||||||
isPrivate
|
|
||||||
)
|
)
|
||||||
send(sender, "The supported currencies are: ", isPrivate)
|
)
|
||||||
@Suppress("MagicNumber")
|
event.sendMessage("The supported currencies are: ")
|
||||||
sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate = isPrivate, isIndent = true)
|
event.sendList(ArrayList(EXCHANGE_RATES.keys), 11, isIndent = true)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -161,7 +150,6 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
/**
|
/**
|
||||||
* Converts from a currency to another.
|
* Converts from a currency to another.
|
||||||
*/
|
*/
|
||||||
@Suppress("MagicNumber")
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun convertCurrency(query: String): Message {
|
fun convertCurrency(query: String): Message {
|
||||||
val cmds = query.split(" ")
|
val cmds = query.split(" ")
|
||||||
|
@ -194,7 +182,6 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
fun currencyRates(): List<String> {
|
fun currencyRates(): List<String> {
|
||||||
val rates = mutableListOf<String>()
|
val rates = mutableListOf<String>()
|
||||||
for ((key, value) in EXCHANGE_RATES.toSortedMap()) {
|
for ((key, value) in EXCHANGE_RATES.toSortedMap()) {
|
||||||
@Suppress("MagicNumber")
|
|
||||||
rates.add("$key: ${value.padStart(8)}")
|
rates.add("$key: ${value.padStart(8)}")
|
||||||
}
|
}
|
||||||
return rates
|
return rates
|
||||||
|
|
|
@ -31,37 +31,33 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Dice module.
|
* The Dice module.
|
||||||
*/
|
*/
|
||||||
class Dice(bot: Mobibot) : AbstractModule(bot) {
|
class Dice : AbstractModule() {
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
|
||||||
cmd: String,
|
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
val botRoll = roll()
|
val botRoll = roll()
|
||||||
val roll = roll()
|
val roll = roll()
|
||||||
val botTotal = botRoll.first + botRoll.second
|
val botTotal = botRoll.first + botRoll.second
|
||||||
val total = roll.first + roll.second
|
val total = roll.first + roll.second
|
||||||
with(bot) {
|
with(event.bot()) {
|
||||||
send(
|
event.respond(
|
||||||
channel,
|
"you rolled ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]} for a total of ${bold(total)}"
|
||||||
"$sender rolled ${total}: ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]}",
|
|
||||||
isPrivate
|
|
||||||
)
|
)
|
||||||
action(
|
sendIRC().action(
|
||||||
"rolled ${botTotal}: ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]}"
|
channel,
|
||||||
|
"rolled ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]} for a total of ${bold(botTotal)}"
|
||||||
)
|
)
|
||||||
when (winLoseOrTie(botTotal, total)) {
|
when (winLoseOrTie(botTotal, total)) {
|
||||||
Result.WIN -> action("wins.")
|
Result.WIN -> sendIRC().action(channel, "wins.")
|
||||||
Result.LOSE -> action("lost.")
|
Result.LOSE -> sendIRC().action(channel, "lost.")
|
||||||
else -> action("tied.")
|
else -> sendIRC().action(channel, "tied.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,29 +31,34 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||||
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.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
||||||
import net.thauvin.erik.mobibot.Utils.urlReader
|
import net.thauvin.erik.mobibot.Utils.urlReader
|
||||||
|
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.NoticeMessage
|
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||||
import org.jibble.pircbot.Colors
|
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import org.pircbotx.Colors
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GoogleSearch module.
|
* The GoogleSearch module.
|
||||||
*/
|
*/
|
||||||
class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) {
|
class GoogleSearch : ThreadedModule() {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches Google.
|
* Searches Google.
|
||||||
*/
|
*/
|
||||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
with(bot) {
|
|
||||||
if (args.isNotBlank()) {
|
if (args.isNotBlank()) {
|
||||||
try {
|
try {
|
||||||
val results = searchGoogle(
|
val results = searchGoogle(
|
||||||
|
@ -61,15 +66,14 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
properties[GOOGLE_CSE_KEY_PROP]
|
properties[GOOGLE_CSE_KEY_PROP]
|
||||||
)
|
)
|
||||||
for (msg in results) {
|
for (msg in results) {
|
||||||
send(sender, msg)
|
event.sendMessage(channel, msg)
|
||||||
}
|
}
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
send(sender, e.message, isPrivate)
|
event.sendMessage(e.message!!)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,29 +96,33 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) {
|
if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) {
|
||||||
throw ModuleException("${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing.")
|
throw ModuleException("${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing.")
|
||||||
}
|
}
|
||||||
return if (query.isNotBlank()) {
|
|
||||||
val results = mutableListOf<Message>()
|
val results = mutableListOf<Message>()
|
||||||
|
if (query.isNotBlank()) {
|
||||||
try {
|
try {
|
||||||
val url = URL(
|
val url = URL(
|
||||||
"https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" +
|
"https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" +
|
||||||
"&q=${encodeUrl(query)}&filter=1&num=5&alt=json"
|
"&q=${encodeUrl(query)}&filter=1&num=5&alt=json"
|
||||||
)
|
)
|
||||||
val json = JSONObject(urlReader(url))
|
val json = JSONObject(urlReader(url))
|
||||||
|
if (json.has("items")) {
|
||||||
val ja = json.getJSONArray("items")
|
val ja = json.getJSONArray("items")
|
||||||
for (i in 0 until ja.length()) {
|
for (i in 0 until ja.length()) {
|
||||||
val j = ja.getJSONObject(i)
|
val j = ja.getJSONObject(i)
|
||||||
results.add(NoticeMessage(unescapeXml(j.getString("title"))))
|
results.add(NoticeMessage(unescapeXml(j.getString("title"))))
|
||||||
results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN))
|
results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
results.add(ErrorMessage("No results found.", Colors.RED))
|
||||||
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e)
|
throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e)
|
||||||
} catch (e: JSONException) {
|
} catch (e: JSONException) {
|
||||||
throw ModuleException("searchGoogle($query)", "A JSON error has occurred searching Google.", e)
|
throw ModuleException("searchGoogle($query)", "A JSON error has occurred searching Google.", e)
|
||||||
}
|
}
|
||||||
results
|
|
||||||
} else {
|
} else {
|
||||||
throw ModuleException("Invalid query. Please try again.")
|
results.add(ErrorMessage("Invalid query. Please try again."))
|
||||||
}
|
}
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,42 +33,43 @@ package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.cyan
|
import net.thauvin.erik.mobibot.Utils.cyan
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.urlReader
|
import net.thauvin.erik.mobibot.Utils.urlReader
|
||||||
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.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Joke module.
|
* The Joke module.
|
||||||
*/
|
*/
|
||||||
class Joke(bot: Mobibot) : ThreadedModule(bot) {
|
class Joke : ThreadedModule() {
|
||||||
override fun commandResponse(
|
private val logger: Logger = LoggerFactory.getLogger(Joke::class.java)
|
||||||
sender: String,
|
|
||||||
cmd: String,
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch { run(sender, cmd, args, isPrivate) }
|
launch { run(channel, cmd, args, event) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a random joke from [The Internet Chuck Norris Database](http://www.icndb.com/).
|
* Returns a random joke from [The Internet Chuck Norris Database](http://www.icndb.com/).
|
||||||
*/
|
*/
|
||||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
with(bot) {
|
with(event.bot()) {
|
||||||
try {
|
try {
|
||||||
send(cyan(randomJoke().msg))
|
sendIRC().notice(channel, cyan(randomJoke().msg))
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
send(sender, e.message, isPrivate)
|
event.sendMessage(e.message!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,12 @@
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Constants
|
import net.thauvin.erik.mobibot.Constants
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import org.apache.commons.net.whois.WhoisClient
|
import org.apache.commons.net.whois.WhoisClient
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
|
@ -42,19 +45,13 @@ import java.net.UnknownHostException
|
||||||
/**
|
/**
|
||||||
* The Lookup module.
|
* The Lookup module.
|
||||||
*/
|
*/
|
||||||
class Lookup(bot: Mobibot) : AbstractModule(bot) {
|
class Lookup : AbstractModule() {
|
||||||
override fun commandResponse(
|
private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java)
|
||||||
sender: String,
|
|
||||||
cmd: String,
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (args.matches("(\\S.)+(\\S)+".toRegex())) {
|
if (args.matches("(\\S.)+(\\S)+".toRegex())) {
|
||||||
with(bot) {
|
|
||||||
try {
|
try {
|
||||||
nslookup(args).split(',').forEach {
|
event.respondWith(nslookup(args).prependIndent())
|
||||||
send(it.trim())
|
|
||||||
}
|
|
||||||
} catch (ignore: UnknownHostException) {
|
} catch (ignore: UnknownHostException) {
|
||||||
if (args.matches(
|
if (args.matches(
|
||||||
("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
|
("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
|
||||||
|
@ -66,28 +63,33 @@ class Lookup(bot: Mobibot) : AbstractModule(bot) {
|
||||||
val lines = whois(args)
|
val lines = whois(args)
|
||||||
if (lines.isNotEmpty()) {
|
if (lines.isNotEmpty()) {
|
||||||
var line: String
|
var line: String
|
||||||
|
var hasData = false
|
||||||
for (rawLine in lines) {
|
for (rawLine in lines) {
|
||||||
line = rawLine.trim()
|
line = rawLine.trim()
|
||||||
if (line.isNotEmpty() && line[0] != '#') {
|
if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) {
|
||||||
send(line)
|
if (!hasData) {
|
||||||
|
event.respondWith(line)
|
||||||
|
hasData = true
|
||||||
|
} else {
|
||||||
|
event.bot().sendIRC().notice(event.user.nick, line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
send("Unknown host.")
|
event.respond("Unknown host.")
|
||||||
}
|
}
|
||||||
} catch (ioe: IOException) {
|
} catch (ioe: IOException) {
|
||||||
if (logger.isDebugEnabled) {
|
if (logger.isWarnEnabled) {
|
||||||
logger.debug("Unable to perform whois IP lookup: $args", ioe)
|
logger.warn("Unable to perform whois IP lookup: $args", ioe)
|
||||||
}
|
}
|
||||||
send("Unable to perform whois IP lookup: ${ioe.message}")
|
event.respond("Unable to perform whois IP lookup: ${ioe.message}")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
send("Unknown host.")
|
event.respond("Unknown host.")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(sender, true)
|
helpResponse(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,24 +31,20 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Ping module.
|
* The Ping module.
|
||||||
*/
|
*/
|
||||||
class Ping(bot: Mobibot) : AbstractModule(bot) {
|
class Ping : AbstractModule() {
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
event.bot().sendIRC().action(channel, randomPing())
|
||||||
cmd: String,
|
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
bot.action(randomPing())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -32,16 +32,17 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import net.thauvin.erik.mobibot.Utils.bot
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple module example in Kotlin.
|
* Simple module example in Kotlin.
|
||||||
*/
|
*/
|
||||||
class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) {
|
class RockPaperScissors : AbstractModule() {
|
||||||
init {
|
init {
|
||||||
with(commands) {
|
with(commands) {
|
||||||
add(Hands.ROCK.name.lowercase())
|
add(Hands.ROCK.name.lowercase())
|
||||||
|
@ -101,20 +102,26 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
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.values()[Random.nextInt(0, Hands.values().size)]
|
val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)]
|
||||||
with(bot) {
|
with(event.bot()) {
|
||||||
send("${hand.emoji} vs. ${botHand.emoji}")
|
sendIRC().message(channel, "${hand.emoji} vs. ${botHand.emoji}")
|
||||||
when {
|
when {
|
||||||
hand == botHand -> {
|
hand == botHand -> {
|
||||||
action("tied.")
|
sendIRC().action(channel, "tied.")
|
||||||
}
|
}
|
||||||
hand.beats(botHand) -> {
|
hand.beats(botHand) -> {
|
||||||
action("lost. ${hand.name.capitalise()} ${hand.action} ${botHand.name.lowercase()}.")
|
sendIRC().action(
|
||||||
|
channel,
|
||||||
|
"lost. ${hand.name.capitalise()} ${hand.action} ${botHand.name.lowercase()}."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
action("wins. ${botHand.name.capitalise()} ${botHand.action} ${hand.name.lowercase()}.")
|
sendIRC().action(
|
||||||
|
channel,
|
||||||
|
"wins. ${botHand.name.capitalise()} ${botHand.action} ${hand.name.lowercase()}."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,10 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||||
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.sendMessage
|
||||||
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
import net.thauvin.erik.mobibot.Utils.unescapeXml
|
||||||
import net.thauvin.erik.mobibot.Utils.urlReader
|
import net.thauvin.erik.mobibot.Utils.urlReader
|
||||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||||
|
@ -43,31 +43,34 @@ import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The StockQuote module.
|
* The StockQuote module.
|
||||||
*/
|
*/
|
||||||
class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
class StockQuote : ThreadedModule() {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the specified stock quote from Alpha Vantage.
|
* Returns the specified stock quote from Alpha Vantage.
|
||||||
*/
|
*/
|
||||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
with(bot) {
|
|
||||||
if (args.isNotBlank()) {
|
if (args.isNotBlank()) {
|
||||||
try {
|
try {
|
||||||
val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP])
|
val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP])
|
||||||
for (msg in messages) {
|
for (msg in messages) {
|
||||||
send(sender, msg)
|
event.sendMessage(channel, msg)
|
||||||
}
|
}
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
send(e.message)
|
event.sendMessage(e.message!!)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,9 +132,9 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
"${STOCK_CMD.capitalise()} is disabled. The API key is missing."
|
"${STOCK_CMD.capitalise()} is disabled. The API key is missing."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return if (symbol.isNotBlank()) {
|
|
||||||
val debugMessage = "getQuote($symbol)"
|
|
||||||
val messages = mutableListOf<Message>()
|
val messages = mutableListOf<Message>()
|
||||||
|
if (symbol.isNotBlank()) {
|
||||||
|
val debugMessage = "getQuote($symbol)"
|
||||||
var response: String
|
var response: String
|
||||||
try {
|
try {
|
||||||
with(messages) {
|
with(messages) {
|
||||||
|
@ -161,8 +164,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
val quote = json.getJSONObject("Global Quote")
|
val quote = json.getJSONObject("Global Quote")
|
||||||
if (quote.isEmpty) {
|
if (quote.isEmpty) {
|
||||||
add(ErrorMessage(INVALID_SYMBOL))
|
add(ErrorMessage(INVALID_SYMBOL))
|
||||||
return messages
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
add(
|
add(
|
||||||
PublicMessage(
|
PublicMessage(
|
||||||
|
@ -171,7 +173,6 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
val pad = 10
|
val pad = 10
|
||||||
|
|
||||||
add(
|
add(
|
||||||
|
@ -213,15 +214,16 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
throw ModuleException(debugMessage, "An IO error has occurred retrieving a stock quote.", e)
|
throw ModuleException(debugMessage, "An IO error has occurred retrieving a stock quote.", e)
|
||||||
} catch (e: NullPointerException) {
|
} catch (e: NullPointerException) {
|
||||||
throw ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e)
|
throw ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e)
|
||||||
}
|
}
|
||||||
messages
|
|
||||||
} else {
|
} else {
|
||||||
throw ModuleException(INVALID_SYMBOL)
|
messages.add(ErrorMessage(INVALID_SYMBOL))
|
||||||
}
|
}
|
||||||
|
return messages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,36 +33,26 @@ package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `ThreadedModule` class.
|
* The `ThreadedModule` class.
|
||||||
*/
|
*/
|
||||||
abstract class ThreadedModule(bot: Mobibot) : AbstractModule(bot) {
|
abstract class ThreadedModule : AbstractModule() {
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (isEnabled && event.message.isNotEmpty()) {
|
||||||
cmd: String,
|
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
if (isEnabled && args.isNotEmpty()) {
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch {
|
launch {
|
||||||
run(sender, cmd, args, isPrivate)
|
run(channel, cmd, args, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the thread.
|
* Runs the thread.
|
||||||
*/
|
*/
|
||||||
abstract fun run(
|
abstract fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent)
|
||||||
sender: String,
|
|
||||||
cmd: String,
|
|
||||||
args: String,
|
|
||||||
isPrivate: Boolean
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,21 +34,26 @@ package net.thauvin.erik.mobibot.modules
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.thauvin.erik.mobibot.Constants
|
import net.thauvin.erik.mobibot.Constants
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.TwitterTimer
|
import net.thauvin.erik.mobibot.TwitterTimer
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||||
import net.thauvin.erik.mobibot.msg.Message
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import twitter4j.TwitterException
|
import twitter4j.TwitterException
|
||||||
import twitter4j.TwitterFactory
|
import twitter4j.TwitterFactory
|
||||||
import twitter4j.conf.ConfigurationBuilder
|
import twitter4j.conf.ConfigurationBuilder
|
||||||
|
import java.util.Timer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Twitter module.
|
* The Twitter module.
|
||||||
*/
|
*/
|
||||||
class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
class Twitter : ThreadedModule() {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(Twitter::class.java)
|
||||||
|
|
||||||
|
private val timer = Timer(true)
|
||||||
|
|
||||||
// Twitter auto-posts.
|
// Twitter auto-posts.
|
||||||
private val entries: MutableSet<Int> = HashSet()
|
private val entries: MutableSet<Int> = HashSet()
|
||||||
|
|
||||||
|
@ -87,7 +92,6 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
* Send a notification to the registered Twitter handle.
|
* Send a notification to the registered Twitter handle.
|
||||||
*/
|
*/
|
||||||
fun notification(msg: String) {
|
fun notification(msg: String) {
|
||||||
with(bot) {
|
|
||||||
if (isEnabled && !handle.isNullOrBlank()) {
|
if (isEnabled && !handle.isNullOrBlank()) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch {
|
launch {
|
||||||
|
@ -101,13 +105,12 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts on Twitter.
|
* Posts on Twitter.
|
||||||
*/
|
*/
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun post(handle: String = "${properties[HANDLE_PROP]}", message: String, isDm: Boolean): Message {
|
fun post(handle: String = "${properties[HANDLE_PROP]}", message: String, isDm: Boolean): String {
|
||||||
return twitterPost(
|
return twitterPost(
|
||||||
properties[CONSUMER_KEY_PROP],
|
properties[CONSUMER_KEY_PROP],
|
||||||
properties[CONSUMER_SECRET_PROP],
|
properties[CONSUMER_SECRET_PROP],
|
||||||
|
@ -123,15 +126,14 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
* Post an entry to twitter.
|
* Post an entry to twitter.
|
||||||
*/
|
*/
|
||||||
fun postEntry(index: Int) {
|
fun postEntry(index: Int) {
|
||||||
with(bot) {
|
if (isAutoPost && hasEntry(index) && LinksMgr.entries.links.size >= index) {
|
||||||
if (isAutoPost && hasEntry(index) && LinksMgr.entries.size >= index) {
|
val entry = LinksMgr.entries.links[index]
|
||||||
val entry = LinksMgr.entries[index]
|
val msg = "${entry.title} ${entry.link} via ${entry.nick} on ${entry.channel}"
|
||||||
val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel"
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch {
|
launch {
|
||||||
try {
|
try {
|
||||||
if (logger.isDebugEnabled) {
|
if (logger.isDebugEnabled) {
|
||||||
logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkCmd(index))
|
logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkLabel(index))
|
||||||
}
|
}
|
||||||
post(message = msg, isDm = false)
|
post(message = msg, isDm = false)
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
|
@ -142,16 +144,14 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
removeEntry(index)
|
removeEntry(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun queueEntry(index: Int) {
|
fun queueEntry(index: Int) {
|
||||||
if (isAutoPost) {
|
if (isAutoPost) {
|
||||||
addEntry(index)
|
addEntry(index)
|
||||||
if (bot.logger.isDebugEnabled) {
|
if (logger.isDebugEnabled) {
|
||||||
bot.logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkCmd(index))
|
logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkLabel(index))
|
||||||
}
|
}
|
||||||
@Suppress("MagicNumber")
|
timer.schedule(TwitterTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L)
|
||||||
bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,18 +162,12 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
/**
|
/**
|
||||||
* Posts to twitter.
|
* Posts to twitter.
|
||||||
*/
|
*/
|
||||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
with(bot) {
|
|
||||||
try {
|
try {
|
||||||
send(
|
event.respond(post(event.user.nick, "$args (by ${event.user.nick} on $channel)", false))
|
||||||
sender,
|
|
||||||
post(sender, "$args (by $sender on $channel)", false).msg,
|
|
||||||
isPrivate
|
|
||||||
)
|
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
send(sender, e.message, isPrivate)
|
event.respond(e.message)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +175,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
* Post all the entries to Twitter on shutdown.
|
* Post all the entries to Twitter on shutdown.
|
||||||
*/
|
*/
|
||||||
fun shutdown() {
|
fun shutdown() {
|
||||||
|
timer.cancel()
|
||||||
if (isAutoPost) {
|
if (isAutoPost) {
|
||||||
for (index in entries) {
|
for (index in entries) {
|
||||||
postEntry(index)
|
postEntry(index)
|
||||||
|
@ -213,23 +208,23 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
handle: String?,
|
handle: String?,
|
||||||
message: String,
|
message: String,
|
||||||
isDm: Boolean
|
isDm: Boolean
|
||||||
): Message {
|
): String {
|
||||||
return try {
|
return try {
|
||||||
val cb = ConfigurationBuilder()
|
val cb = ConfigurationBuilder().apply {
|
||||||
cb.setDebugEnabled(true)
|
setDebugEnabled(true)
|
||||||
.setOAuthConsumerKey(consumerKey)
|
setOAuthConsumerKey(consumerKey)
|
||||||
.setOAuthConsumerSecret(consumerSecret)
|
setOAuthConsumerSecret(consumerSecret)
|
||||||
.setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret)
|
setOAuthAccessToken(token)
|
||||||
|
setOAuthAccessTokenSecret(tokenSecret)
|
||||||
|
}
|
||||||
val tf = TwitterFactory(cb.build())
|
val tf = TwitterFactory(cb.build())
|
||||||
val twitter = tf.instance
|
val twitter = tf.instance
|
||||||
if (!isDm) {
|
if (!isDm) {
|
||||||
val status = twitter.updateStatus(message)
|
val status = twitter.updateStatus(message)
|
||||||
NoticeMessage(
|
"Your message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}"
|
||||||
"You message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}"
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
val dm = twitter.sendDirectMessage(handle, message)
|
val dm = twitter.sendDirectMessage(handle, message)
|
||||||
NoticeMessage(dm.text)
|
dm.text
|
||||||
}
|
}
|
||||||
} catch (e: TwitterException) {
|
} catch (e: TwitterException) {
|
||||||
throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e)
|
throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e)
|
||||||
|
|
|
@ -35,45 +35,48 @@ import net.aksingh.owmjapis.api.APIException
|
||||||
import net.aksingh.owmjapis.core.OWM
|
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.Mobibot
|
|
||||||
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.capitalise
|
||||||
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
|
||||||
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
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.NoticeMessage
|
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||||
import org.jibble.pircbot.Colors
|
import org.pircbotx.Colors
|
||||||
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Weather2` module.
|
* The `Weather2` module.
|
||||||
*/
|
*/
|
||||||
class Weather2(bot: Mobibot) : ThreadedModule(bot) {
|
class Weather2 : ThreadedModule() {
|
||||||
|
private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the weather data from a specific city.
|
* Fetches the weather data from a specific city.
|
||||||
*/
|
*/
|
||||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
if (args.isNotBlank()) {
|
if (args.isNotBlank()) {
|
||||||
with(bot) {
|
|
||||||
try {
|
try {
|
||||||
val messages = getWeather(args, properties[OWM_API_KEY_PROP])
|
val messages = getWeather(args, properties[OWM_API_KEY_PROP])
|
||||||
if (messages[0].isError) {
|
if (messages[0].isError) {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
} else {
|
} else {
|
||||||
for (msg in messages) {
|
for (msg in messages) {
|
||||||
send(sender, msg)
|
event.sendMessage(channel, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
if (logger.isDebugEnabled) logger.debug(e.debugMessage, e)
|
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||||
send(e.message)
|
event.respond(e.message)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helpResponse(sender, isPrivate)
|
helpResponse(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +93,6 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
* Converts and rounds temperature from °F to °C.
|
* Converts and rounds temperature from °F to °C.
|
||||||
*/
|
*/
|
||||||
fun ftoC(d: Double?): Pair<Int, Int> {
|
fun ftoC(d: Double?): Pair<Int, Int> {
|
||||||
@Suppress("MagicNumber")
|
|
||||||
val c = (d!! - 32) * 5 / 9
|
val c = (d!! - 32) * 5 / 9
|
||||||
return d.roundToInt() to c.roundToInt()
|
return d.roundToInt() to c.roundToInt()
|
||||||
}
|
}
|
||||||
|
@ -208,7 +210,6 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) {
|
||||||
* Converts and rounds temperature from mph to km/h.
|
* Converts and rounds temperature from mph to km/h.
|
||||||
*/
|
*/
|
||||||
fun mphToKmh(w: Double): Pair<Int, Int> {
|
fun mphToKmh(w: Double): Pair<Int, Int> {
|
||||||
@Suppress("MagicNumber")
|
|
||||||
val kmh = w * 1.60934
|
val kmh = w * 1.60934
|
||||||
return w.roundToInt() to kmh.roundToInt()
|
return w.roundToInt() to kmh.roundToInt()
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,11 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Mobibot
|
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
import net.thauvin.erik.mobibot.Utils.sendList
|
||||||
import net.thauvin.erik.mobibot.msg.Message
|
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
@ -46,7 +45,7 @@ import java.util.Collections
|
||||||
/**
|
/**
|
||||||
* The WorldTime module.
|
* The WorldTime module.
|
||||||
*/
|
*/
|
||||||
class WorldTime(bot: Mobibot) : AbstractModule(bot) {
|
class WorldTime : AbstractModule() {
|
||||||
companion object {
|
companion object {
|
||||||
// Beats (Internet Time) keyword
|
// Beats (Internet Time) keyword
|
||||||
const val BEATS_KEYWORD = ".beats"
|
const val BEATS_KEYWORD = ".beats"
|
||||||
|
@ -57,6 +56,12 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) {
|
||||||
// The Time command
|
// The Time command
|
||||||
private const val TIME_CMD = "time"
|
private const val TIME_CMD = "time"
|
||||||
|
|
||||||
|
// The zones arguments
|
||||||
|
private const val ZONES_ARGS = "zones"
|
||||||
|
|
||||||
|
// The default zone
|
||||||
|
private const val DEFAULT_ZONE = "PST"
|
||||||
|
|
||||||
// Date/Time Format
|
// Date/Time Format
|
||||||
private var dtf =
|
private var dtf =
|
||||||
DateTimeFormatter.ofPattern("'The time is ${bold("'HH:mm'")} on ${bold("'EEEE, d MMMM yyyy'")} in '")
|
DateTimeFormatter.ofPattern("'The time is ${bold("'HH:mm'")} on ${bold("'EEEE, d MMMM yyyy'")} in '")
|
||||||
|
@ -64,342 +69,316 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) {
|
||||||
/**
|
/**
|
||||||
* Returns the current Internet (beat) Time.
|
* Returns the current Internet (beat) Time.
|
||||||
*/
|
*/
|
||||||
@Suppress("MagicNumber", "ImplicitDefaultLocale")
|
|
||||||
private fun internetTime(): String {
|
private fun internetTime(): String {
|
||||||
val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00"))
|
val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00"))
|
||||||
val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60
|
val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60
|
||||||
+ zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt()
|
+ zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt()
|
||||||
return String.format("%c%03d", '@', beats)
|
return "%c%03d".format('@', beats)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the time for the given timezone/city.
|
* Returns the time for the given timezone/city.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun time(query: String): Message {
|
fun time(query: String = DEFAULT_ZONE): String {
|
||||||
val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).uppercase()]
|
val tz = COUNTRIES_MAP[(if (query.isNotBlank()) query.trim().uppercase() else DEFAULT_ZONE)]
|
||||||
val response: String = if (tz != null) {
|
return if (tz != null) {
|
||||||
if (BEATS_KEYWORD == tz) {
|
if (BEATS_KEYWORD == tz) {
|
||||||
"The current Internet Time is: ${bold(internetTime())} $BEATS_KEYWORD"
|
"The current Internet Time is ${bold(internetTime())} $BEATS_KEYWORD"
|
||||||
} else {
|
} else {
|
||||||
(ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf)
|
(ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf)
|
||||||
+ bold(tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ')))
|
+ bold(tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ')))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return ErrorMessage("Unsupported country/zone. Please try again.")
|
"Unsupported country/zone. Please try again."
|
||||||
}
|
}
|
||||||
return PublicMessage(response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Initialize the countries map
|
// Initialize the zones map
|
||||||
val countries = mutableMapOf<String, String>()
|
val zones = mutableMapOf<String, String>()
|
||||||
countries["AD"] = "Europe/Andorra"
|
zones["AD"] = "Europe/Andorra"
|
||||||
countries["AE"] = "Asia/Dubai"
|
zones["AE"] = "Asia/Dubai"
|
||||||
countries["AF"] = "Asia/Kabul"
|
zones["AF"] = "Asia/Kabul"
|
||||||
countries["AG"] = "America/Antigua"
|
zones["AG"] = "America/Antigua"
|
||||||
countries["AI"] = "America/Anguilla"
|
zones["AI"] = "America/Anguilla"
|
||||||
countries["AKDT"] = "America/Anchorage"
|
zones["AKDT"] = "America/Anchorage"
|
||||||
countries["AKST"] = "America/Anchorage"
|
zones["AKST"] = "America/Anchorage"
|
||||||
countries["AL"] = "Europe/Tirane"
|
zones["AL"] = "Europe/Tirane"
|
||||||
countries["AM"] = "Asia/Yerevan"
|
zones["AM"] = "Asia/Yerevan"
|
||||||
countries["AO"] = "Africa/Luanda"
|
zones["AO"] = "Africa/Luanda"
|
||||||
countries["AQ"] = "Antarctica/South_Pole"
|
zones["AQ"] = "Antarctica/South_Pole"
|
||||||
countries["AR"] = "America/Argentina/Buenos_Aires"
|
zones["AR"] = "America/Argentina/Buenos_Aires"
|
||||||
countries["AS"] = "Pacific/Pago_Pago"
|
zones["AS"] = "Pacific/Pago_Pago"
|
||||||
countries["AT"] = "Europe/Vienna"
|
zones["AT"] = "Europe/Vienna"
|
||||||
countries["AU"] = "Australia/Sydney"
|
zones["AU"] = "Australia/Sydney"
|
||||||
countries["AW"] = "America/Aruba"
|
zones["AW"] = "America/Aruba"
|
||||||
countries["AX"] = "Europe/Mariehamn"
|
zones["AX"] = "Europe/Mariehamn"
|
||||||
countries["AZ"] = "Asia/Baku"
|
zones["AZ"] = "Asia/Baku"
|
||||||
countries["BA"] = "Europe/Sarajevo"
|
zones["BA"] = "Europe/Sarajevo"
|
||||||
countries["BB"] = "America/Barbados"
|
zones["BB"] = "America/Barbados"
|
||||||
countries["BD"] = "Asia/Dhaka"
|
zones["BD"] = "Asia/Dhaka"
|
||||||
countries["BE"] = "Europe/Brussels"
|
zones["BE"] = "Europe/Brussels"
|
||||||
countries["BEAT"] = BEATS_KEYWORD
|
zones["BEAT"] = BEATS_KEYWORD
|
||||||
countries["BF"] = "Africa/Ouagadougou"
|
zones["BF"] = "Africa/Ouagadougou"
|
||||||
countries["BG"] = "Europe/Sofia"
|
zones["BG"] = "Europe/Sofia"
|
||||||
countries["BH"] = "Asia/Bahrain"
|
zones["BH"] = "Asia/Bahrain"
|
||||||
countries["BI"] = "Africa/Bujumbura"
|
zones["BI"] = "Africa/Bujumbura"
|
||||||
countries["BJ"] = "Africa/Porto-Novo"
|
zones["BJ"] = "Africa/Porto-Novo"
|
||||||
countries["BL"] = "America/St_Barthelemy"
|
zones["BL"] = "America/St_Barthelemy"
|
||||||
countries["BM"] = "Atlantic/Bermuda"
|
zones["BM"] = "Atlantic/Bermuda"
|
||||||
countries["BMT"] = BEATS_KEYWORD
|
zones["BMT"] = BEATS_KEYWORD
|
||||||
countries["BN"] = "Asia/Brunei"
|
zones["BN"] = "Asia/Brunei"
|
||||||
countries["BO"] = "America/La_Paz"
|
zones["BO"] = "America/La_Paz"
|
||||||
countries["BQ"] = "America/Kralendijk"
|
zones["BQ"] = "America/Kralendijk"
|
||||||
countries["BR"] = "America/Sao_Paulo"
|
zones["BR"] = "America/Sao_Paulo"
|
||||||
countries["BS"] = "America/Nassau"
|
zones["BS"] = "America/Nassau"
|
||||||
countries["BT"] = "Asia/Thimphu"
|
zones["BT"] = "Asia/Thimphu"
|
||||||
countries["BW"] = "Africa/Gaborone"
|
zones["BW"] = "Africa/Gaborone"
|
||||||
countries["BY"] = "Europe/Minsk"
|
zones["BY"] = "Europe/Minsk"
|
||||||
countries["BZ"] = "America/Belize"
|
zones["BZ"] = "America/Belize"
|
||||||
countries["CA"] = "America/Montreal"
|
zones["CA"] = "America/Montreal"
|
||||||
countries["CC"] = "Indian/Cocos"
|
zones["CC"] = "Indian/Cocos"
|
||||||
countries["CD"] = "Africa/Kinshasa"
|
zones["CD"] = "Africa/Kinshasa"
|
||||||
countries["CDT"] = "America/Chicago"
|
zones["CDT"] = "America/Chicago"
|
||||||
countries["CET"] = "CET"
|
zones["CET"] = "CET"
|
||||||
countries["CF"] = "Africa/Bangui"
|
zones["CF"] = "Africa/Bangui"
|
||||||
countries["CG"] = "Africa/Brazzaville"
|
zones["CG"] = "Africa/Brazzaville"
|
||||||
countries["CH"] = "Europe/Zurich"
|
zones["CH"] = "Europe/Zurich"
|
||||||
countries["CI"] = "Africa/Abidjan"
|
zones["CI"] = "Africa/Abidjan"
|
||||||
countries["CK"] = "Pacific/Rarotonga"
|
zones["CK"] = "Pacific/Rarotonga"
|
||||||
countries["CL"] = "America/Santiago"
|
zones["CL"] = "America/Santiago"
|
||||||
countries["CM"] = "Africa/Douala"
|
zones["CM"] = "Africa/Douala"
|
||||||
countries["CN"] = "Asia/Shanghai"
|
zones["CN"] = "Asia/Shanghai"
|
||||||
countries["CO"] = "America/Bogota"
|
zones["CO"] = "America/Bogota"
|
||||||
countries["CR"] = "America/Costa_Rica"
|
zones["CR"] = "America/Costa_Rica"
|
||||||
countries["CST"] = "America/Chicago"
|
zones["CST"] = "America/Chicago"
|
||||||
countries["CU"] = "Cuba"
|
zones["CU"] = "Cuba"
|
||||||
countries["CV"] = "Atlantic/Cape_Verde"
|
zones["CV"] = "Atlantic/Cape_Verde"
|
||||||
countries["CW"] = "America/Curacao"
|
zones["CW"] = "America/Curacao"
|
||||||
countries["CX"] = "Indian/Christmas"
|
zones["CX"] = "Indian/Christmas"
|
||||||
countries["CY"] = "Asia/Nicosia"
|
zones["CY"] = "Asia/Nicosia"
|
||||||
countries["CZ"] = "Europe/Prague"
|
zones["CZ"] = "Europe/Prague"
|
||||||
countries["DE"] = "Europe/Berlin"
|
zones["DE"] = "Europe/Berlin"
|
||||||
countries["DJ"] = "Africa/Djibouti"
|
zones["DJ"] = "Africa/Djibouti"
|
||||||
countries["DK"] = "Europe/Copenhagen"
|
zones["DK"] = "Europe/Copenhagen"
|
||||||
countries["DM"] = "America/Dominica"
|
zones["DM"] = "America/Dominica"
|
||||||
countries["DO"] = "America/Santo_Domingo"
|
zones["DO"] = "America/Santo_Domingo"
|
||||||
countries["DZ"] = "Africa/Algiers"
|
zones["DZ"] = "Africa/Algiers"
|
||||||
countries["EC"] = "Pacific/Galapagos"
|
zones["EC"] = "Pacific/Galapagos"
|
||||||
countries["EDT"] = "America/New_York"
|
zones["EDT"] = "America/New_York"
|
||||||
countries["EE"] = "Europe/Tallinn"
|
zones["EE"] = "Europe/Tallinn"
|
||||||
countries["EG"] = "Africa/Cairo"
|
zones["EG"] = "Africa/Cairo"
|
||||||
countries["EH"] = "Africa/El_Aaiun"
|
zones["EH"] = "Africa/El_Aaiun"
|
||||||
countries["ER"] = "Africa/Asmara"
|
zones["ER"] = "Africa/Asmara"
|
||||||
countries["ES"] = "Europe/Madrid"
|
zones["ES"] = "Europe/Madrid"
|
||||||
countries["EST"] = "America/New_York"
|
zones["EST"] = "America/New_York"
|
||||||
countries["ET"] = "Africa/Addis_Ababa"
|
zones["ET"] = "Africa/Addis_Ababa"
|
||||||
countries["FI"] = "Europe/Helsinki"
|
zones["FI"] = "Europe/Helsinki"
|
||||||
countries["FJ"] = "Pacific/Fiji"
|
zones["FJ"] = "Pacific/Fiji"
|
||||||
countries["FK"] = "Atlantic/Stanley"
|
zones["FK"] = "Atlantic/Stanley"
|
||||||
countries["FM"] = "Pacific/Yap"
|
zones["FM"] = "Pacific/Yap"
|
||||||
countries["FO"] = "Atlantic/Faroe"
|
zones["FO"] = "Atlantic/Faroe"
|
||||||
countries["FR"] = "Europe/Paris"
|
zones["FR"] = "Europe/Paris"
|
||||||
countries["GA"] = "Africa/Libreville"
|
zones["GA"] = "Africa/Libreville"
|
||||||
countries["GB"] = "Europe/London"
|
zones["GB"] = "Europe/London"
|
||||||
countries["GD"] = "America/Grenada"
|
zones["GD"] = "America/Grenada"
|
||||||
countries["GE"] = "Asia/Tbilisi"
|
zones["GE"] = "Asia/Tbilisi"
|
||||||
countries["GF"] = "America/Cayenne"
|
zones["GF"] = "America/Cayenne"
|
||||||
countries["GG"] = "Europe/Guernsey"
|
zones["GG"] = "Europe/Guernsey"
|
||||||
countries["GH"] = "Africa/Accra"
|
zones["GH"] = "Africa/Accra"
|
||||||
countries["GI"] = "Europe/Gibraltar"
|
zones["GI"] = "Europe/Gibraltar"
|
||||||
countries["GL"] = "America/Thule"
|
zones["GL"] = "America/Thule"
|
||||||
countries["GM"] = "Africa/Banjul"
|
zones["GM"] = "Africa/Banjul"
|
||||||
countries["GMT"] = "GMT"
|
zones["GMT"] = "GMT"
|
||||||
countries["GN"] = "Africa/Conakry"
|
zones["GN"] = "Africa/Conakry"
|
||||||
countries["GP"] = "America/Guadeloupe"
|
zones["GP"] = "America/Guadeloupe"
|
||||||
countries["GQ"] = "Africa/Malabo"
|
zones["GQ"] = "Africa/Malabo"
|
||||||
countries["GR"] = "Europe/Athens"
|
zones["GR"] = "Europe/Athens"
|
||||||
countries["GS"] = "Atlantic/South_Georgia"
|
zones["GS"] = "Atlantic/South_Georgia"
|
||||||
countries["GT"] = "America/Guatemala"
|
zones["GT"] = "America/Guatemala"
|
||||||
countries["GU"] = "Pacific/Guam"
|
zones["GU"] = "Pacific/Guam"
|
||||||
countries["GW"] = "Africa/Bissau"
|
zones["GW"] = "Africa/Bissau"
|
||||||
countries["GY"] = "America/Guyana"
|
zones["GY"] = "America/Guyana"
|
||||||
countries["HK"] = "Asia/Hong_Kong"
|
zones["HK"] = "Asia/Hong_Kong"
|
||||||
countries["HN"] = "America/Tegucigalpa"
|
zones["HN"] = "America/Tegucigalpa"
|
||||||
countries["HR"] = "Europe/Zagreb"
|
zones["HR"] = "Europe/Zagreb"
|
||||||
countries["HST"] = "Pacific/Honolulu"
|
zones["HST"] = "Pacific/Honolulu"
|
||||||
countries["HT"] = "America/Port-au-Prince"
|
zones["HT"] = "America/Port-au-Prince"
|
||||||
countries["HU"] = "Europe/Budapest"
|
zones["HU"] = "Europe/Budapest"
|
||||||
countries["ID"] = "Asia/Jakarta"
|
zones["ID"] = "Asia/Jakarta"
|
||||||
countries["IE"] = "Europe/Dublin"
|
zones["IE"] = "Europe/Dublin"
|
||||||
countries["IL"] = "Asia/Tel_Aviv"
|
zones["IL"] = "Asia/Tel_Aviv"
|
||||||
countries["IM"] = "Europe/Isle_of_Man"
|
zones["IM"] = "Europe/Isle_of_Man"
|
||||||
countries["IN"] = "Asia/Kolkata"
|
zones["IN"] = "Asia/Kolkata"
|
||||||
countries["IO"] = "Indian/Chagos"
|
zones["IO"] = "Indian/Chagos"
|
||||||
countries["IQ"] = "Asia/Baghdad"
|
zones["IQ"] = "Asia/Baghdad"
|
||||||
countries["IR"] = "Asia/Tehran"
|
zones["IR"] = "Asia/Tehran"
|
||||||
countries["IS"] = "Atlantic/Reykjavik"
|
zones["IS"] = "Atlantic/Reykjavik"
|
||||||
countries["IT"] = "Europe/Rome"
|
zones["IT"] = "Europe/Rome"
|
||||||
countries["JE"] = "Europe/Jersey"
|
zones["JE"] = "Europe/Jersey"
|
||||||
countries["JM"] = "Jamaica"
|
zones["JM"] = "Jamaica"
|
||||||
countries["JO"] = "Asia/Amman"
|
zones["JO"] = "Asia/Amman"
|
||||||
countries["JP"] = "Asia/Tokyo"
|
zones["JP"] = "Asia/Tokyo"
|
||||||
countries["KE"] = "Africa/Nairobi"
|
zones["KE"] = "Africa/Nairobi"
|
||||||
countries["KG"] = "Asia/Bishkek"
|
zones["KG"] = "Asia/Bishkek"
|
||||||
countries["KH"] = "Asia/Phnom_Penh"
|
zones["KH"] = "Asia/Phnom_Penh"
|
||||||
countries["KI"] = "Pacific/Tarawa"
|
zones["KI"] = "Pacific/Tarawa"
|
||||||
countries["KM"] = "Indian/Comoro"
|
zones["KM"] = "Indian/Comoro"
|
||||||
countries["KN"] = "America/St_Kitts"
|
zones["KN"] = "America/St_Kitts"
|
||||||
countries["KP"] = "Asia/Pyongyang"
|
zones["KP"] = "Asia/Pyongyang"
|
||||||
countries["KR"] = "Asia/Seoul"
|
zones["KR"] = "Asia/Seoul"
|
||||||
countries["KW"] = "Asia/Riyadh"
|
zones["KW"] = "Asia/Riyadh"
|
||||||
countries["KY"] = "America/Cayman"
|
zones["KY"] = "America/Cayman"
|
||||||
countries["KZ"] = "Asia/Oral"
|
zones["KZ"] = "Asia/Oral"
|
||||||
countries["LA"] = "Asia/Vientiane"
|
zones["LA"] = "Asia/Vientiane"
|
||||||
countries["LB"] = "Asia/Beirut"
|
zones["LB"] = "Asia/Beirut"
|
||||||
countries["LC"] = "America/St_Lucia"
|
zones["LC"] = "America/St_Lucia"
|
||||||
countries["LI"] = "Europe/Vaduz"
|
zones["LI"] = "Europe/Vaduz"
|
||||||
countries["LK"] = "Asia/Colombo"
|
zones["LK"] = "Asia/Colombo"
|
||||||
countries["LR"] = "Africa/Monrovia"
|
zones["LR"] = "Africa/Monrovia"
|
||||||
countries["LS"] = "Africa/Maseru"
|
zones["LS"] = "Africa/Maseru"
|
||||||
countries["LT"] = "Europe/Vilnius"
|
zones["LT"] = "Europe/Vilnius"
|
||||||
countries["LU"] = "Europe/Luxembourg"
|
zones["LU"] = "Europe/Luxembourg"
|
||||||
countries["LV"] = "Europe/Riga"
|
zones["LV"] = "Europe/Riga"
|
||||||
countries["LY"] = "Africa/Tripoli"
|
zones["LY"] = "Africa/Tripoli"
|
||||||
countries["MA"] = "Africa/Casablanca"
|
zones["MA"] = "Africa/Casablanca"
|
||||||
countries["MC"] = "Europe/Monaco"
|
zones["MC"] = "Europe/Monaco"
|
||||||
countries["MD"] = "Europe/Chisinau"
|
zones["MD"] = "Europe/Chisinau"
|
||||||
countries["MDT"] = "America/Denver"
|
zones["MDT"] = "America/Denver"
|
||||||
countries["ME"] = "Europe/Podgorica"
|
zones["ME"] = "Europe/Podgorica"
|
||||||
countries["MF"] = "America/Marigot"
|
zones["MF"] = "America/Marigot"
|
||||||
countries["MG"] = "Indian/Antananarivo"
|
zones["MG"] = "Indian/Antananarivo"
|
||||||
countries["MH"] = "Pacific/Majuro"
|
zones["MH"] = "Pacific/Majuro"
|
||||||
countries["MK"] = "Europe/Skopje"
|
zones["MK"] = "Europe/Skopje"
|
||||||
countries["ML"] = "Africa/Timbuktu"
|
zones["ML"] = "Africa/Timbuktu"
|
||||||
countries["MM"] = "Asia/Yangon"
|
zones["MM"] = "Asia/Yangon"
|
||||||
countries["MN"] = "Asia/Ulaanbaatar"
|
zones["MN"] = "Asia/Ulaanbaatar"
|
||||||
countries["MO"] = "Asia/Macau"
|
zones["MO"] = "Asia/Macau"
|
||||||
countries["MP"] = "Pacific/Saipan"
|
zones["MP"] = "Pacific/Saipan"
|
||||||
countries["MQ"] = "America/Martinique"
|
zones["MQ"] = "America/Martinique"
|
||||||
countries["MR"] = "Africa/Nouakchott"
|
zones["MR"] = "Africa/Nouakchott"
|
||||||
countries["MS"] = "America/Montserrat"
|
zones["MS"] = "America/Montserrat"
|
||||||
countries["MST"] = "America/Denver"
|
zones["MST"] = "America/Denver"
|
||||||
countries["MT"] = "Europe/Malta"
|
zones["MT"] = "Europe/Malta"
|
||||||
countries["MU"] = "Indian/Mauritius"
|
zones["MU"] = "Indian/Mauritius"
|
||||||
countries["MV"] = "Indian/Maldives"
|
zones["MV"] = "Indian/Maldives"
|
||||||
countries["MW"] = "Africa/Blantyre"
|
zones["MW"] = "Africa/Blantyre"
|
||||||
countries["MX"] = "America/Mexico_City"
|
zones["MX"] = "America/Mexico_City"
|
||||||
countries["MY"] = "Asia/Kuala_Lumpur"
|
zones["MY"] = "Asia/Kuala_Lumpur"
|
||||||
countries["MZ"] = "Africa/Maputo"
|
zones["MZ"] = "Africa/Maputo"
|
||||||
countries["NA"] = "Africa/Windhoek"
|
zones["NA"] = "Africa/Windhoek"
|
||||||
countries["NC"] = "Pacific/Noumea"
|
zones["NC"] = "Pacific/Noumea"
|
||||||
countries["NE"] = "Africa/Niamey"
|
zones["NE"] = "Africa/Niamey"
|
||||||
countries["NF"] = "Pacific/Norfolk"
|
zones["NF"] = "Pacific/Norfolk"
|
||||||
countries["NG"] = "Africa/Lagos"
|
zones["NG"] = "Africa/Lagos"
|
||||||
countries["NI"] = "America/Managua"
|
zones["NI"] = "America/Managua"
|
||||||
countries["NL"] = "Europe/Amsterdam"
|
zones["NL"] = "Europe/Amsterdam"
|
||||||
countries["NO"] = "Europe/Oslo"
|
zones["NO"] = "Europe/Oslo"
|
||||||
countries["NP"] = "Asia/Kathmandu"
|
zones["NP"] = "Asia/Kathmandu"
|
||||||
countries["NR"] = "Pacific/Nauru"
|
zones["NR"] = "Pacific/Nauru"
|
||||||
countries["NU"] = "Pacific/Niue"
|
zones["NU"] = "Pacific/Niue"
|
||||||
countries["NZ"] = "Pacific/Auckland"
|
zones["NZ"] = "Pacific/Auckland"
|
||||||
countries["OM"] = "Asia/Muscat"
|
zones["OM"] = "Asia/Muscat"
|
||||||
countries["PA"] = "America/Panama"
|
zones["PA"] = "America/Panama"
|
||||||
countries["PDT"] = "America/Los_Angeles"
|
zones["PDT"] = "America/Los_Angeles"
|
||||||
countries["PE"] = "America/Lima"
|
zones["PE"] = "America/Lima"
|
||||||
countries["PF"] = "Pacific/Tahiti"
|
zones["PF"] = "Pacific/Tahiti"
|
||||||
countries["PG"] = "Pacific/Port_Moresby"
|
zones["PG"] = "Pacific/Port_Moresby"
|
||||||
countries["PH"] = "Asia/Manila"
|
zones["PH"] = "Asia/Manila"
|
||||||
countries["PK"] = "Asia/Karachi"
|
zones["PK"] = "Asia/Karachi"
|
||||||
countries["PL"] = "Europe/Warsaw"
|
zones["PL"] = "Europe/Warsaw"
|
||||||
countries["PM"] = "America/Miquelon"
|
zones["PM"] = "America/Miquelon"
|
||||||
countries["PN"] = "Pacific/Pitcairn"
|
zones["PN"] = "Pacific/Pitcairn"
|
||||||
countries["PR"] = "America/Puerto_Rico"
|
zones["PR"] = "America/Puerto_Rico"
|
||||||
countries["PS"] = "Asia/Gaza"
|
zones["PS"] = "Asia/Gaza"
|
||||||
countries["PST"] = "America/Los_Angeles"
|
zones["PST"] = "America/Los_Angeles"
|
||||||
countries["PT"] = "Europe/Lisbon"
|
zones["PT"] = "Europe/Lisbon"
|
||||||
countries["PW"] = "Pacific/Palau"
|
zones["PW"] = "Pacific/Palau"
|
||||||
countries["PY"] = "America/Asuncion"
|
zones["PY"] = "America/Asuncion"
|
||||||
countries["QA"] = "Asia/Qatar"
|
zones["QA"] = "Asia/Qatar"
|
||||||
countries["RE"] = "Indian/Reunion"
|
zones["RE"] = "Indian/Reunion"
|
||||||
countries["RO"] = "Europe/Bucharest"
|
zones["RO"] = "Europe/Bucharest"
|
||||||
countries["RS"] = "Europe/Belgrade"
|
zones["RS"] = "Europe/Belgrade"
|
||||||
countries["RU"] = "Europe/Moscow"
|
zones["RU"] = "Europe/Moscow"
|
||||||
countries["RW"] = "Africa/Kigali"
|
zones["RW"] = "Africa/Kigali"
|
||||||
countries["SA"] = "Asia/Riyadh"
|
zones["SA"] = "Asia/Riyadh"
|
||||||
countries["SB"] = "Pacific/Guadalcanal"
|
zones["SB"] = "Pacific/Guadalcanal"
|
||||||
countries["SC"] = "Indian/Mahe"
|
zones["SC"] = "Indian/Mahe"
|
||||||
countries["SD"] = "Africa/Khartoum"
|
zones["SD"] = "Africa/Khartoum"
|
||||||
countries["SE"] = "Europe/Stockholm"
|
zones["SE"] = "Europe/Stockholm"
|
||||||
countries["SG"] = "Asia/Singapore"
|
zones["SG"] = "Asia/Singapore"
|
||||||
countries["SH"] = "Atlantic/St_Helena"
|
zones["SH"] = "Atlantic/St_Helena"
|
||||||
countries["SI"] = "Europe/Ljubljana"
|
zones["SI"] = "Europe/Ljubljana"
|
||||||
countries["SJ"] = "Atlantic/Jan_Mayen"
|
zones["SJ"] = "Atlantic/Jan_Mayen"
|
||||||
countries["SK"] = "Europe/Bratislava"
|
zones["SK"] = "Europe/Bratislava"
|
||||||
countries["SL"] = "Africa/Freetown"
|
zones["SL"] = "Africa/Freetown"
|
||||||
countries["SM"] = "Europe/San_Marino"
|
zones["SM"] = "Europe/San_Marino"
|
||||||
countries["SN"] = "Africa/Dakar"
|
zones["SN"] = "Africa/Dakar"
|
||||||
countries["SO"] = "Africa/Mogadishu"
|
zones["SO"] = "Africa/Mogadishu"
|
||||||
countries["SR"] = "America/Paramaribo"
|
zones["SR"] = "America/Paramaribo"
|
||||||
countries["SS"] = "Africa/Juba"
|
zones["SS"] = "Africa/Juba"
|
||||||
countries["ST"] = "Africa/Sao_Tome"
|
zones["ST"] = "Africa/Sao_Tome"
|
||||||
countries["SV"] = "America/El_Salvador"
|
zones["SV"] = "America/El_Salvador"
|
||||||
countries["SX"] = "America/Lower_Princes"
|
zones["SX"] = "America/Lower_Princes"
|
||||||
countries["SY"] = "Asia/Damascus"
|
zones["SY"] = "Asia/Damascus"
|
||||||
countries["SZ"] = "Africa/Mbabane"
|
zones["SZ"] = "Africa/Mbabane"
|
||||||
countries["TC"] = "America/Grand_Turk"
|
zones["TC"] = "America/Grand_Turk"
|
||||||
countries["TD"] = "Africa/Ndjamena"
|
zones["TD"] = "Africa/Ndjamena"
|
||||||
countries["TF"] = "Indian/Kerguelen"
|
zones["TF"] = "Indian/Kerguelen"
|
||||||
countries["TG"] = "Africa/Lome"
|
zones["TG"] = "Africa/Lome"
|
||||||
countries["TH"] = "Asia/Bangkok"
|
zones["TH"] = "Asia/Bangkok"
|
||||||
countries["TJ"] = "Asia/Dushanbe"
|
zones["TJ"] = "Asia/Dushanbe"
|
||||||
countries["TK"] = "Pacific/Fakaofo"
|
zones["TK"] = "Pacific/Fakaofo"
|
||||||
countries["TL"] = "Asia/Dili"
|
zones["TL"] = "Asia/Dili"
|
||||||
countries["TM"] = "Asia/Ashgabat"
|
zones["TM"] = "Asia/Ashgabat"
|
||||||
countries["TN"] = "Africa/Tunis"
|
zones["TN"] = "Africa/Tunis"
|
||||||
countries["TO"] = "Pacific/Tongatapu"
|
zones["TO"] = "Pacific/Tongatapu"
|
||||||
countries["TR"] = "Europe/Istanbul"
|
zones["TR"] = "Europe/Istanbul"
|
||||||
countries["TT"] = "America/Port_of_Spain"
|
zones["TT"] = "America/Port_of_Spain"
|
||||||
countries["TV"] = "Pacific/Funafuti"
|
zones["TV"] = "Pacific/Funafuti"
|
||||||
countries["TW"] = "Asia/Taipei"
|
zones["TW"] = "Asia/Taipei"
|
||||||
countries["TZ"] = "Africa/Dar_es_Salaam"
|
zones["TZ"] = "Africa/Dar_es_Salaam"
|
||||||
countries["UA"] = "Europe/Kiev"
|
zones["UA"] = "Europe/Kiev"
|
||||||
countries["UG"] = "Africa/Kampala"
|
zones["UG"] = "Africa/Kampala"
|
||||||
countries["UK"] = "Europe/London"
|
zones["UK"] = "Europe/London"
|
||||||
countries["UM"] = "Pacific/Wake"
|
zones["UM"] = "Pacific/Wake"
|
||||||
countries["US"] = "America/New_York"
|
zones["US"] = "America/New_York"
|
||||||
countries["UTC"] = "UTC"
|
zones["UTC"] = "UTC"
|
||||||
countries["UY"] = "America/Montevideo"
|
zones["UY"] = "America/Montevideo"
|
||||||
countries["UZ"] = "Asia/Tashkent"
|
zones["UZ"] = "Asia/Tashkent"
|
||||||
countries["VA"] = "Europe/Vatican"
|
zones["VA"] = "Europe/Vatican"
|
||||||
countries["VC"] = "America/St_Vincent"
|
zones["VC"] = "America/St_Vincent"
|
||||||
countries["VE"] = "America/Caracas"
|
zones["VE"] = "America/Caracas"
|
||||||
countries["VG"] = "America/Tortola"
|
zones["VG"] = "America/Tortola"
|
||||||
countries["VI"] = "America/St_Thomas"
|
zones["VI"] = "America/St_Thomas"
|
||||||
countries["VN"] = "Asia/Ho_Chi_Minh"
|
zones["VN"] = "Asia/Ho_Chi_Minh"
|
||||||
countries["VU"] = "Pacific/Efate"
|
zones["VU"] = "Pacific/Efate"
|
||||||
countries["WF"] = "Pacific/Wallis"
|
zones["WF"] = "Pacific/Wallis"
|
||||||
countries["WS"] = "Pacific/Apia"
|
zones["WS"] = "Pacific/Apia"
|
||||||
countries["YE"] = "Asia/Aden"
|
zones["YE"] = "Asia/Aden"
|
||||||
countries["YT"] = "Indian/Mayotte"
|
zones["YT"] = "Indian/Mayotte"
|
||||||
countries["ZA"] = "Africa/Johannesburg"
|
zones["ZA"] = "Africa/Johannesburg"
|
||||||
countries["ZM"] = "Africa/Lusaka"
|
zones["ZM"] = "Africa/Lusaka"
|
||||||
countries["ZULU"] = "Zulu"
|
zones["ZULU"] = "Zulu"
|
||||||
countries["ZW"] = "Africa/Harare"
|
zones["ZW"] = "Africa/Harare"
|
||||||
@Suppress("MagicNumber")
|
|
||||||
ZoneId.getAvailableZoneIds().stream()
|
ZoneId.getAvailableZoneIds().stream()
|
||||||
.filter { tz: String ->
|
.filter { tz: String ->
|
||||||
tz.length <= 3 && !countries.containsKey(tz)
|
tz.length <= 3 && !zones.containsKey(tz)
|
||||||
}
|
}
|
||||||
.forEach { tz: String ->
|
.forEach { tz: String ->
|
||||||
countries[tz] = tz
|
zones[tz] = tz
|
||||||
}
|
}
|
||||||
COUNTRIES_MAP = Collections.unmodifiableMap(countries)
|
COUNTRIES_MAP = Collections.unmodifiableMap(zones)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun commandResponse(
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
sender: String,
|
if (args.equals(ZONES_ARGS, true)) {
|
||||||
cmd: String,
|
event.sendMessage("The supported countries/zones are: ")
|
||||||
args: String,
|
event.sendList(COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) }, 14, isIndent = true)
|
||||||
isPrivate: Boolean
|
|
||||||
) {
|
|
||||||
with(bot) {
|
|
||||||
if (args.isEmpty()) {
|
|
||||||
send(sender, "The supported countries/zones are: ", isPrivate)
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
sendList(
|
|
||||||
sender,
|
|
||||||
COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) },
|
|
||||||
14,
|
|
||||||
isPrivate = false,
|
|
||||||
isIndent = true
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
val msg = time(args)
|
event.respond(time(args))
|
||||||
if (isPrivate) {
|
|
||||||
send(sender, msg.msg, true)
|
|
||||||
} else {
|
|
||||||
if (msg.isError) {
|
|
||||||
send(sender, msg.msg, false)
|
|
||||||
} else {
|
|
||||||
send(msg.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,9 +387,9 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) {
|
||||||
init {
|
init {
|
||||||
with(help) {
|
with(help) {
|
||||||
add("To display a country's current date/time:")
|
add("To display a country's current date/time:")
|
||||||
add(helpFormat("%c $TIME_CMD <country code>"))
|
add(helpFormat("%c $TIME_CMD [<country code or zone>]"))
|
||||||
add("For a listing of the supported countries:")
|
add("For a listing of the supported countries/zones:")
|
||||||
add(helpFormat("%c $TIME_CMD"))
|
add(helpFormat("%c $TIME_CMD $ZONES_ARGS"))
|
||||||
}
|
}
|
||||||
commands.add(TIME_CMD)
|
commands.add(TIME_CMD)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Sanitize.kt
|
* ExceptionSanitizer.kt
|
||||||
*
|
*
|
||||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
@ -36,27 +36,26 @@ import net.thauvin.erik.mobibot.Utils.obfuscate
|
||||||
import net.thauvin.erik.mobibot.Utils.replaceEach
|
import net.thauvin.erik.mobibot.Utils.replaceEach
|
||||||
import net.thauvin.erik.mobibot.modules.ModuleException
|
import net.thauvin.erik.mobibot.modules.ModuleException
|
||||||
|
|
||||||
object Sanitize {
|
object ExceptionSanitizer {
|
||||||
/**
|
/**
|
||||||
* Returns a sanitized exception to avoid displaying api keys, etc. in CI logs.
|
* Returns a sanitized exception to avoid displaying api keys, etc. in CI logs.
|
||||||
*/
|
*/
|
||||||
fun sanitizeException(e: ModuleException, vararg sanitize: String): ModuleException {
|
fun ModuleException.sanitize(vararg sanitize: String): ModuleException {
|
||||||
var sanitizedException = e
|
|
||||||
val search = sanitize.filter { it.isNotBlank() }.toTypedArray()
|
val search = sanitize.filter { it.isNotBlank() }.toTypedArray()
|
||||||
if (search.isNotEmpty()) {
|
if (search.isNotEmpty()) {
|
||||||
val obfuscate = search.map { it.obfuscate() }.toTypedArray()
|
val obfuscate = search.map { it.obfuscate() }.toTypedArray()
|
||||||
with(e) {
|
with(this) {
|
||||||
if (cause?.message != null) {
|
if (!cause?.message.isNullOrBlank()) {
|
||||||
sanitizedException = ModuleException(
|
return ModuleException(
|
||||||
debugMessage,
|
debugMessage,
|
||||||
cause!!.javaClass.name + ": " + cause!!.message!!.replaceEach(search, obfuscate),
|
cause!!.javaClass.name + ": " + cause!!.message!!.replaceEach(search, obfuscate),
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
} else if (message != null) {
|
} else if (!message.isNullOrBlank()) {
|
||||||
sanitizedException = ModuleException(debugMessage, message!!.replaceEach(search, obfuscate), this)
|
return ModuleException(debugMessage, message!!.replaceEach(search, obfuscate), this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sanitizedException
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -31,12 +31,14 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot
|
package net.thauvin.erik.mobibot
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.contains
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFailure
|
||||||
|
import assertk.assertions.isInstanceOf
|
||||||
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 org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
|
@ -48,26 +50,24 @@ class FeedReaderTest {
|
||||||
@Test
|
@Test
|
||||||
fun readFeedTest() {
|
fun readFeedTest() {
|
||||||
var messages = readFeed("https://feeds.thauvin.net/ethauvin")
|
var messages = readFeed("https://feeds.thauvin.net/ethauvin")
|
||||||
assertThat(messages.size).describedAs("messages = 10").isEqualTo(10)
|
assertThat(messages.size, "size = 10").isEqualTo(10)
|
||||||
assertThat(messages[1].msg).describedAs("feed entry url").contains("ethauvin")
|
assertThat(messages[1].msg, "feed entry url").contains("ethauvin")
|
||||||
|
|
||||||
messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0")
|
messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0")
|
||||||
assertThat(messages[0].msg).describedAs("nothing to view").contains("nothing")
|
assertThat(messages[0].msg, "nothing to view").contains("nothing")
|
||||||
|
|
||||||
messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=42", 42)
|
messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=42", 42)
|
||||||
assertThat(messages.size).describedAs("messages = 84").isEqualTo(84)
|
assertThat(messages.size, "messages = 84").isEqualTo(84)
|
||||||
assertThat(messages.last().msg).describedAs("example entry url").contains("http://example.com/test/")
|
assertThat(messages.last().msg, "example entry url").contains("http://example.com/test/")
|
||||||
|
|
||||||
assertThatThrownBy { readFeed("blah") }.describedAs("invalid URL")
|
assertThat { readFeed("blah") }.isFailure().isInstanceOf(MalformedURLException::class.java)
|
||||||
.isInstanceOf(MalformedURLException::class.java)
|
|
||||||
|
|
||||||
assertThatThrownBy { readFeed("https://www.example.com") }.describedAs("not a feed")
|
assertThat { readFeed("https://www.example.com") }.isFailure().isInstanceOf(FeedException::class.java)
|
||||||
.isInstanceOf(FeedException::class.java)
|
|
||||||
|
|
||||||
assertThatThrownBy { readFeed("https://www.examples.com/foo") }.describedAs("404 not found")
|
assertThat { readFeed("https://www.examples.com/foo") }.isFailure()
|
||||||
.isInstanceOf(FileNotFoundException::class.java)
|
.isInstanceOf(FileNotFoundException::class.java)
|
||||||
|
|
||||||
assertThatThrownBy { readFeed("https://www.doesnotexists.com") }.describedAs("unknown host")
|
assertThat { readFeed("https://www.doesnotexists.com") }.isFailure()
|
||||||
.isInstanceOf(UnknownHostException::class.java)
|
.isInstanceOf(UnknownHostException::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* PinboardUtilsTest.kt
|
* PinboardTest.kt
|
||||||
*
|
*
|
||||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
@ -32,45 +32,39 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot
|
package net.thauvin.erik.mobibot
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.PinboardUtils.toTimestamp
|
|
||||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||||
import net.thauvin.erik.pinboard.PinboardPoster
|
|
||||||
import org.testng.Assert.assertFalse
|
import org.testng.Assert.assertFalse
|
||||||
import org.testng.Assert.assertTrue
|
import org.testng.Assert.assertTrue
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
class PinboardUtilsTest : LocalProperties() {
|
class PinboardTest : LocalProperties() {
|
||||||
|
private val pinboard = Pinboard()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun pinboardTest() {
|
fun testPinboard() {
|
||||||
val apiToken = getProperty("pinboard-api-token")
|
val apiToken = getProperty("pinboard-api-token")
|
||||||
val pinboard = PinboardPoster(apiToken)
|
|
||||||
val url = "https://www.example.com/"
|
val url = "https://www.example.com/"
|
||||||
val ircServer = "irc.test.com"
|
val ircServer = "irc.test.com"
|
||||||
val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test"))
|
val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test"))
|
||||||
|
|
||||||
PinboardUtils.addPin(pinboard, ircServer, entry)
|
pinboard.setApiToken(apiToken)
|
||||||
|
|
||||||
|
pinboard.addPin(ircServer, entry)
|
||||||
assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "validate add")
|
assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "validate add")
|
||||||
|
|
||||||
entry.link = "https://www.foo.com/"
|
entry.link = "https://www.foo.com/"
|
||||||
PinboardUtils.updatePin(pinboard, ircServer, url, entry)
|
pinboard.updatePin(ircServer, url, entry)
|
||||||
assertTrue(validatePin(apiToken, url = entry.link, ircServer), "validate update")
|
assertTrue(validatePin(apiToken, url = entry.link, ircServer), "validate update")
|
||||||
|
|
||||||
entry.title = "Foo Title"
|
entry.title = "Foo Title"
|
||||||
PinboardUtils.updatePin(pinboard, ircServer, entry.link, entry)
|
pinboard.updatePin(ircServer, entry.link, entry)
|
||||||
assertTrue(validatePin(apiToken, url = entry.link, entry.title), "validate title")
|
assertTrue(validatePin(apiToken, url = entry.link, entry.title), "validate title")
|
||||||
|
|
||||||
PinboardUtils.deletePin(pinboard, entry)
|
pinboard.deletePin(entry)
|
||||||
assertFalse(validatePin(apiToken, url = entry.link), "validate delete")
|
assertFalse(validatePin(apiToken, url = entry.link), "validate delete")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun toTimestampTest() {
|
|
||||||
val d = Date()
|
|
||||||
assertTrue(d.toTimestamp().matches("[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z".toRegex()))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean {
|
private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean {
|
||||||
val response = Utils.urlReader(
|
val response = Utils.urlReader(
|
||||||
URL(
|
URL(
|
|
@ -31,6 +31,8 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot
|
package net.thauvin.erik.mobibot
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
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.buildCmdSyntax
|
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||||
|
@ -55,8 +57,7 @@ import net.thauvin.erik.mobibot.Utils.unescapeXml
|
||||||
import net.thauvin.erik.mobibot.Utils.uptime
|
import net.thauvin.erik.mobibot.Utils.uptime
|
||||||
import net.thauvin.erik.mobibot.Utils.urlReader
|
import net.thauvin.erik.mobibot.Utils.urlReader
|
||||||
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.pircbotx.Colors
|
||||||
import org.jibble.pircbot.Colors
|
|
||||||
import org.testng.annotations.BeforeClass
|
import org.testng.annotations.BeforeClass
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -86,59 +87,59 @@ class UtilsTest {
|
||||||
val dir = "dir"
|
val dir = "dir"
|
||||||
val sep = '/'
|
val sep = '/'
|
||||||
val url = "https://erik.thauvin.net"
|
val url = "https://erik.thauvin.net"
|
||||||
assertThat(dir.appendIfMissing(File.separatorChar)).describedAs("appendIfMissing(dir)")
|
assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)")
|
||||||
.isEqualTo(dir + File.separatorChar)
|
.isEqualTo(dir + File.separatorChar)
|
||||||
assertThat(url.appendIfMissing(sep)).describedAs("appendIfMissing(url)").isEqualTo("$url$sep")
|
assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep")
|
||||||
assertThat("$url$sep".appendIfMissing(sep)).describedAs("appendIfMissing($url$sep)").isEqualTo("$url$sep")
|
assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBold() {
|
fun testBold() {
|
||||||
assertThat(bold(1)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD)
|
assertThat(bold(1), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD)
|
||||||
assertThat(bold(2L)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD)
|
assertThat(bold(2L), "bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD)
|
||||||
assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
assertThat(bold(ascii), "bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
||||||
assertThat(bold("test")).describedAs("bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD)
|
assertThat(bold("test"), "bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBuildCmdSyntax() {
|
fun testBuildCmdSyntax() {
|
||||||
val bot = "mobibot"
|
val bot = "mobibot"
|
||||||
assertThat(buildCmdSyntax("%c $test %n $test", bot, false)).describedAs("public")
|
assertThat(buildCmdSyntax("%c $test %n $test", bot, false), "public")
|
||||||
.isEqualTo("$bot: $test $bot $test")
|
.isEqualTo("$bot: $test $bot $test")
|
||||||
assertThat(buildCmdSyntax("%c %n $test %c $test %n", bot, true)).describedAs("public")
|
assertThat(buildCmdSyntax("%c %n $test %c $test %n", bot, true), "public")
|
||||||
.isEqualTo("/msg $bot $bot $test /msg $bot $test $bot")
|
.isEqualTo("/msg $bot $bot $test /msg $bot $test $bot")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCapitalise() {
|
fun testCapitalise() {
|
||||||
assertThat("test".capitalise()).describedAs("capitalize(test)").isEqualTo("Test")
|
assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test")
|
||||||
assertThat("Test".capitalise()).describedAs("capitalize(Test)").isEqualTo("Test")
|
assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test")
|
||||||
assertThat(test.capitalise()).describedAs("capitalize($test)").isEqualTo(test)
|
assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test)
|
||||||
assertThat("".capitalise()).describedAs("capitalize()").isEqualTo("")
|
assertThat("".capitalise(), "capitalize()").isEqualTo("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun textCapitaliseWords() {
|
fun textCapitaliseWords() {
|
||||||
assertThat(test.capitalizeWords()).describedAs("captiatlizeWords(test)").isEqualTo("This Is A Test.")
|
assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.")
|
||||||
assertThat("Already Capitalized".capitalizeWords()).describedAs("already capitalized")
|
assertThat("Already Capitalized".capitalizeWords(), "already capitalized")
|
||||||
.isEqualTo("Already Capitalized")
|
.isEqualTo("Already Capitalized")
|
||||||
assertThat(" a test ".capitalizeWords()).describedAs("with spaces").isEqualTo(" A Test ")
|
assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testColorize() {
|
fun testColorize() {
|
||||||
assertThat(colorize(ascii, Colors.REVERSE)).describedAs("colorize(reverse)").isEqualTo(
|
assertThat(colorize(ascii, Colors.REVERSE), "colorize(reverse)").isEqualTo(
|
||||||
Colors.REVERSE + ascii + Colors.REVERSE
|
Colors.REVERSE + ascii + Colors.REVERSE
|
||||||
)
|
)
|
||||||
assertThat(colorize(ascii, Colors.RED)).describedAs("colorize(red)")
|
assertThat(colorize(ascii, Colors.RED), "colorize(red)")
|
||||||
.isEqualTo(Colors.RED + ascii + Colors.NORMAL)
|
.isEqualTo(Colors.RED + ascii + Colors.NORMAL)
|
||||||
assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)")
|
assertThat(colorize(ascii, Colors.BOLD), "colorized(bold)")
|
||||||
.isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
.isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
||||||
assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo("")
|
assertThat(colorize(null, Colors.RED), "colorize(null)").isEqualTo("")
|
||||||
assertThat(colorize("", Colors.RED)).describedAs("colorize()").isEqualTo("")
|
assertThat(colorize("", Colors.RED), "colorize()").isEqualTo("")
|
||||||
assertThat(colorize(ascii, DEFAULT_COLOR)).describedAs("colorize(none)").isEqualTo(ascii)
|
assertThat(colorize(ascii, DEFAULT_COLOR), "colorize(none)").isEqualTo(ascii)
|
||||||
assertThat(colorize(" ", Colors.NORMAL)).describedAs("colorize(blank)")
|
assertThat(colorize(" ", Colors.NORMAL), "colorize(blank)")
|
||||||
.isEqualTo(Colors.NORMAL + " " + Colors.NORMAL)
|
.isEqualTo(Colors.NORMAL + " " + Colors.NORMAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,9 +158,9 @@ class UtilsTest {
|
||||||
val p = Properties()
|
val p = Properties()
|
||||||
p["one"] = "1"
|
p["one"] = "1"
|
||||||
p["two"] = "two"
|
p["two"] = "two"
|
||||||
assertThat(p.getIntProperty("one", 9)).describedAs("getIntProperty(one)").isEqualTo(1)
|
assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1)
|
||||||
assertThat(p.getIntProperty("two", 2)).describedAs("getIntProperty(two)").isEqualTo(2)
|
assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2)
|
||||||
assertThat(p.getIntProperty("foo", 3)).describedAs("getIntProperty(foo)").isEqualTo(3)
|
assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -169,26 +170,26 @@ class UtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testHelpFormat() {
|
fun testHelpFormat() {
|
||||||
assertThat(helpFormat(test, isBold = true, isIndent = false)).describedAs("bold")
|
assertThat(helpFormat(test, isBold = true, isIndent = false), "bold")
|
||||||
.isEqualTo("${Colors.BOLD}$test${Colors.BOLD}")
|
.isEqualTo("${Colors.BOLD}$test${Colors.BOLD}")
|
||||||
assertThat(helpFormat(test, isBold = false, isIndent = true)).describedAs("indent")
|
assertThat(helpFormat(test, isBold = false, isIndent = true), "indent")
|
||||||
.isEqualTo(test.prependIndent())
|
.isEqualTo(test.prependIndent())
|
||||||
assertThat(helpFormat(test, isBold = true, isIndent = true)).describedAs("bold-indent")
|
assertThat(helpFormat(test, isBold = true, isIndent = true), "bold-indent")
|
||||||
.isEqualTo(colorize(test, Colors.BOLD).prependIndent())
|
.isEqualTo(colorize(test, Colors.BOLD).prependIndent())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testIsoLocalDate() {
|
fun testIsoLocalDate() {
|
||||||
assertThat(cal.time.toIsoLocalDate()).describedAs("isoLocalDate(date)").isEqualTo("1952-02-17")
|
assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17")
|
||||||
assertThat(localDateTime.toIsoLocalDate()).describedAs("isoLocalDate(localDate)").isEqualTo("1952-02-17")
|
assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testObfuscate() {
|
fun testObfuscate() {
|
||||||
assertThat(ascii.obfuscate().length).describedAs("obfuscate is right length").isEqualTo(ascii.length)
|
assertThat(ascii.obfuscate().length, "obfuscate is right length").isEqualTo(ascii.length)
|
||||||
assertThat(ascii.obfuscate()).describedAs("obfuscate()").isEqualTo("x".repeat(ascii.length))
|
assertThat(ascii.obfuscate(), "obfuscate()").isEqualTo("x".repeat(ascii.length))
|
||||||
assertThat(" ".obfuscate()).describedAs("obfuscate(blank)").isEqualTo(" ")
|
assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -197,7 +198,7 @@ class UtilsTest {
|
||||||
val weeks = "weeks"
|
val weeks = "weeks"
|
||||||
|
|
||||||
for (i in -1..3) {
|
for (i in -1..3) {
|
||||||
assertThat(week.plural(i.toLong())).describedAs("plural($i)").isEqualTo(if (i > 1) weeks else week)
|
assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,15 +206,15 @@ class UtilsTest {
|
||||||
fun testReplaceEach() {
|
fun testReplaceEach() {
|
||||||
val search = arrayOf("one", "two", "three")
|
val search = arrayOf("one", "two", "three")
|
||||||
val replace = arrayOf("1", "2", "3")
|
val replace = arrayOf("1", "2", "3")
|
||||||
assertThat(search.joinToString(",").replaceEach(search, replace)).describedAs("replaceEach(1,2,3")
|
assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3")
|
||||||
.isEqualTo(replace.joinToString(","))
|
.isEqualTo(replace.joinToString(","))
|
||||||
|
|
||||||
assertThat(test.replaceEach(search, replace)).describedAs("replaceEach(nothing)").isEqualTo(test)
|
assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test)
|
||||||
|
|
||||||
assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E"))).describedAs("replaceEach($test)")
|
assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)")
|
||||||
.isEqualTo(test.replace("t", "").replace("e", "E"))
|
.isEqualTo(test.replace("t", "").replace("e", "E"))
|
||||||
|
|
||||||
assertThat(test.replaceEach(search, emptyArray())).describedAs("replaceEach(search, empty)")
|
assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)")
|
||||||
.isEqualTo(test)
|
.isEqualTo(test)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,8 +235,8 @@ class UtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testToIntOrDefault() {
|
fun testToIntOrDefault() {
|
||||||
assertThat("10".toIntOrDefault(1)).describedAs("toIntOrDefault(10, 1)").isEqualTo(10)
|
assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10)
|
||||||
assertThat("a".toIntOrDefault(2)).describedAs("toIntOrDefault(a, 2)").isEqualTo(2)
|
assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -247,26 +248,26 @@ class UtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testUptime() {
|
fun testUptime() {
|
||||||
assertThat(uptime(547800300076L)).describedAs("full")
|
assertThat(uptime(547800300076L), "full")
|
||||||
.isEqualTo("17 years 2 months 2 weeks 1 day 6 hours 45 minutes")
|
.isEqualTo("17 years 2 months 2 weeks 1 day 6 hours 45 minutes")
|
||||||
assertThat(uptime(2700000L)).describedAs("minutes").isEqualTo("45 minutes")
|
assertThat(uptime(2700000L), "minutes").isEqualTo("45 minutes")
|
||||||
assertThat(uptime(24300000L)).describedAs("hours minutes").isEqualTo("6 hours 45 minutes")
|
assertThat(uptime(24300000L), "hours minutes").isEqualTo("6 hours 45 minutes")
|
||||||
assertThat(uptime(110700000L)).describedAs("days hours minutes").isEqualTo("1 day 6 hours 45 minutes")
|
assertThat(uptime(110700000L), "days hours minutes").isEqualTo("1 day 6 hours 45 minutes")
|
||||||
assertThat(uptime(1320300000L)).describedAs("weeks days hours minutes")
|
assertThat(uptime(1320300000L), "weeks days hours minutes")
|
||||||
.isEqualTo("2 weeks 1 day 6 hours 45 minutes")
|
.isEqualTo("2 weeks 1 day 6 hours 45 minutes")
|
||||||
assertThat(uptime(0L)).describedAs("0 minutes").isEqualTo("0 minute")
|
assertThat(uptime(0L), "0 minutes").isEqualTo("0 minute")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun testUrlReader() {
|
fun testUrlReader() {
|
||||||
assertThat(urlReader(URL("https://postman-echo.com/status/200"))).describedAs("urlReader()")
|
assertThat(urlReader(URL("https://postman-echo.com/status/200")), "urlReader()")
|
||||||
.isEqualTo("{\"status\":200}")
|
.isEqualTo("{\"status\":200}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testUtcDateTime() {
|
fun testUtcDateTime() {
|
||||||
assertThat(cal.time.toUtcDateTime()).describedAs("utcDateTime(date)").isEqualTo("1952-02-17 12:30")
|
assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30")
|
||||||
assertThat(localDateTime.toUtcDateTime()).describedAs("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30")
|
assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,12 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.commands.tell
|
package net.thauvin.erik.mobibot.commands.tell
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFalse
|
||||||
|
import assertk.assertions.isTrue
|
||||||
|
import assertk.assertions.prop
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
@ -51,18 +56,21 @@ class TellMessageTest {
|
||||||
val recipient = "recipient"
|
val recipient = "recipient"
|
||||||
val sender = "sender"
|
val sender = "sender"
|
||||||
val tellMessage = TellMessage(sender, recipient, message)
|
val tellMessage = TellMessage(sender, recipient, message)
|
||||||
assertThat(tellMessage).extracting("sender", "recipient", "message")
|
assertThat(tellMessage).all {
|
||||||
.containsExactly(sender, recipient, message)
|
prop(TellMessage::sender).isEqualTo(sender)
|
||||||
assertThat(isValidDate(tellMessage.queued)).describedAs("queued is valid date/time").isTrue
|
prop(TellMessage::recipient).isEqualTo(recipient)
|
||||||
assertThat(tellMessage.isMatch(sender)).describedAs("match sender").isTrue
|
prop(TellMessage::message).isEqualTo(message)
|
||||||
assertThat(tellMessage.isMatch(recipient)).describedAs("match recipient").isTrue
|
}
|
||||||
assertThat(tellMessage.isMatch("foo")).describedAs("foo is no match").isFalse
|
assertThat(isValidDate(tellMessage.queued), "queued is valid date/time").isTrue()
|
||||||
|
assertThat(tellMessage.isMatch(sender), "match sender").isTrue()
|
||||||
|
assertThat(tellMessage.isMatch(recipient), "match recipient").isTrue()
|
||||||
|
assertThat(tellMessage.isMatch("foo"), "foo is no match").isFalse()
|
||||||
tellMessage.isReceived = false
|
tellMessage.isReceived = false
|
||||||
assertThat(tellMessage.receptionDate).describedAs("reception date not set").isEqualTo(LocalDateTime.MIN)
|
assertThat(tellMessage.receptionDate, "reception date not set").isEqualTo(LocalDateTime.MIN)
|
||||||
tellMessage.isReceived = true
|
tellMessage.isReceived = true
|
||||||
assertThat(tellMessage.isReceived).describedAs("is received").isTrue
|
assertThat(tellMessage.isReceived, "is received").isTrue()
|
||||||
assertThat(isValidDate(tellMessage.receptionDate)).describedAs("received is valid date/time").isTrue
|
assertThat(isValidDate(tellMessage.receptionDate), "received is valid date/time").isTrue()
|
||||||
tellMessage.isNotified = true
|
tellMessage.isNotified = true
|
||||||
assertThat(tellMessage.isNotified).describedAs("is notified").isTrue
|
assertThat(tellMessage.isNotified, "is notified").isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,16 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.entries
|
package net.thauvin.erik.mobibot.entries
|
||||||
|
|
||||||
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEmpty
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFalse
|
||||||
|
import assertk.assertions.isTrue
|
||||||
|
import assertk.assertions.prop
|
||||||
|
import assertk.assertions.size
|
||||||
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.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
@ -58,21 +65,30 @@ class EntryLinkTest {
|
||||||
entryLink.addComment("c$i", "u$i")
|
entryLink.addComment("c$i", "u$i")
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
assertThat(entryLink.comments.size).describedAs("getComments().size() == 5").isEqualTo(i)
|
assertThat(entryLink.comments.size, "getComments().size() == 5").isEqualTo(i)
|
||||||
i = 0
|
i = 0
|
||||||
for (comment in entryLink.comments) {
|
for (comment in entryLink.comments) {
|
||||||
assertThat(comment).extracting("comment", "nick").containsExactly("c$i", "u$i")
|
assertThat(comment).all {
|
||||||
|
prop(EntryComment::comment).isEqualTo("c$i")
|
||||||
|
prop(EntryComment::nick).isEqualTo("u$i")
|
||||||
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
val r = SecureRandom()
|
val r = SecureRandom()
|
||||||
while (entryLink.comments.size > 0) {
|
while (entryLink.comments.size > 0) {
|
||||||
entryLink.deleteComment(r.nextInt(entryLink.comments.size))
|
entryLink.deleteComment(r.nextInt(entryLink.comments.size))
|
||||||
}
|
}
|
||||||
assertThat(entryLink.comments).describedAs("hasComments()").isEmpty()
|
assertThat(entryLink.comments, "hasComments()").isEmpty()
|
||||||
entryLink.addComment("nothing", "nobody")
|
entryLink.addComment("nothing", "nobody")
|
||||||
entryLink.setComment(0, "something", "somebody")
|
entryLink.setComment(0, "something", "somebody")
|
||||||
assertThat(entryLink.getComment(0)).describedAs("get first comment").extracting("nick", "comment")
|
val comment = entryLink.getComment(0)
|
||||||
.containsExactly("somebody", "something")
|
assertThat(comment, "get first comment").all {
|
||||||
|
prop(EntryComment::nick).isEqualTo("somebody")
|
||||||
|
prop(EntryComment::comment).isEqualTo("something")
|
||||||
|
}
|
||||||
|
assertThat(entryLink.deleteComment(comment), "delete comment").isTrue()
|
||||||
|
assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -80,19 +96,19 @@ class EntryLinkTest {
|
||||||
val tag = "test"
|
val tag = "test"
|
||||||
val tags = listOf(SyndCategoryImpl().apply { name = tag })
|
val tags = listOf(SyndCategoryImpl().apply { name = tag })
|
||||||
val link = EntryLink("link", "title", "nick", "channel", Date(), tags)
|
val link = EntryLink("link", "title", "nick", "channel", Date(), tags)
|
||||||
assertThat(link.tags.size).describedAs("check tag size").isEqualTo(tags.size)
|
assertThat(link.tags.size, "check tag size").isEqualTo(tags.size)
|
||||||
assertThat(link.tags[0].name).describedAs("check tag name").isEqualTo(tag)
|
assertThat(link.tags[0].name, "check tag name").isEqualTo(tag)
|
||||||
assertThat(link.pinboardTags).describedAs("check pinboard tags").isEqualTo("nick,$tag")
|
assertThat(link.pinboardTags, "check pinboard tags").isEqualTo("nick,$tag")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMatches() {
|
fun testMatches() {
|
||||||
assertThat(entryLink.matches("mobitopia")).describedAs("match mobitopia").isTrue
|
assertThat(entryLink.matches("mobitopia"), "match mobitopia").isTrue()
|
||||||
assertThat(entryLink.matches("skynx")).describedAs("match nick").isTrue
|
assertThat(entryLink.matches("skynx"), "match nick").isTrue()
|
||||||
assertThat(entryLink.matches("www.mobitopia.org")).describedAs("match url").isTrue
|
assertThat(entryLink.matches("www.mobitopia.org"), "match url").isTrue()
|
||||||
assertThat(entryLink.matches("foo")).describedAs("match foo").isFalse
|
assertThat(entryLink.matches("foo"), "match foo").isFalse()
|
||||||
assertThat(entryLink.matches("")).describedAs("match empty").isFalse
|
assertThat(entryLink.matches("<empty>"), "match empty").isFalse()
|
||||||
assertThat(entryLink.matches(null)).describedAs("match null").isFalse
|
assertThat(entryLink.matches(null), "match null").isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,20 +116,19 @@ class EntryLinkTest {
|
||||||
fun testTags() {
|
fun testTags() {
|
||||||
val tags: List<SyndCategory> = entryLink.tags
|
val tags: List<SyndCategory> = entryLink.tags
|
||||||
for ((i, tag) in tags.withIndex()) {
|
for ((i, tag) in tags.withIndex()) {
|
||||||
assertThat(tag.name).describedAs("tag.getName($i)").isEqualTo("tag" + (i + 1))
|
assertThat(tag.name, "tag.getName($i)").isEqualTo("tag" + (i + 1))
|
||||||
}
|
}
|
||||||
assertThat(entryLink.tags.size).describedAs("getTags().size() is 5").isEqualTo(5)
|
assertThat(entryLink.tags, "size is 5").size().isEqualTo(5)
|
||||||
assertThat(entryLink.tags).describedAs("hasTags() is true").isNotEmpty
|
|
||||||
entryLink.setTags("-tag5")
|
entryLink.setTags("-tag5")
|
||||||
entryLink.setTags("+mobitopia")
|
entryLink.setTags("+mobitopia")
|
||||||
entryLink.setTags("tag4")
|
entryLink.setTags("tag4")
|
||||||
entryLink.setTags("-mobitopia")
|
entryLink.setTags("-mobitopia")
|
||||||
assertThat(entryLink.pinboardTags).describedAs("getPinboardTags()")
|
assertThat(entryLink.pinboardTags, "getPinboardTags()")
|
||||||
.isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia")
|
.isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia")
|
||||||
val size = entryLink.tags.size
|
val size = entryLink.tags.size
|
||||||
entryLink.setTags("")
|
entryLink.setTags("")
|
||||||
assertThat(entryLink.tags.size).describedAs("empty tag").isEqualTo(size)
|
assertThat(entryLink.tags.size, "empty tag").isEqualTo(size)
|
||||||
entryLink.setTags(" ")
|
entryLink.setTags(" ")
|
||||||
assertThat(entryLink.tags.size).describedAs("blank tag").isEqualTo(size)
|
assertThat(entryLink.tags.size, "blank tag").isEqualTo(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
121
src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt
Normal file
121
src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* FeedMgrTest.kt
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* Neither the name of this project nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.thauvin.erik.mobibot.entries
|
||||||
|
|
||||||
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.endsWith
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isTrue
|
||||||
|
import assertk.assertions.prop
|
||||||
|
import net.thauvin.erik.mobibot.Utils.today
|
||||||
|
import org.testng.annotations.BeforeSuite
|
||||||
|
import org.testng.annotations.Test
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.Date
|
||||||
|
import kotlin.io.path.absolutePathString
|
||||||
|
import kotlin.io.path.deleteIfExists
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
import kotlin.io.path.fileSize
|
||||||
|
import kotlin.io.path.name
|
||||||
|
|
||||||
|
class FeedMgrTest {
|
||||||
|
private val entries = Entries()
|
||||||
|
private val channel = "mobibot"
|
||||||
|
|
||||||
|
@BeforeSuite(alwaysRun = true)
|
||||||
|
fun beforeSuite() {
|
||||||
|
entries.logsDir = "src/test/resources/"
|
||||||
|
entries.ircServer = "irc.example.com"
|
||||||
|
entries.channel = channel
|
||||||
|
entries.backlogs = "https://www.mobitopia.org/mobibot/logs"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFeedMgr() {
|
||||||
|
// Load the feed
|
||||||
|
assertThat(FeedsMgr.loadFeed(entries), "pubDate").isEqualTo("2021-10-31")
|
||||||
|
|
||||||
|
assertThat(entries.links.size, "2 links").isEqualTo(2)
|
||||||
|
entries.links.forEachIndexed { i, entryLink ->
|
||||||
|
assertThat(entryLink, "Example $(i + 1)").all {
|
||||||
|
prop(EntryLink::title).isEqualTo("Example ${i + 1}")
|
||||||
|
prop(EntryLink::link).isEqualTo("https://www.example.com/${i + 1}")
|
||||||
|
prop(EntryLink::channel).isEqualTo(channel)
|
||||||
|
}
|
||||||
|
entryLink.tags.forEachIndexed { y, tag ->
|
||||||
|
assertThat(tag.name, "tag${i + 1}-${y + 1}").isEqualTo("tag${i + 1}-${y + 1}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with(entries.links.first()) {
|
||||||
|
assertThat(nick, "first nick").isEqualTo("ErikT")
|
||||||
|
assertThat(date, "first date").isEqualTo(Date(1635638400000L))
|
||||||
|
comments.forEachIndexed { i, entryComment ->
|
||||||
|
assertThat(entryComment.comment, "comment ${i + 1}").endsWith("comment ${i + 1}.")
|
||||||
|
if (i == 0) {
|
||||||
|
assertThat(entryComment.nick, "comment ${i + 1} nick").isEqualTo("ErikT")
|
||||||
|
} else {
|
||||||
|
assertThat(entryComment.nick, "comment ${i + 1} nick").isEqualTo("Skynx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(entries.links[1], "second link").all {
|
||||||
|
prop(EntryLink::nick).isEqualTo("Skynx")
|
||||||
|
prop(EntryLink::date).isEqualTo(Date(1635638460000L))
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentFile = Paths.get("${entries.logsDir}test.xml")
|
||||||
|
val backlogFile = Paths.get("${entries.logsDir}${today()}.xml")
|
||||||
|
|
||||||
|
// Save the feed
|
||||||
|
FeedsMgr.saveFeed(entries, currentFile.name)
|
||||||
|
|
||||||
|
assertThat(currentFile.exists(), "${currentFile.absolutePathString()} exists").isTrue()
|
||||||
|
assertThat(backlogFile.exists(), "${backlogFile.absolutePathString()} exits").isTrue()
|
||||||
|
|
||||||
|
assertThat(currentFile.fileSize(), "files are identical").isEqualTo(backlogFile.fileSize())
|
||||||
|
|
||||||
|
// Load the test feed
|
||||||
|
entries.links.clear()
|
||||||
|
FeedsMgr.loadFeed(entries, currentFile.name)
|
||||||
|
|
||||||
|
entries.links.forEachIndexed { i, entryLink ->
|
||||||
|
assertThat(entryLink.title, "${currentFile.name} title ${i + 1}").isEqualTo("Example ${i + 1}")
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(currentFile.deleteIfExists(), "delete ${currentFile.absolutePathString()}").isTrue()
|
||||||
|
assertThat(backlogFile.deleteIfExists(), "delete ${backlogFile.absolutePathString()}").isTrue()
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,11 +31,13 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFailure
|
||||||
|
import assertk.assertions.isInstanceOf
|
||||||
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
|
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate
|
import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,11 +46,11 @@ import org.testng.annotations.Test
|
||||||
class CalcTest {
|
class CalcTest {
|
||||||
@Test
|
@Test
|
||||||
fun testCalculate() {
|
fun testCalculate() {
|
||||||
assertThat(calculate("1 + 1")).describedAs("calculate(1+1)").isEqualTo("1+1 = %s", bold(2))
|
assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${bold(2)}")
|
||||||
assertThat(calculate("1 -3")).describedAs("calculate(1 -3)").isEqualTo("1-3 = %s", bold(-2))
|
assertThat(calculate("1 -3"), "calculate(1 -3)").isEqualTo("1-3 = ${bold(-2)}")
|
||||||
assertThat(calculate("pi+π+e+φ")).describedAs("calculate(pi+π+e+φ)")
|
assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)")
|
||||||
.isEqualTo("pi+π+e+φ = %s", bold("10.62"))
|
.isEqualTo("pi+π+e+φ = ${bold("10.62")}")
|
||||||
assertThatThrownBy { calculate("one + one") }.describedAs("calculate(one+one)")
|
assertThat { calculate("one + one") }
|
||||||
.isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
.isFailure().isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,13 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isGreaterThan
|
||||||
|
import assertk.assertions.prop
|
||||||
|
import net.thauvin.erik.crypto.CryptoPrice
|
||||||
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice
|
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,11 +48,17 @@ class CryptoPricesTest {
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun testMarketPrice() {
|
fun testMarketPrice() {
|
||||||
var price = currentPrice(listOf("BTC"))
|
var price = currentPrice(listOf("BTC"))
|
||||||
assertThat(price).extracting("base", "currency").containsExactly("BTC", "USD")
|
assertThat(price, "BTC in USD").all {
|
||||||
assertThat(price.amount.signum()).describedAs("BTC > 0").isGreaterThan(0)
|
prop(CryptoPrice::base).isEqualTo("BTC")
|
||||||
|
prop(CryptoPrice::currency).isEqualTo("USD")
|
||||||
|
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
||||||
|
}
|
||||||
|
|
||||||
price = currentPrice(listOf("ETH", "EUR"))
|
price = currentPrice(listOf("ETH", "EUR"))
|
||||||
assertThat(price).extracting("base", "currency").containsExactly("ETH", "EUR")
|
assertThat(price, "ETH in EUR").all {
|
||||||
assertThat(price.amount.signum()).describedAs("ETH > 0").isGreaterThan(0)
|
prop(CryptoPrice::base).isEqualTo("ETH")
|
||||||
|
prop(CryptoPrice::currency).isEqualTo("EUR")
|
||||||
|
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,21 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.any
|
||||||
|
import assertk.assertions.contains
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isInstanceOf
|
||||||
|
import assertk.assertions.matches
|
||||||
|
import assertk.assertions.prop
|
||||||
|
import assertk.assertions.size
|
||||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency
|
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency
|
||||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.currencyRates
|
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.currencyRates
|
||||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadRates
|
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadRates
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||||
|
import net.thauvin.erik.mobibot.msg.Message
|
||||||
|
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||||
import org.testng.annotations.BeforeClass
|
import org.testng.annotations.BeforeClass
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
|
@ -50,14 +61,28 @@ class CurrencyConverterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testConvertCurrency() {
|
fun testConvertCurrency() {
|
||||||
assertThat(convertCurrency("100 USD to EUR").msg)
|
assertThat(
|
||||||
.describedAs("100 USD to EUR").matches("\\$100\\.00 = €\\d{2,3}\\.\\d{2}")
|
convertCurrency("100 USD to EUR").msg,
|
||||||
assertThat(convertCurrency("100 USD to USD").msg).describedAs("100 USD to USD")
|
"100 USD to EUR"
|
||||||
.contains("You're kidding, right?")
|
).matches("\\$100\\.00 = €\\d{2,3}\\.\\d{2}".toRegex())
|
||||||
assertThat(convertCurrency("100 USD").msg).describedAs("100 USD").contains("Invalid query.")
|
assertThat(convertCurrency("100 USD to USD"), "100 USD to USD").all {
|
||||||
|
prop(Message::msg).contains("You're kidding, right?")
|
||||||
|
isInstanceOf(PublicMessage::class.java)
|
||||||
|
}
|
||||||
|
assertThat(convertCurrency("100 USD"), "100 USD").all {
|
||||||
|
prop(Message::msg).contains("Invalid query.")
|
||||||
|
isInstanceOf(ErrorMessage::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCurrencyRates() {
|
||||||
val rates = currencyRates()
|
val rates = currencyRates()
|
||||||
assertThat(rates.size).describedAs("currencyRates.size == 33").isEqualTo(33)
|
assertThat(rates).all {
|
||||||
assertThat(rates).describedAs("currencyRates(EUR< USD)").contains("EUR: 1")
|
size().isEqualTo(33)
|
||||||
.anyMatch { it.matches("USD: .*".toRegex()) }
|
any { it.matches("[A-Z]{3}: +[\\d.]+".toRegex()) }
|
||||||
|
contains("EUR: 1")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,14 +33,15 @@
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
class DiceTest {
|
class DiceTest {
|
||||||
@Test
|
@Test
|
||||||
fun testWinLoseOrTie() {
|
fun testWinLoseOrTie() {
|
||||||
assertThat(Dice.winLoseOrTie(6, 6)).describedAs("6 vs. 6").isEqualTo(Dice.Result.TIE)
|
assertThat(Dice.winLoseOrTie(6, 6), "6 vs. 6").isEqualTo(Dice.Result.TIE)
|
||||||
assertThat(Dice.winLoseOrTie(6, 5)).describedAs("6 vs. 5").isEqualTo(Dice.Result.WIN)
|
assertThat(Dice.winLoseOrTie(6, 5), "6 vs. 5").isEqualTo(Dice.Result.WIN)
|
||||||
assertThat(Dice.winLoseOrTie(5, 6)).describedAs("5 vs. 6").isEqualTo(Dice.Result.LOSE)
|
assertThat(Dice.winLoseOrTie(5, 6), "5 vs. 6").isEqualTo(Dice.Result.LOSE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,11 +31,20 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.contains
|
||||||
|
import assertk.assertions.hasNoCause
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFailure
|
||||||
|
import assertk.assertions.isInstanceOf
|
||||||
|
import assertk.assertions.isNotEmpty
|
||||||
|
import assertk.assertions.prop
|
||||||
|
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
|
||||||
import net.thauvin.erik.mobibot.LocalProperties
|
import net.thauvin.erik.mobibot.LocalProperties
|
||||||
import net.thauvin.erik.mobibot.Sanitize.sanitizeException
|
|
||||||
import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
|
import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import net.thauvin.erik.mobibot.msg.Message
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,23 +58,33 @@ class GoogleSearchTest : LocalProperties() {
|
||||||
val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP)
|
val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP)
|
||||||
try {
|
try {
|
||||||
var messages = searchGoogle("mobitopia", apiKey, cseKey)
|
var messages = searchGoogle("mobitopia", apiKey, cseKey)
|
||||||
assertThat(messages).describedAs("mobitopia results not empty").isNotEmpty
|
assertThat(messages, "mobitopia results not empty").isNotEmpty()
|
||||||
assertThat(messages[0].msg).describedAs("found mobibtopia").containsIgnoringCase("mobitopia")
|
assertThat(messages[0].msg, "found mobibtopia").contains("mobitopia", true)
|
||||||
|
|
||||||
messages = searchGoogle("aapl", apiKey, cseKey)
|
messages = searchGoogle("aapl", apiKey, cseKey)
|
||||||
assertThat(messages).describedAs("aapl results not empty").isNotEmpty
|
assertThat(messages, "aapl results not empty").isNotEmpty()
|
||||||
assertThat(messages[0].msg).describedAs("found apple").containsIgnoringCase("apple")
|
assertThat(messages[0].msg, "found apple").contains("apple", true)
|
||||||
assertThatThrownBy { searchGoogle("test", "", "apiKey") }
|
|
||||||
.describedAs("no API key")
|
messages = searchGoogle("adadflkjl", apiKey, cseKey)
|
||||||
|
assertThat(messages[0], "not found").all {
|
||||||
|
isInstanceOf(ErrorMessage::class.java)
|
||||||
|
prop(Message::msg).isEqualTo("No results found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
searchGoogle("", "apikey", "cssKey").first(),
|
||||||
|
"empty query"
|
||||||
|
).isInstanceOf(ErrorMessage::class.java)
|
||||||
|
|
||||||
|
assertThat { searchGoogle("test", "", "apiKey") }.isFailure()
|
||||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||||
assertThatThrownBy { searchGoogle("test", "apiKey", "") }
|
|
||||||
.describedAs("no CSE API key")
|
assertThat { searchGoogle("test", "apiKey", "") }.isFailure()
|
||||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||||
assertThatThrownBy { searchGoogle("", "apikey", "apiKey") }
|
|
||||||
.describedAs("no query").isInstanceOf(ModuleException::class.java).hasNoCause()
|
|
||||||
} 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")) {
|
||||||
throw sanitizeException(e, apiKey, cseKey)
|
throw e.sanitize(apiKey, cseKey)
|
||||||
} else {
|
} else {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,11 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.contains
|
||||||
|
import assertk.assertions.isNotEmpty
|
||||||
import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke
|
import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,7 +45,9 @@ class JokeTest {
|
||||||
@Test
|
@Test
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun testRandomJoke() {
|
fun testRandomJoke() {
|
||||||
assertThat(randomJoke().msg).describedAs("randomJoke() > 0").isNotEmpty
|
assertThat(randomJoke().msg, "randomJoke() > 0").all {
|
||||||
assertThat(randomJoke().msg).describedAs("randomJoke()").containsIgnoringCase("chuck")
|
isNotEmpty()
|
||||||
|
contains("chuck", true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,11 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.any
|
||||||
|
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.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,14 +46,13 @@ class LookupTest {
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testLookup() {
|
fun testLookup() {
|
||||||
val result = nslookup("apple.com")
|
val result = nslookup("apple.com")
|
||||||
assertThat(result).describedAs("lookup(apple.com)").contains("17.253.144.10")
|
assertThat(result, "lookup(apple.com)").contains("17.253.144.10")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testWhois() {
|
fun testWhois() {
|
||||||
val result = whois("17.178.96.59", Lookup.WHOIS_HOST)
|
val result = whois("17.178.96.59", Lookup.WHOIS_HOST)
|
||||||
assertThat(result).describedAs("whois(17.178.96.59/Apple Inc.")
|
assertThat(result, "whois(17.178.96.59/Apple Inc.").any { it.contains("Apple Inc.") }
|
||||||
.anyMatch { it.contains("Apple Inc.") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,16 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import net.thauvin.erik.mobibot.Sanitize.sanitizeException
|
import assertk.all
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.contains
|
||||||
|
import assertk.assertions.doesNotContain
|
||||||
|
import assertk.assertions.endsWith
|
||||||
|
import assertk.assertions.hasMessage
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isNotNull
|
||||||
|
import assertk.assertions.isNull
|
||||||
|
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
|
||||||
import org.testng.annotations.DataProvider
|
import org.testng.annotations.DataProvider
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -58,37 +66,41 @@ class ModuleExceptionTest {
|
||||||
|
|
||||||
@Test(dataProvider = "dp")
|
@Test(dataProvider = "dp")
|
||||||
fun testGetDebugMessage(e: ModuleException) {
|
fun testGetDebugMessage(e: ModuleException) {
|
||||||
assertThat(e.debugMessage).describedAs("get debug message").isEqualTo(debugMessage)
|
assertThat(e.debugMessage, "get debug message").isEqualTo(debugMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(dataProvider = "dp")
|
@Test(dataProvider = "dp")
|
||||||
fun testGetMessage(e: ModuleException) {
|
fun testGetMessage(e: ModuleException) {
|
||||||
assertThat(e).describedAs("get message").hasMessage(message)
|
assertThat(e, "get message").hasMessage(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSanitizeMessage() {
|
fun testSanitizeMessage() {
|
||||||
val apiKey = "1234567890"
|
val apiKey = "1234567890"
|
||||||
var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me"))
|
var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me"))
|
||||||
assertThat(sanitizeException(e, apiKey, "", "me")).describedAs("sanitized url")
|
assertThat(e.sanitize(apiKey, "", "me").message, "sanitized url").isNotNull().all {
|
||||||
.hasMessageContainingAll("xxxxxxxxxx", "userID=xx", "java.io.IOException")
|
contains("xxxxxxxxxx", "userID=xx", "java.io.IOException")
|
||||||
.hasMessageNotContainingAny(apiKey, "me")
|
doesNotContain(apiKey, "me")
|
||||||
|
}
|
||||||
|
|
||||||
e = ModuleException(debugMessage, message, null)
|
e = ModuleException(debugMessage, message, null)
|
||||||
assertThat(sanitizeException(e, apiKey)).describedAs("no cause").hasMessage(message)
|
assertThat(e.sanitize(apiKey), "no cause").hasMessage(message)
|
||||||
|
|
||||||
e = ModuleException(debugMessage, message, IOException())
|
e = ModuleException(debugMessage, message, IOException())
|
||||||
assertThat(sanitizeException(e, apiKey)).describedAs("no cause message").hasMessage(message)
|
assertThat(e.sanitize(apiKey), "no cause message").hasMessage(message)
|
||||||
|
|
||||||
e = ModuleException(apiKey)
|
e = ModuleException(apiKey)
|
||||||
assertThat(sanitizeException(e, apiKey)).describedAs("api key in message").hasMessageNotContaining(apiKey)
|
assertThat(e.sanitize(apiKey).message, "api key in message").isNotNull().doesNotContain(apiKey)
|
||||||
|
|
||||||
val msg: String? = null
|
val msg: String? = null
|
||||||
e = ModuleException(debugMessage, msg, IOException(msg))
|
e = ModuleException(debugMessage, msg, IOException(msg))
|
||||||
assertThat(sanitizeException(e, apiKey).message).describedAs("null message").isNull()
|
assertThat(e.sanitize(apiKey).message, "null message").isNull()
|
||||||
|
|
||||||
e = ModuleException(msg, msg, IOException("foo is $apiKey"))
|
e = ModuleException(msg, msg, IOException("foo is $apiKey"))
|
||||||
assertThat(sanitizeException(e, " ", apiKey, "foo").message).describedAs("key in cause")
|
assertThat(e.sanitize(" ", apiKey, "foo").message, "key in cause").isNotNull().all {
|
||||||
.doesNotContain(apiKey).endsWith("xxx is xxxxxxxxxx")
|
doesNotContain(apiKey)
|
||||||
|
endsWith("xxx is xxxxxxxxxx")
|
||||||
|
}
|
||||||
|
assertThat(e.sanitize(), "empty").isEqualTo(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,10 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.contains
|
||||||
|
import assertk.assertions.isNotEmpty
|
||||||
import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing
|
import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,13 +43,13 @@ import org.testng.annotations.Test
|
||||||
class PingTest {
|
class PingTest {
|
||||||
@Test
|
@Test
|
||||||
fun testPingsArray() {
|
fun testPingsArray() {
|
||||||
assertThat(Ping.PINGS).describedAs("Pings array is not empty.").isNotEmpty
|
assertThat(Ping.PINGS, "Pings array is not empty.").isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRandomPing() {
|
fun testRandomPing() {
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
assertThat(randomPing()).describedAs("Random ping $i").isIn(Ping.PINGS)
|
assertThat(Ping.PINGS, "Random ping $i").contains(randomPing())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,23 +32,19 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
class RockPaperScissorsTest {
|
class RockPaperScissorsTest {
|
||||||
@Test
|
@Test
|
||||||
fun testWinLoseOrDraw() {
|
fun testWinLoseOrDraw() {
|
||||||
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper")).describedAs("scissors vs. paper")
|
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win")
|
||||||
.isEqualTo("win")
|
assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win")
|
||||||
assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock")).describedAs("paper vs. rock").isEqualTo("win")
|
assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win")
|
||||||
assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors")).describedAs("rock vs. scissors")
|
assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose")
|
||||||
.isEqualTo("win")
|
assertThat(RockPaperScissors.winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose")
|
||||||
assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors")).describedAs("paper vs. scissors")
|
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose")
|
||||||
.isEqualTo("lose")
|
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw")
|
||||||
assertThat(RockPaperScissors.winLoseOrDraw("rock", "paper")).describedAs("rock vs. paper").isEqualTo("lose")
|
|
||||||
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "rock")).describedAs("scissors vs. rock")
|
|
||||||
.isEqualTo("lose")
|
|
||||||
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "scissors"))
|
|
||||||
.describedAs("scissors vs. scissors").isEqualTo("draw")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,11 +31,20 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.hasNoCause
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFailure
|
||||||
|
import assertk.assertions.isInstanceOf
|
||||||
|
import assertk.assertions.isNotEmpty
|
||||||
|
import assertk.assertions.matches
|
||||||
|
import assertk.assertions.prop
|
||||||
|
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
|
||||||
import net.thauvin.erik.mobibot.LocalProperties
|
import net.thauvin.erik.mobibot.LocalProperties
|
||||||
import net.thauvin.erik.mobibot.Sanitize.sanitizeException
|
|
||||||
import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote
|
import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import net.thauvin.erik.mobibot.msg.Message
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,24 +61,25 @@ class StockQuoteTest : LocalProperties() {
|
||||||
val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP)
|
val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP)
|
||||||
try {
|
try {
|
||||||
val messages = getQuote("apple inc", apiKey)
|
val messages = getQuote("apple inc", apiKey)
|
||||||
assertThat(messages).describedAs("response not empty").isNotEmpty
|
assertThat(messages, "response not empty").isNotEmpty()
|
||||||
assertThat(messages[0].msg).describedAs("same stock symbol").matches("Symbol: AAPL .*")
|
assertThat(messages[0].msg, "same stock symbol").matches("Symbol: AAPL .*".toRegex())
|
||||||
assertThat(messages[1].msg).describedAs("price label").matches(buildMatch("Price"))
|
assertThat(messages[1].msg, "price label").matches(buildMatch("Price").toRegex())
|
||||||
assertThat(messages[2].msg).describedAs("previous label").matches(buildMatch("Previous"))
|
assertThat(messages[2].msg, "previous label").matches(buildMatch("Previous").toRegex())
|
||||||
assertThat(messages[3].msg).describedAs("open label").matches(buildMatch("Open"))
|
assertThat(messages[3].msg, "open label").matches(buildMatch("Open").toRegex())
|
||||||
try {
|
|
||||||
getQuote("blahfoo", apiKey)
|
assertThat(getQuote("blahfoo", apiKey).first(), "invalid symbol").all {
|
||||||
} catch (e: ModuleException) {
|
isInstanceOf(ErrorMessage::class.java)
|
||||||
assertThat(e.message).describedAs("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL)
|
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||||
}
|
}
|
||||||
assertThatThrownBy { getQuote("test", "") }.describedAs("no API key")
|
assertThat(getQuote("", "apikey").first(), "empty symbol").all {
|
||||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
isInstanceOf(ErrorMessage::class.java)
|
||||||
assertThatThrownBy { getQuote("", "apikey") }.describedAs("no symbol")
|
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
}
|
||||||
|
assertThat { getQuote("test", "") }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||||
} 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")) {
|
||||||
throw sanitizeException(e, apiKey)
|
throw e.sanitize(apiKey)
|
||||||
} else {
|
} else {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,11 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isSuccess
|
||||||
import net.thauvin.erik.mobibot.LocalProperties
|
import net.thauvin.erik.mobibot.LocalProperties
|
||||||
import net.thauvin.erik.mobibot.modules.Twitter.Companion.twitterPost
|
import net.thauvin.erik.mobibot.modules.Twitter.Companion.twitterPost
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
|
@ -56,7 +58,7 @@ class TwitterTest : LocalProperties() {
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun testPostTwitter() {
|
fun testPostTwitter() {
|
||||||
val msg = "Testing Twitter API from $ci"
|
val msg = "Testing Twitter API from $ci"
|
||||||
assertThat(
|
assertThat {
|
||||||
twitterPost(
|
twitterPost(
|
||||||
getProperty(Twitter.CONSUMER_KEY_PROP),
|
getProperty(Twitter.CONSUMER_KEY_PROP),
|
||||||
getProperty(Twitter.CONSUMER_SECRET_PROP),
|
getProperty(Twitter.CONSUMER_SECRET_PROP),
|
||||||
|
@ -65,7 +67,7 @@ class TwitterTest : LocalProperties() {
|
||||||
getProperty(Twitter.HANDLE_PROP),
|
getProperty(Twitter.HANDLE_PROP),
|
||||||
msg,
|
msg,
|
||||||
true
|
true
|
||||||
).msg
|
)
|
||||||
).describedAs("twitterPost($msg)").isEqualTo(msg)
|
}.isSuccess().isEqualTo(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,16 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.all
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.contains
|
||||||
|
import assertk.assertions.endsWith
|
||||||
|
import assertk.assertions.hasNoCause
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFailure
|
||||||
|
import assertk.assertions.isInstanceOf
|
||||||
|
import assertk.assertions.isNotNull
|
||||||
|
import assertk.assertions.isTrue
|
||||||
import net.aksingh.owmjapis.api.APIException
|
import net.aksingh.owmjapis.api.APIException
|
||||||
import net.aksingh.owmjapis.core.OWM
|
import net.aksingh.owmjapis.core.OWM
|
||||||
import net.thauvin.erik.mobibot.LocalProperties
|
import net.thauvin.erik.mobibot.LocalProperties
|
||||||
|
@ -39,8 +49,6 @@ import net.thauvin.erik.mobibot.modules.Weather2.Companion.ftoC
|
||||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry
|
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getCountry
|
||||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather
|
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather
|
||||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh
|
import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
@ -51,50 +59,63 @@ class Weather2Test : LocalProperties() {
|
||||||
@Test
|
@Test
|
||||||
fun testFtoC() {
|
fun testFtoC() {
|
||||||
val t = ftoC(32.0)
|
val t = ftoC(32.0)
|
||||||
assertThat(t.second).describedAs("32 °F is 0 °C").isEqualTo(0)
|
assertThat(t.second, "32 °F is 0 °C").isEqualTo(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetCountry() {
|
fun testGetCountry() {
|
||||||
assertThat(getCountry("foo")).describedAs("not a country").isEqualTo(OWM.Country.UNITED_STATES)
|
assertThat(getCountry("foo"), "not a country").isEqualTo(OWM.Country.UNITED_STATES)
|
||||||
assertThat(getCountry("fr")).describedAs("fr is france").isEqualTo(OWM.Country.FRANCE)
|
assertThat(getCountry("fr"), "fr is france").isEqualTo(OWM.Country.FRANCE)
|
||||||
|
|
||||||
val country = OWM.Country.values()
|
val country = OWM.Country.values()
|
||||||
repeat(3) {
|
repeat(3) {
|
||||||
val rand = country[Random.nextInt(0, country.size - 1)]
|
val rand = country[Random.nextInt(0, country.size - 1)]
|
||||||
assertThat(getCountry(rand.value)).describedAs(rand.name).isEqualTo(rand)
|
assertThat(getCountry(rand.value), rand.name).isEqualTo(rand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMphToKmh() {
|
fun testMphToKmh() {
|
||||||
val w = mphToKmh(0.62)
|
val w = mphToKmh(0.62)
|
||||||
assertThat(w.second).describedAs("0.62 mph is 1 km/h").isEqualTo(1)
|
assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun testWeather() {
|
fun testWeather() {
|
||||||
var messages = getWeather("98204", getProperty(OWM_API_KEY_PROP))
|
var messages = getWeather("98204", getProperty(OWM_API_KEY_PROP))
|
||||||
assertThat(messages[0].msg).describedAs("is Everett").contains("Everett, United States").contains("US")
|
assertThat(messages[0].msg, "is Everett").all {
|
||||||
assertThat(messages[messages.size - 1].msg).describedAs("is Everett zip code").endsWith("98204%2CUS")
|
contains("Everett, United States")
|
||||||
|
contains("US")
|
||||||
|
}
|
||||||
|
assertThat(messages[messages.size - 1].msg, "is Everett zip code").endsWith("98204%2CUS")
|
||||||
|
|
||||||
messages = getWeather("San Francisco", getProperty(OWM_API_KEY_PROP))
|
messages = getWeather("San Francisco", getProperty(OWM_API_KEY_PROP))
|
||||||
assertThat(messages[0].msg).describedAs("is San Francisco").contains("San Francisco").contains("US")
|
assertThat(messages[0].msg, "is San Francisco").all {
|
||||||
assertThat(messages[messages.size - 1].msg).describedAs("is San Fran city code").endsWith("5391959")
|
contains("San Francisco")
|
||||||
|
contains("US")
|
||||||
|
}
|
||||||
|
assertThat(messages[messages.size - 1].msg, "is San Fran city code").endsWith("5391959")
|
||||||
|
|
||||||
messages = getWeather("London, GB", getProperty(OWM_API_KEY_PROP))
|
messages = getWeather("London, GB", getProperty(OWM_API_KEY_PROP))
|
||||||
assertThat(messages[0].msg).describedAs("is UK").contains("London, United Kingdom").contains("GB")
|
assertThat(messages[0].msg, "is UK").all {
|
||||||
assertThat(messages[messages.size - 1].msg).describedAs("is London city code").endsWith("2643743")
|
contains("London, United Kingdom")
|
||||||
|
contains("GB")
|
||||||
|
}
|
||||||
|
assertThat(messages[messages.size - 1].msg, "is London city code").endsWith("2643743")
|
||||||
|
|
||||||
assertThatThrownBy { getWeather("Foo, US", getProperty(OWM_API_KEY_PROP)) }
|
try {
|
||||||
.describedAs("foo not found").hasCauseInstanceOf(APIException::class.java)
|
getWeather("Foo, US", getProperty(OWM_API_KEY_PROP))
|
||||||
assertThatThrownBy { getWeather("test", "") }
|
} catch (e: ModuleException) {
|
||||||
.describedAs("no API key").isInstanceOf(ModuleException::class.java).hasNoCause()
|
assertThat(e.cause, "cause is API exception").isNotNull().isInstanceOf(APIException::class.java)
|
||||||
assertThatThrownBy { getWeather("test", null) }
|
}
|
||||||
.describedAs("null API key").isInstanceOf(ModuleException::class.java).hasNoCause()
|
|
||||||
|
assertThat { getWeather("test", "") }.isFailure()
|
||||||
|
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||||
|
assertThat { getWeather("test", null) }.isFailure()
|
||||||
|
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||||
|
|
||||||
messages = getWeather("", "apikey")
|
messages = getWeather("", "apikey")
|
||||||
assertThat(messages[0].isError).describedAs("no query").isTrue
|
assertThat(messages[0].isError, "no query").isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,18 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.erik.mobibot.modules
|
package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.endsWith
|
||||||
|
import assertk.assertions.isSuccess
|
||||||
|
import assertk.assertions.matches
|
||||||
|
import assertk.assertions.startsWith
|
||||||
import net.thauvin.erik.mobibot.Utils.bold
|
import net.thauvin.erik.mobibot.Utils.bold
|
||||||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.BEATS_KEYWORD
|
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.BEATS_KEYWORD
|
||||||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP
|
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP
|
||||||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time
|
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.pircbotx.Colors
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.zone.ZoneRulesException
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `WordTimeTest` class.
|
* The `WordTimeTest` class.
|
||||||
|
@ -46,16 +50,23 @@ import java.time.zone.ZoneRulesException
|
||||||
class WordTimeTest {
|
class WordTimeTest {
|
||||||
@Test
|
@Test
|
||||||
fun testTime() {
|
fun testTime() {
|
||||||
assertThat(time("PST").msg).describedAs("PST").endsWith(bold("Los Angeles"))
|
assertThat(time(), "no zone").matches(
|
||||||
assertThat(time("BLAH").isError).describedAs("BLAH").isTrue
|
("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " +
|
||||||
assertThat(time("BEAT").msg).describedAs(BEATS_KEYWORD).matches("[\\w ]+: .?@\\d{3}+.? .beats")
|
"on ${Colors.BOLD}\\w+, \\d{1,2} \\w+ \\d{4}${Colors.BOLD} " +
|
||||||
|
"in ${Colors.BOLD}Los Angeles${Colors.BOLD}").toRegex()
|
||||||
|
)
|
||||||
|
assertThat(time(""), "empty zone").endsWith(bold("Los Angeles"))
|
||||||
|
assertThat(time("PST"), "PST").endsWith(bold("Los Angeles"))
|
||||||
|
assertThat(time("GB"), "GB").endsWith(bold("London"))
|
||||||
|
assertThat(time("FR"), "FR").endsWith(bold("Paris"))
|
||||||
|
assertThat(time("BLAH"), "BLAH").startsWith("Unsupported")
|
||||||
|
assertThat(time("BEAT"), BEATS_KEYWORD).matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(ZoneRulesException::class)
|
fun testZones() {
|
||||||
fun testCountries() {
|
|
||||||
COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach {
|
COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach {
|
||||||
ZoneId.of(it.value)
|
assertThat { ZoneId.of(it.value) }.isSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,8 @@
|
||||||
|
|
||||||
package net.thauvin.erik.mobibot.msg
|
package net.thauvin.erik.mobibot.msg
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isTrue
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
class TestMessage {
|
class TestMessage {
|
||||||
|
@ -41,15 +42,15 @@ class TestMessage {
|
||||||
var msg = Message()
|
var msg = Message()
|
||||||
|
|
||||||
msg.isError = true
|
msg.isError = true
|
||||||
assertThat(msg.isNotice).describedAs("message is notice").isTrue
|
assertThat(msg.isNotice, "message is notice").isTrue()
|
||||||
|
|
||||||
msg = Message("foo", isError = true)
|
msg = Message("foo", isError = true)
|
||||||
assertThat(msg.isNotice).describedAs("message is notice too").isTrue
|
assertThat(msg.isNotice, "message is notice too").isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testErrorMessage() {
|
fun testErrorMessage() {
|
||||||
val msg = ErrorMessage("foo")
|
val msg = ErrorMessage("foo")
|
||||||
assertThat(msg.isNotice).describedAs("error message is notice").isTrue
|
assertThat(msg.isNotice, "error message is notice").isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
36
src/test/resources/current.xml
Normal file
36
src/test/resources/current.xml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
|
||||||
|
<channel>
|
||||||
|
<title>#mobibot IRC Links</title>
|
||||||
|
<link>https://www.mobitopia.org/mobibot/logs</link>
|
||||||
|
<description>Links from irc.example.com on #mobibot</description>
|
||||||
|
<language>en</language>
|
||||||
|
<pubDate>Sun, 31 Oct 2021 21:45:11 GMT</pubDate>
|
||||||
|
<dc:date>2021-10-31T21:45:11Z</dc:date>
|
||||||
|
<dc:language>en</dc:language>
|
||||||
|
<item>
|
||||||
|
<title>Example 2</title>
|
||||||
|
<link>https://www.example.com/2</link>
|
||||||
|
<description>Posted by <b>Skynx</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a></description>
|
||||||
|
<category>tag2-1</category>
|
||||||
|
<category>tag2-2</category>
|
||||||
|
<pubDate>Sun, 31 Oct 2021 21:45:11 GMT</pubDate>
|
||||||
|
<guid>https://www.foo.com</guid>
|
||||||
|
<dc:creator>mobibot@irc.libera.chat (Skynx)</dc:creator>
|
||||||
|
<dc:date>2021-10-31T00:01:00Z</dc:date>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title>Example 1</title>
|
||||||
|
<link>https://www.example.com/1</link>
|
||||||
|
<description>Posted by <b>ErikT</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a>
|
||||||
|
<br/><br/>ErikT: This is comment 1. <br/>Skynx: This is comment 2.
|
||||||
|
</description>
|
||||||
|
<category>tag1-1</category>
|
||||||
|
<category>tag1-2</category>
|
||||||
|
<pubDate>Sun, 31 Oct 2021 21:43:15 GMT</pubDate>
|
||||||
|
<guid>https://www.example.com/</guid>
|
||||||
|
<dc:creator>mobibot@irc.libera.chat (ErikT)</dc:creator>
|
||||||
|
<dc:date>2021-10-31T00:00:00Z</dc:date>
|
||||||
|
</item>
|
||||||
|
</channel>
|
||||||
|
</rss>
|
|
@ -1,9 +1,9 @@
|
||||||
#Generated by the Semver Plugin for Gradle
|
#Generated by the Semver Plugin for Gradle
|
||||||
#Wed Sep 15 17:27:25 PDT 2021
|
#Mon Nov 08 13:50:12 PST 2021
|
||||||
version.buildmeta=1400
|
version.buildmeta=2220
|
||||||
version.major=0
|
version.major=0
|
||||||
version.minor=8
|
version.minor=8
|
||||||
version.patch=0
|
version.patch=0
|
||||||
version.prerelease=beta
|
version.prerelease=beta
|
||||||
version.project=mobibot
|
version.project=mobibot
|
||||||
version.semver=0.8.0-beta+1400
|
version.semver=0.8.0-beta+2220
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue