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="url" value="file:$MAVEN_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>
|
||||
</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 'com.github.ben-manes.versions' version '0.39.0'
|
||||
id 'idea'
|
||||
id 'io.gitlab.arturbosch.detekt' version '1.18.1'
|
||||
id 'io.gitlab.arturbosch.detekt' version '1.19.0-RC1'
|
||||
id 'jacoco'
|
||||
id 'java'
|
||||
id 'net.thauvin.erik.gradle.semver' version '1.0.4'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.6.0-RC'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.6.0-RC'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.6.0-RC2'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.6.0-RC2'
|
||||
id 'org.sonarqube' version '3.3'
|
||||
id 'pmd'
|
||||
}
|
||||
|
@ -22,12 +22,13 @@ mainClassName = packageName + '.Mobibot'
|
|||
|
||||
ext.versions = [
|
||||
log4j: '2.14.1',
|
||||
pmd : '6.35.0',
|
||||
pmd : '6.40.0',
|
||||
]
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||
}
|
||||
|
||||
|
@ -35,8 +36,13 @@ dependencies {
|
|||
kapt(semverProcessor)
|
||||
compileOnly(semverProcessor)
|
||||
|
||||
implementation 'pircbot:pircbot:1.5.0'
|
||||
compileOnly 'pircbot:pircbot:1.5.0:sources'
|
||||
implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT'
|
||||
|
||||
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 '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-core:$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 '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.objecthunter:exp4j:0.4.8'
|
||||
|
||||
|
@ -59,7 +66,9 @@ dependencies {
|
|||
implementation 'org.jsoup:jsoup:1.14.3'
|
||||
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'
|
||||
}
|
||||
|
||||
|
@ -117,6 +126,7 @@ jar {
|
|||
manifest.attributes('Main-Class': mainClassName,
|
||||
'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/'))
|
||||
archiveVersion.set("")
|
||||
exclude('log4j2.xml')
|
||||
}
|
||||
|
||||
clean {
|
||||
|
|
|
@ -2,54 +2,76 @@
|
|||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
||||
<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>LongMethod:EntriesMgr.kt$EntriesMgr$ fun saveEntries( bot: Mobibot, entries: List<EntryLink>, history: MutableList<String>, isDayBackup: Boolean )</ID>
|
||||
<ID>LongMethod:Mobibot.kt$Mobibot.Companion$ @JvmStatic fun main(args: Array<String>)</ID>
|
||||
<ID>LongMethod:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</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: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$( 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:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent )</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:Calc.kt$Calc$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID>
|
||||
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</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:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</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:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID>
|
||||
<ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID>
|
||||
<ID>NestedBlockDepth:Mobibot.kt$Mobibot$ fun connect()</ID>
|
||||
<ID>NestedBlockDepth:StockQuote.kt$StockQuote$ 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:FeedsMgr.kt$FeedsMgr.Companion$ fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID>
|
||||
<ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID>
|
||||
<ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</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:Tell.kt$Tell$ @JvmOverloads fun send(nickname: String, isMessage: Boolean = false)</ID>
|
||||
<ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean)</ID>
|
||||
<ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ fun save(file: String, messages: List<TellMessage?>?, logger: Logger)</ID>
|
||||
<ID>NestedBlockDepth:Tell.kt$Tell$ fun send(event: GenericUserEvent)</ID>
|
||||
<ID>NestedBlockDepth:Tell.kt$Tell$// Delete message. private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent)</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: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:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: 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(sender: String, topic: String, isOp: Boolean, isPrivate: Boolean): 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 help(channel: String, topic: String, event: GenericMessageEvent): 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: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: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:Weather2.kt$Weather2.Companion$e: NullPointerException</ID>
|
||||
<ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID>
|
||||
<ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
|
@ -1,16 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Configuration status="warn">
|
||||
<Appenders>
|
||||
<Console name="stderr" target="SYSTEM_ERR">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</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>
|
||||
<Loggers>
|
||||
<Logger name="net.thauvin.erik.mobibot" level="warn" additivity="false">
|
||||
<AppenderRef ref="stderr"/>
|
||||
</Logger>
|
||||
<Root level="error">
|
||||
<Root level="warn" additivity="false">
|
||||
<AppenderRef ref="stderr"/>
|
||||
</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>
|
||||
</Configuration>
|
||||
|
|
|
@ -3,9 +3,13 @@ server=irc.freenode.net
|
|||
#port=6667
|
||||
login=mobibot
|
||||
nick=mobibot
|
||||
#realname=mobibot
|
||||
|
||||
# Die command password, if any
|
||||
#die=changeme
|
||||
|
||||
# NickServ password
|
||||
ident=changepwd
|
||||
ident=changeme
|
||||
#ident-nick=nickserv
|
||||
#ident-msg=IDENTIFY changepwd
|
||||
|
||||
|
@ -14,7 +18,6 @@ ignore=chanserv,nickserv
|
|||
tags=mobile mobitopia
|
||||
tags-keywords=android ios apple google
|
||||
|
||||
weblog=http://www.mobitopia.org/
|
||||
feed=http://www.mobitopia.org/rss.xml
|
||||
backlogs=http://www.mobitopia.org/mobibot/logs
|
||||
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
|
@ -66,8 +66,8 @@ public final class War extends AbstractModule {
|
|||
/**
|
||||
* The default constructor.
|
||||
*/
|
||||
public War(final Mobibot bot) {
|
||||
super(bot);
|
||||
public War() {
|
||||
super();
|
||||
|
||||
commands.add(WAR_CMD);
|
||||
|
||||
|
@ -79,10 +79,8 @@ public final class War extends AbstractModule {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(@NotNull final String sender,
|
||||
@NotNull final String cmd,
|
||||
@NotNull final String args,
|
||||
final boolean isPrivate) {
|
||||
public void commandResponse(@NotNull final String channel, @NotNull final String cmd, @NotNull final String args,
|
||||
@NotNull final GenericMessageEvent event) {
|
||||
int i;
|
||||
int y;
|
||||
|
||||
|
@ -90,20 +88,20 @@ public final class War extends AbstractModule {
|
|||
i = RANDOM.nextInt(HEARTS.length);
|
||||
y = RANDOM.nextInt(HEARTS.length);
|
||||
|
||||
getBot().send(sender + " drew: " + DECK[RANDOM.nextInt(DECK.length)][i]);
|
||||
getBot().action("drew: " + DECK[RANDOM.nextInt(DECK.length)][y]);
|
||||
event.respond("you drew " + DECK[RANDOM.nextInt(DECK.length)][i]);
|
||||
event.getBot().sendIRC().action(channel, "drew " + DECK[RANDOM.nextInt(DECK.length)][y]);
|
||||
|
||||
if (i != y) {
|
||||
break;
|
||||
}
|
||||
|
||||
getBot().send("This means " + bold("WAR") + '!');
|
||||
event.respond("This means " + bold("WAR") + '!');
|
||||
}
|
||||
|
||||
if (i < y) {
|
||||
getBot().action("lost.");
|
||||
event.getBot().sendIRC().action(channel, "lost.");
|
||||
} 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.modules.AbstractModule
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.util.Properties
|
||||
|
||||
/**
|
||||
|
@ -77,7 +79,7 @@ class Addons {
|
|||
if (isEnabled()) {
|
||||
commands.add(this)
|
||||
if (isVisible) {
|
||||
if (isOp) {
|
||||
if (isOpOnly) {
|
||||
ops.add(name)
|
||||
} else {
|
||||
names.add(name)
|
||||
|
@ -90,17 +92,18 @@ class Addons {
|
|||
/**
|
||||
* Execute a command or module.
|
||||
*/
|
||||
fun exec(sender: String, login: String, cmd: String, args: String, isOp: Boolean, isPrivate: Boolean): Boolean {
|
||||
for (command in commands) {
|
||||
fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean {
|
||||
val cmds = if (event is PrivateMessageEvent) commands else commands.filter { it.isPublic }
|
||||
for (command in cmds) {
|
||||
if (command.name.startsWith(cmd)) {
|
||||
command.commandResponse(sender, login, args, isOp, isPrivate)
|
||||
command.commandResponse(channel, args, event)
|
||||
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) {
|
||||
if (module.commands.contains(cmd)) {
|
||||
module.commandResponse(sender, cmd, args, isPrivate)
|
||||
module.commandResponse(channel, cmd, args, event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -110,10 +113,10 @@ class Addons {
|
|||
/**
|
||||
* 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) {
|
||||
if (command.matches(message)) {
|
||||
command.commandResponse(sender, login, message, isOp, isPrivate)
|
||||
if (command.matches(event.message)) {
|
||||
command.commandResponse(channel, event.message, event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -123,15 +126,15 @@ class Addons {
|
|||
/**
|
||||
* 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) {
|
||||
if (command.isVisible && command.name.startsWith(topic)) {
|
||||
return command.helpResponse(topic, sender, isOp, isPrivate)
|
||||
return command.helpResponse(channel, topic, event)
|
||||
}
|
||||
}
|
||||
for (module in modules) {
|
||||
if (module.commands.contains(topic)) {
|
||||
return module.helpResponse(sender, isPrivate)
|
||||
return module.helpResponse(event)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -45,11 +45,6 @@ object Constants {
|
|||
*/
|
||||
const val DEBUG_ARG = "debug"
|
||||
|
||||
/**
|
||||
* The debug command.
|
||||
*/
|
||||
const val DEBUG_CMD = "debug"
|
||||
|
||||
/**
|
||||
* Default IRC Port.
|
||||
*/
|
||||
|
@ -58,12 +53,7 @@ object Constants {
|
|||
/**
|
||||
* Default IRC Server.
|
||||
*/
|
||||
const val DEFAULT_SERVER = "irc.freenode.net"
|
||||
|
||||
/**
|
||||
* The die command.
|
||||
*/
|
||||
const val DIE_CMD = "die"
|
||||
const val DEFAULT_SERVER = "irc.libera.chat"
|
||||
|
||||
/**
|
||||
* Help command line argument.
|
||||
|
@ -75,11 +65,6 @@ object Constants {
|
|||
*/
|
||||
const val HELP_CMD = "help"
|
||||
|
||||
/**
|
||||
* The kill command.
|
||||
*/
|
||||
const val KILL_CMD = "kill"
|
||||
|
||||
/**
|
||||
* The link command.
|
||||
*/
|
||||
|
|
|
@ -36,38 +36,36 @@ import com.rometools.rome.io.SyndFeedInput
|
|||
import com.rometools.rome.io.XmlReader
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
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.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.net.URL
|
||||
|
||||
/**
|
||||
* Reads an RSS feed.
|
||||
*/
|
||||
class FeedReader(
|
||||
// Bot
|
||||
private val bot: Mobibot,
|
||||
// Nick of the person who sent the message
|
||||
private val sender: String,
|
||||
// URL to fetch
|
||||
private val url: String
|
||||
) : Runnable {
|
||||
class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable {
|
||||
private val logger: Logger = LoggerFactory.getLogger(FeedsMgr::class.java)
|
||||
|
||||
/**
|
||||
* Fetches the Feed's items.
|
||||
*/
|
||||
override fun run() {
|
||||
with(bot) {
|
||||
try {
|
||||
readFeed(url).forEach {
|
||||
send(sender, it)
|
||||
}
|
||||
} catch (e: FeedException) {
|
||||
if (logger.isDebugEnabled) logger.debug("Unable to parse the feed at $url", e)
|
||||
send(sender, "An error has occurred while parsing the feed: ${e.message}", false)
|
||||
} catch (e: IOException) {
|
||||
if (logger.isDebugEnabled) logger.debug("Unable to fetch the feed at $url", e)
|
||||
send(sender, "An error has occurred while fetching the feed: ${e.message}", false)
|
||||
try {
|
||||
readFeed(url).forEach {
|
||||
event.sendMessage("", it)
|
||||
}
|
||||
} catch (e: FeedException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e)
|
||||
event.sendMessage("An error has occurred while parsing the feed: ${e.message}")
|
||||
} catch (e: IOException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e)
|
||||
event.sendMessage("An error has occurred while fetching the feed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,11 +79,11 @@ class FeedReader(
|
|||
val feed = input.build(reader)
|
||||
val items = feed.entries
|
||||
if (items.isEmpty()) {
|
||||
messages.add(PublicMessage("There is currently nothing to view."))
|
||||
messages.add(NoticeMessage("There is currently nothing to view."))
|
||||
} else {
|
||||
items.take(maxItems).forEach {
|
||||
messages.add(PublicMessage(it.title))
|
||||
messages.add(PublicMessage(helpFormat(green(it.link), false)))
|
||||
messages.add(NoticeMessage(it.title))
|
||||
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
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.colorize
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
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.today
|
||||
import net.thauvin.erik.mobibot.commands.AddLog
|
||||
import net.thauvin.erik.mobibot.commands.ChannelFeed
|
||||
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.Ignore
|
||||
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.Modules
|
||||
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.View
|
||||
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.CryptoPrices
|
||||
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.RockPaperScissors
|
||||
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.Weather2
|
||||
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 org.apache.commons.cli.CommandLine
|
||||
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.Options
|
||||
import org.apache.commons.cli.ParseException
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.apache.logging.log4j.Logger
|
||||
import org.jibble.pircbot.PircBot
|
||||
import org.pircbotx.Configuration
|
||||
import org.pircbotx.PircBotX
|
||||
import org.pircbotx.hooks.ListenerAdapter
|
||||
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.File
|
||||
import java.io.FileNotFoundException
|
||||
|
@ -100,427 +102,139 @@ import java.io.PrintStream
|
|||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.Properties
|
||||
import java.util.Timer
|
||||
import java.util.logging.ConsoleHandler
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* Implements the #mobitopia bot.
|
||||
*/
|
||||
@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
|
||||
private val addons = Addons()
|
||||
|
||||
// Tell module
|
||||
private val tell: Tell
|
||||
|
||||
/** Main channel. */
|
||||
val channel: String
|
||||
|
||||
// IRC port
|
||||
private val ircPort: Int
|
||||
|
||||
/** IRC server. */
|
||||
val ircServer: String
|
||||
|
||||
/** Logger. */
|
||||
val logger: Logger = LogManager.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)
|
||||
}
|
||||
}
|
||||
val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java)
|
||||
|
||||
/**
|
||||
* Connects to the server and joins the channel.
|
||||
*/
|
||||
fun connect() {
|
||||
try {
|
||||
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)
|
||||
}
|
||||
PircBotX(config).startBot()
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the default help.
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
fun helpDefault(sender: String, isOp: Boolean, isPrivate: Boolean) {
|
||||
send(sender, "Type a URL on $channel to post it.", isPrivate)
|
||||
send(sender, "For more information on a specific command, type:", isPrivate)
|
||||
send(
|
||||
sender,
|
||||
helpFormat(buildCmdSyntax("%c ${Constants.HELP_CMD} <command>", nick, isPrivate)),
|
||||
isPrivate
|
||||
private fun helpDefault(event: GenericMessageEvent) {
|
||||
event.sendMessage("Type a URL on $channel to post it.")
|
||||
event.sendMessage("For more information on a specific command, type:")
|
||||
event.sendMessage(
|
||||
Utils.helpFormat(
|
||||
Utils.buildCmdSyntax(
|
||||
"%c ${Constants.HELP_CMD} <command>",
|
||||
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) {
|
||||
send(sender, "The op commands are:", isPrivate)
|
||||
sendList(sender, addons.ops, 8, isPrivate = isPrivate, isBold = true, isIndent = true)
|
||||
event.sendMessage("The commands are:")
|
||||
event.sendList(addons.names, 8, 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.
|
||||
*/
|
||||
private fun helpResponse(sender: String, topic: String, isPrivate: Boolean) {
|
||||
val isOp = isOp(sender)
|
||||
if (topic.isBlank() || !addons.help(sender, topic.lowercase().trim(), isOp, isPrivate)) {
|
||||
helpDefault(sender, isOp, isPrivate)
|
||||
private fun helpResponse(event: GenericMessageEvent, topic: String) {
|
||||
if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) {
|
||||
helpDefault(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the bot.
|
||||
*/
|
||||
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 onAction(event: ActionEvent?) {
|
||||
if (channel == event?.channel?.name) {
|
||||
storeRecap(event.user!!.nick, event.action, true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the specified sender is an Op on the [channel][.ircChannel].
|
||||
*/
|
||||
fun isOp(sender: String): Boolean {
|
||||
for (user in getUsers(channel)) {
|
||||
if (user.nick == sender) {
|
||||
return user.isOp
|
||||
override fun onDisconnect(event: DisconnectEvent?) {
|
||||
with(event!!.getBot<PircBotX>()) {
|
||||
LinksMgr.twitter.notification("$nick disconnected from irc://$serverHostname")
|
||||
}
|
||||
LinksMgr.twitter.shutdown()
|
||||
}
|
||||
|
||||
override fun onPrivateMessage(event: PrivateMessageEvent?) {
|
||||
if (logger.isTraceEnabled) logger.trace("<<< ${event!!.user!!.nick}: ${event.message}")
|
||||
val cmds = event!!.message.trim().split(" ".toRegex(), 2)
|
||||
val cmd = cmds[0].lowercase()
|
||||
val args = if (cmds.size > 1) {
|
||||
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 onJoin(event: JoinEvent?) {
|
||||
with(event!!.getBot<PircBotX>()) {
|
||||
if (event.user!!.nick == nick) {
|
||||
LinksMgr.twitter.notification("$nick has joined ${event.channel.name} on irc://$serverHostname")
|
||||
} else {
|
||||
tell.send(event)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the bot's channel.
|
||||
*/
|
||||
private fun joinChannel() {
|
||||
joinChannel(channel)
|
||||
twitter.notification("$name ${ReleaseInfo.VERSION} has joined $channel")
|
||||
}
|
||||
|
||||
override fun onDisconnect() {
|
||||
if (weblogUrl.isNotBlank()) {
|
||||
version = weblogUrl
|
||||
}
|
||||
@Suppress("MagicNumber")
|
||||
sleep(5)
|
||||
connect()
|
||||
}
|
||||
|
||||
override fun onMessage(
|
||||
channel: String,
|
||||
sender: String,
|
||||
login: String,
|
||||
hostname: String,
|
||||
message: String
|
||||
) {
|
||||
if (logger.isDebugEnabled) logger.debug(">>> $sender: $message")
|
||||
tell.send(sender, true)
|
||||
if (message.matches("(?i)${Pattern.quote(nick)}:.*".toRegex())) { // mobibot: <command>
|
||||
override fun onMessage(event: MessageEvent?) {
|
||||
val sender = event!!.user!!.nick
|
||||
val message = event.message
|
||||
tell.send(event)
|
||||
if (message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command>
|
||||
if (logger.isTraceEnabled) logger.trace(">>> $sender: $message")
|
||||
val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2)
|
||||
val cmd = cmds[0].lowercase()
|
||||
val args = if (cmds.size > 1) {
|
||||
cmds[1].trim()
|
||||
} else ""
|
||||
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
|
||||
helpResponse(sender, args, false)
|
||||
helpResponse(event, args)
|
||||
} else {
|
||||
// Execute module or command
|
||||
addons.exec(sender, login, cmd, args, isOp(sender), false)
|
||||
addons.exec(channel, cmd, args, event)
|
||||
}
|
||||
} else {
|
||||
// Links, e.g.: https://www.example.com/ or L1: , etc.
|
||||
addons.match(sender, login, message, isOp(sender), false)
|
||||
} else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc.
|
||||
if (logger.isTraceEnabled) logger.trace(">>> $sender: $message")
|
||||
}
|
||||
storeRecap(sender, message, false)
|
||||
}
|
||||
|
||||
override fun onPrivateMessage(
|
||||
sender: String,
|
||||
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 onNickChange(event: NickChangeEvent?) {
|
||||
tell.send(event!!)
|
||||
}
|
||||
|
||||
override fun onAction(sender: String, login: String, hostname: String, target: String, action: String) {
|
||||
if (channel == target) {
|
||||
storeRecap(sender, action, true)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
override fun onPart(event: PartEvent?) {
|
||||
with(event!!.getBot<PircBotX>()) {
|
||||
if (event.user!!.nick == nick) {
|
||||
LinksMgr.twitter.notification("$nick has left ${event.channel.name} on irc://$serverHostname")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
// Maximum number of times the bot will try to reconnect, if disconnected
|
||||
private const val MAX_RECONNECT = 10
|
||||
|
||||
/**
|
||||
* The Truth is Out There!
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
// Set up the command line options
|
||||
|
@ -593,7 +307,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
|
|||
val stdout = PrintStream(
|
||||
BufferedOutputStream(
|
||||
FileOutputStream(
|
||||
logsDir + channel.substring(1) + '.' + today() + ".log", true
|
||||
logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true
|
||||
)
|
||||
), true
|
||||
)
|
||||
|
@ -615,11 +329,8 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
|
|||
}
|
||||
}
|
||||
|
||||
// Create the bot
|
||||
val bot = Mobibot(nickname, channel, logsDir, p)
|
||||
|
||||
// Connect
|
||||
bot.connect()
|
||||
// Start the bot
|
||||
Mobibot(nickname, channel, logsDir, p).connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -629,97 +340,93 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert
|
|||
* Initialize the bot.
|
||||
*/
|
||||
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
|
||||
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
|
||||
loggerLevel = logger.level
|
||||
// Load the current entries
|
||||
with(LinksMgr) {
|
||||
entries.channel = channel
|
||||
entries.ircServer = ircServer
|
||||
entries.logsDir = logsDirPath
|
||||
entries.backlogs = p.getProperty("backlogs", "")
|
||||
entries.load()
|
||||
|
||||
setVerbose(true)
|
||||
setAutoNickChange(true)
|
||||
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 up pinboard
|
||||
pinboard.setApiToken(p.getProperty("pinboard-api-token", ""))
|
||||
}
|
||||
|
||||
// Set the pinboard authentication
|
||||
setPinboardAuth(p.getProperty("pinboard-api-token", ""))
|
||||
|
||||
// Load the commands
|
||||
addons.add(AddLog(this), p)
|
||||
addons.add(ChannelFeed(this, channelName), p)
|
||||
addons.add(Cycle(this), p)
|
||||
addons.add(Die(this), p)
|
||||
addons.add(Debug(this), p)
|
||||
addons.add(Ignore(this), p)
|
||||
addons.add(Info(this), p)
|
||||
addons.add(Kill(this), p)
|
||||
addons.add(Me(this), p)
|
||||
addons.add(Modules(this), p)
|
||||
addons.add(Msg(this), p)
|
||||
addons.add(Nick(this), p)
|
||||
addons.add(Recap(this), p)
|
||||
addons.add(Say(this), p)
|
||||
addons.add(Users(this), p)
|
||||
addons.add(Versions(this), p)
|
||||
addons.add(ChannelFeed(channel.removePrefix("#")), p)
|
||||
addons.add(Comment(), p)
|
||||
addons.add(Cycle(), p)
|
||||
addons.add(Die(), p)
|
||||
addons.add(Ignore(), p)
|
||||
addons.add(LinksMgr(), p)
|
||||
addons.add(Me(), p)
|
||||
addons.add(Msg(), p)
|
||||
addons.add(Nick(), p)
|
||||
addons.add(Posting(), p)
|
||||
addons.add(Recap(), p)
|
||||
addons.add(Say(), p)
|
||||
addons.add(Tags(), p)
|
||||
|
||||
// Tell command
|
||||
tell = Tell(this)
|
||||
tell = Tell("${logsDirPath}${nickname}.ser")
|
||||
addons.add(tell, p)
|
||||
|
||||
// Load the links commands
|
||||
addons.add(Comment(this), p)
|
||||
addons.add(Posting(this), p)
|
||||
addons.add(Tags(this), p)
|
||||
addons.add(LinksMgr(this), p)
|
||||
addons.add(View(this), p)
|
||||
addons.add(LinksMgr.twitter, p)
|
||||
addons.add(Users(), p)
|
||||
addons.add(Versions(), p)
|
||||
addons.add(View(), p)
|
||||
|
||||
// Load the modules
|
||||
addons.add(Calc(this), p)
|
||||
addons.add(CryptoPrices(this), p)
|
||||
addons.add(CurrencyConverter(this), p)
|
||||
addons.add(Dice(this), p)
|
||||
addons.add(GoogleSearch(this), p)
|
||||
addons.add(Joke(this), p)
|
||||
addons.add(Lookup(this), p)
|
||||
addons.add(Ping(this), p)
|
||||
addons.add(RockPaperScissors(this), p)
|
||||
addons.add(StockQuote(this), p)
|
||||
addons.add(War(this), p)
|
||||
addons.add(Weather2(this), p)
|
||||
addons.add(WorldTime(this), p)
|
||||
|
||||
// Twitter module
|
||||
twitter = Twitter(this)
|
||||
addons.add(twitter, p)
|
||||
addons.add(Calc(), p)
|
||||
addons.add(CryptoPrices(), p)
|
||||
addons.add(CurrencyConverter(), p)
|
||||
addons.add(Dice(), p)
|
||||
addons.add(GoogleSearch(), p)
|
||||
addons.add(Info(tell), p)
|
||||
addons.add(Joke(), p)
|
||||
addons.add(Lookup(), p)
|
||||
addons.add(Modules(addons.modulesNames), p)
|
||||
addons.add(Ping(), p)
|
||||
addons.add(RockPaperScissors(), p)
|
||||
addons.add(StockQuote(), p)
|
||||
addons.add(Weather2(), p)
|
||||
addons.add(WorldTime(), p)
|
||||
addons.add(War(), p)
|
||||
|
||||
// Sort the addons
|
||||
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)
|
||||
* All rights reserved.
|
||||
|
@ -45,33 +45,44 @@ import java.util.Date
|
|||
/**
|
||||
* Handles posts to pinboard.in.
|
||||
*/
|
||||
object PinboardUtils {
|
||||
class Pinboard {
|
||||
private val poster = PinboardPoster()
|
||||
|
||||
/**
|
||||
* Adds a pin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun addPin(poster: PinboardPoster, ircServer: String, entry: EntryLink) {
|
||||
runBlocking {
|
||||
launch {
|
||||
poster.addPin(
|
||||
entry.link,
|
||||
entry.title,
|
||||
entry.postedBy(ircServer),
|
||||
entry.pinboardTags,
|
||||
entry.date.toTimestamp()
|
||||
)
|
||||
fun addPin(ircServer: String, entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
poster.addPin(
|
||||
entry.link,
|
||||
entry.title,
|
||||
entry.postedBy(ircServer),
|
||||
entry.pinboardTags,
|
||||
entry.date.toTimestamp()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pinboard API token.
|
||||
*/
|
||||
fun setApiToken(apiToken: String) {
|
||||
poster.apiToken = apiToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a pin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun deletePin(poster: PinboardPoster, entry: EntryLink) {
|
||||
runBlocking {
|
||||
launch {
|
||||
poster.deletePin(entry.link)
|
||||
fun deletePin(entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
poster.deletePin(entry.link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,30 +90,15 @@ object PinboardUtils {
|
|||
/**
|
||||
* Updates a pin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun updatePin(poster: PinboardPoster, ircServer: String, oldUrl: String, entry: EntryLink) {
|
||||
runBlocking {
|
||||
launch {
|
||||
with(entry) {
|
||||
if (oldUrl != link) {
|
||||
poster.deletePin(oldUrl)
|
||||
poster.addPin(
|
||||
link,
|
||||
title,
|
||||
entry.postedBy(ircServer),
|
||||
pinboardTags,
|
||||
date.toTimestamp()
|
||||
)
|
||||
} else {
|
||||
poster.addPin(
|
||||
link,
|
||||
title,
|
||||
entry.postedBy(ircServer),
|
||||
pinboardTags,
|
||||
date.toTimestamp(),
|
||||
replace = true,
|
||||
shared = true
|
||||
)
|
||||
fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
with(entry) {
|
||||
if (oldUrl != link) {
|
||||
poster.deletePin(oldUrl)
|
||||
}
|
||||
poster.addPin(link, title, entry.postedBy(ircServer), pinboardTags, date.toTimestamp())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,8 +108,7 @@ object PinboardUtils {
|
|||
/**
|
||||
* Format a date to a UTC timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Date.toTimestamp(): String {
|
||||
private fun Date.toTimestamp(): String {
|
||||
return ZonedDateTime.ofInstant(
|
||||
this.toInstant().truncatedTo(ChronoUnit.SECONDS),
|
||||
ZoneId.systemDefault()
|
|
@ -95,7 +95,6 @@ object TwitterOAuth {
|
|||
""".trimIndent()
|
||||
)
|
||||
} catch (te: TwitterException) {
|
||||
@Suppress("MagicNumber")
|
||||
if (401 == te.statusCode) {
|
||||
println("Unable to get the access token.")
|
||||
} else {
|
||||
|
|
|
@ -32,10 +32,11 @@
|
|||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.modules.Twitter
|
||||
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() {
|
||||
bot.twitter.postEntry(index)
|
||||
twitter.postEntry(index)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,9 +31,13 @@
|
|||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
||||
import org.jibble.pircbot.Colors
|
||||
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.IOException
|
||||
import java.io.InputStreamReader
|
||||
|
@ -85,6 +89,13 @@ object Utils {
|
|||
@JvmStatic
|
||||
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
|
||||
* nick.
|
||||
|
@ -159,6 +170,14 @@ object Utils {
|
|||
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.
|
||||
*/
|
||||
|
@ -203,6 +222,56 @@ object Utils {
|
|||
@JvmStatic
|
||||
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.
|
||||
*/
|
||||
|
@ -258,7 +327,6 @@ object Utils {
|
|||
/**
|
||||
* Converts milliseconds to year month week day hour and minutes.
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
@JvmStatic
|
||||
fun uptime(uptime: Long): String {
|
||||
val info = StringBuilder()
|
||||
|
|
|
@ -32,31 +32,31 @@
|
|||
|
||||
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.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
|
||||
|
||||
abstract class AbstractCommand(val bot: Mobibot) {
|
||||
abstract class AbstractCommand {
|
||||
abstract val name: String
|
||||
abstract val help: List<String>
|
||||
abstract val isOp: Boolean
|
||||
abstract val isOpOnly: Boolean
|
||||
abstract val isPublic: Boolean
|
||||
abstract val isVisible: Boolean
|
||||
|
||||
val properties: MutableMap<String, String> = ConcurrentHashMap()
|
||||
|
||||
abstract fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
)
|
||||
abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent)
|
||||
|
||||
open fun helpResponse(command: String, sender: String, isOp: Boolean, isPrivate: Boolean): Boolean {
|
||||
if (!this.isOp || this.isOp == isOp) {
|
||||
open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
if (!isOpOnly || isOpOnly == isChannelOp(channel, event)) {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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.runBlocking
|
||||
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.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 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 isVisible = true
|
||||
|
||||
|
@ -53,22 +54,16 @@ class ChannelFeed(bot: Mobibot, channel: String) : AbstractCommand(bot) {
|
|||
initProperties(FEED_PROP)
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
with(properties[FEED_PROP]) {
|
||||
if (!isNullOrBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
FeedReader(bot, sender, this@with).run()
|
||||
FeedReader(this@with, event).run()
|
||||
}
|
||||
}
|
||||
} 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
|
||||
|
||||
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.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Cycle(bot: Mobibot) : AbstractCommand(bot) {
|
||||
@Suppress("MagicNumber")
|
||||
class Cycle : AbstractCommand() {
|
||||
private val wait = 10
|
||||
override val name = "cycle"
|
||||
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 isVisible = true
|
||||
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
with(bot) {
|
||||
if (isOp) {
|
||||
send("$sender has just asked me to leave. I'll be back!")
|
||||
sleep(wait)
|
||||
partChannel(channel)
|
||||
sleep(wait)
|
||||
joinChannel(channel)
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
runBlocking {
|
||||
sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!")
|
||||
userChannelDao.getChannel(channel).send().part()
|
||||
delay(wait * 1000L)
|
||||
sendIRC().joinChannel(channel)
|
||||
}
|
||||
} 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
|
||||
|
||||
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 help = emptyList<String>()
|
||||
override val isOp = true
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = false
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
bot.send("$sender has just signed my death sentence.")
|
||||
bot.shutdown(sender)
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
if (isChannelOp(channel, event) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) {
|
||||
sendIRC().message(channel, "${event.user?.nick} has just signed my death sentence.")
|
||||
stopBotReconnect()
|
||||
sendIRC().quitServer("The Bot is Out There!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Max days property.
|
||||
*/
|
||||
const val DIE_PROP = "die"
|
||||
}
|
||||
|
||||
init {
|
||||
initProperties(DIE_PROP)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,13 +32,17 @@
|
|||
|
||||
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.bot
|
||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||
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 org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Ignore(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Ignore : AbstractCommand() {
|
||||
private val me = "me"
|
||||
|
||||
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> ...]"))
|
||||
)
|
||||
|
||||
override val isOp = false
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
|
@ -73,56 +77,45 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) {
|
|||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val isMe = args.trim().equals(me, true)
|
||||
if (isMe || !isOp) {
|
||||
val nick = sender.lowercase()
|
||||
ignoreNick(bot, nick, isMe, isPrivate)
|
||||
if (isMe || !isChannelOp(channel, event)) {
|
||||
val nick = event.user.nick.lowercase()
|
||||
ignoreNick(nick, isMe, event)
|
||||
} else {
|
||||
ignoreOp(bot, sender, args, isPrivate)
|
||||
ignoreOp(args, event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(
|
||||
command: String,
|
||||
sender: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
): Boolean {
|
||||
return if (isOp) {
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
return if (isChannelOp(channel, event)) {
|
||||
for (h in helpOp) {
|
||||
bot.send(sender, buildCmdSyntax(h, bot.nick, isPrivate), isPrivate)
|
||||
event.sendMessage(buildCmdSyntax(h, event.bot().nick, true))
|
||||
}
|
||||
true
|
||||
} 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 (ignored.remove(sender)) {
|
||||
bot.send(sender, "You are no longer ignored.", isPrivate)
|
||||
event.sendMessage("You are no longer ignored.")
|
||||
} else {
|
||||
ignored.add(sender)
|
||||
bot.send(sender, "You are now ignored.", isPrivate)
|
||||
event.sendMessage("You are now ignored.")
|
||||
}
|
||||
} else {
|
||||
if (ignored.contains(sender)) {
|
||||
bot.send(sender, "You are currently ignored.", isPrivate)
|
||||
event.sendMessage("You are currently ignored.")
|
||||
} 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()) {
|
||||
val nicks = args.lowercase().split(" ")
|
||||
for (nick in nicks) {
|
||||
|
@ -138,11 +131,10 @@ class Ignore(bot: Mobibot) : AbstractCommand(bot) {
|
|||
}
|
||||
|
||||
if (ignored.size > 0) {
|
||||
bot.send(sender, "The following nicks are ignored:", isPrivate)
|
||||
@Suppress("MagicNumber")
|
||||
bot.sendList(sender, ignored.sorted(), 8, isPrivate = isPrivate, isIndent = true)
|
||||
event.sendMessage("The following nicks are ignored:")
|
||||
event.sendList(ignored.sorted(), 8, isIndent = true)
|
||||
} 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
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
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.commands.links.LinksMgr
|
||||
import net.thauvin.erik.mobibot.commands.tell.Tell
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.lang.management.ManagementFactory
|
||||
|
||||
class Info(bot: Mobibot?) : AbstractCommand(bot!!) {
|
||||
class Info(private val tell: Tell) : AbstractCommand() {
|
||||
private val allVersions = listOf(
|
||||
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${green(ReleaseInfo.WEBSITE)})",
|
||||
"Written by ${ReleaseInfo.AUTHOR} (${green(ReleaseInfo.AUTHOR_URL)})"
|
||||
)
|
||||
override val name = "info"
|
||||
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 isVisible = true
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
with(bot) {
|
||||
sendList(sender, allVersions, 1, isPrivate = isPrivate)
|
||||
val info = StringBuilder()
|
||||
info.append("Uptime: ")
|
||||
.append(uptime(ManagementFactory.getRuntimeMXBean().uptime))
|
||||
.append(" [Entries: ")
|
||||
.append(LinksMgr.entries.size)
|
||||
if (isOp) {
|
||||
if (tell.isEnabled()) {
|
||||
info.append(", Messages: ").append(tell.size())
|
||||
}
|
||||
if (twitter.isAutoPost) {
|
||||
info.append(", Twitter: ").append(twitter.entriesCount())
|
||||
}
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
event.sendList(allVersions, 1)
|
||||
val info = StringBuilder()
|
||||
info.append("Uptime: ")
|
||||
.append(uptime(ManagementFactory.getRuntimeMXBean().uptime))
|
||||
.append(" [Entries: ")
|
||||
.append(LinksMgr.entries.links.size)
|
||||
if (isChannelOp(channel, event)) {
|
||||
if (tell.isEnabled()) {
|
||||
info.append(", Messages: ").append(tell.size())
|
||||
}
|
||||
if (LinksMgr.twitter.isAutoPost) {
|
||||
info.append(", Twitter: ").append(LinksMgr.twitter.entriesCount())
|
||||
}
|
||||
info.append(", Recap: ").append(Recap.recaps.size).append(']')
|
||||
send(sender, info.toString(), isPrivate)
|
||||
}
|
||||
info.append(", Recap: ").append(Recap.recaps.size).append(']')
|
||||
event.sendMessage(info.toString())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,27 +32,21 @@
|
|||
|
||||
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.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Me(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Me : AbstractCommand() {
|
||||
override val name = "me"
|
||||
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 isVisible = true
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
bot.action(args)
|
||||
} else {
|
||||
bot.helpDefault(sender, isOp, isPrivate)
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.bot().sendIRC().action(channel, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,35 +32,29 @@
|
|||
|
||||
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.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 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 isVisible = true
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
with(bot) {
|
||||
if (isOp) {
|
||||
if (modulesNames.isEmpty()) {
|
||||
send(sender, "There are no enabled modules.", isPrivate)
|
||||
} else {
|
||||
send(sender, "The enabled modules are: ", isPrivate)
|
||||
@Suppress("MagicNumber")
|
||||
sendList(sender, modulesNames, 7, isPrivate = isPrivate, isIndent = true)
|
||||
}
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
if (modulesNames.isEmpty()) {
|
||||
event.respondPrivateMessage("There are no enabled modules.")
|
||||
} else {
|
||||
helpDefault(sender, isOp, isPrivate)
|
||||
event.respondPrivateMessage("The enabled modules are: ")
|
||||
event.sendList(modulesNames, 7, isIndent = true)
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,35 +32,30 @@
|
|||
|
||||
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.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Msg(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Msg : AbstractCommand() {
|
||||
override val name = "msg"
|
||||
override val help = listOf(
|
||||
"To have the bot send a private message to someone:",
|
||||
helpFormat("%c $name <nick> <text>")
|
||||
)
|
||||
override val isOp = true
|
||||
override val isPublic = true
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
val msg = args.split(" ", limit = 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 {
|
||||
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
|
||||
|
||||
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.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Nick(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Nick : AbstractCommand() {
|
||||
override val name = "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 isVisible = true
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
bot.changeNick(args)
|
||||
} else {
|
||||
bot.helpDefault(sender, isOp, isPrivate)
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.bot().sendIRC().changeNick(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,19 +32,20 @@
|
|||
|
||||
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.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.time.Clock
|
||||
import java.time.LocalDateTime
|
||||
|
||||
class Recap(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Recap : AbstractCommand() {
|
||||
override val name = "recap"
|
||||
override val help = listOf(
|
||||
"To list the last 10 public channel messages:",
|
||||
helpFormat("%c $name")
|
||||
)
|
||||
override val isOp = false
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
|
@ -61,26 +62,19 @@ class Recap(bot: Mobibot) : AbstractCommand(bot) {
|
|||
LocalDateTime.now(Clock.systemUTC()).toUtcDateTime()
|
||||
+ " - $sender" + (if (isAction) " " else ": ") + message
|
||||
)
|
||||
@Suppress("MagicNumber")
|
||||
if (recaps.size > 10) {
|
||||
recaps.removeFirst()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (recaps.isNotEmpty()) {
|
||||
for (r in recaps) {
|
||||
bot.send(sender, r, isPrivate)
|
||||
event.sendMessage(r)
|
||||
}
|
||||
} 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
|
||||
|
||||
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.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Say(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Say : AbstractCommand() {
|
||||
override val name = "say"
|
||||
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 isVisible = true
|
||||
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
bot.send(args)
|
||||
} else {
|
||||
bot.helpDefault(sender, isOp, isPrivate)
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.bot().sendIRC().message(channel, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,36 +32,29 @@
|
|||
|
||||
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.sendList
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Users(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Users : AbstractCommand() {
|
||||
override val name = "users"
|
||||
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 isVisible = true
|
||||
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val nicks = mutableListOf<String>()
|
||||
with(bot) {
|
||||
getUsers(channel).forEach { user ->
|
||||
if (isOp(user.nick)) {
|
||||
nicks.add("@${user.nick}")
|
||||
} else {
|
||||
nicks.add(user.nick)
|
||||
}
|
||||
val ch = event.bot().userChannelDao.getChannel(channel)
|
||||
ch.users.forEach {
|
||||
if (it.channelsOpIn.contains(ch)) {
|
||||
nicks.add("@${it.nick}")
|
||||
} else {
|
||||
nicks.add(it.nick)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
sendList(sender, nicks.sorted(), 8, isPrivate = isPrivate, isIndent = true)
|
||||
}
|
||||
event.sendList(nicks, 8)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,12 +31,14 @@
|
|||
*/
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||
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 org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Versions(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Versions : AbstractCommand() {
|
||||
private val allVersions = listOf(
|
||||
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})",
|
||||
"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 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 isVisible = true
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
bot.sendList(sender, allVersions, 1, isPrivate = isPrivate)
|
||||
} else {
|
||||
bot.helpDefault(sender, false, isPrivate)
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.sendList(allVersions, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,14 +33,16 @@
|
|||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
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.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.entries.EntriesUtils
|
||||
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 help = listOf(
|
||||
"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: ",
|
||||
helpFormat("${Constants.LINK_CMD}1.1:-")
|
||||
)
|
||||
override val isOp = false
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
|
@ -59,29 +61,22 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
|||
const val COMMAND = "comment"
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
@Suppress("MagicNumber")
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
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) {
|
||||
val entry: EntryLink = LinksMgr.entries[index]
|
||||
if (entryIndex < LinksMgr.entries.links.size && LinksMgr.isUpToDate(event)) {
|
||||
val entry: EntryLink = LinksMgr.entries.links[entryIndex]
|
||||
val commentIndex = cmds[1].toInt() - 1
|
||||
if (commentIndex < entry.comments.size) {
|
||||
when (val cmd = cmds[2].trim()) {
|
||||
"" -> showComment(bot, entry, index, commentIndex) // L1.1:
|
||||
"-" -> deleteComment(bot, sender, isOp, entry, index, commentIndex) // L11:-
|
||||
"" -> showComment(entry, entryIndex, commentIndex, event) // L1.1:
|
||||
"-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:-
|
||||
else -> {
|
||||
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>
|
||||
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(
|
||||
command: String,
|
||||
sender: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
): 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
|
||||
)
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
if (super.helpResponse(channel, topic, event)) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.sendMessage("To change a comment's author:")
|
||||
event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?<nick>"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -114,49 +100,52 @@ class Comment(bot: Mobibot) : AbstractCommand(bot) {
|
|||
}
|
||||
|
||||
private fun changeAuthor(
|
||||
bot: Mobibot,
|
||||
channel: String,
|
||||
cmd: String,
|
||||
sender: String,
|
||||
isOp: Boolean,
|
||||
entry: EntryLink,
|
||||
index: Int,
|
||||
commentIndex: Int
|
||||
entryIndex: Int,
|
||||
commentIndex: Int,
|
||||
event: GenericMessageEvent
|
||||
) {
|
||||
if (isOp && cmd.length > 1) {
|
||||
if (isChannelOp(channel, event) && cmd.length > 1) {
|
||||
val comment = entry.getComment(commentIndex)
|
||||
comment.nick = cmd.substring(1)
|
||||
bot.send(EntriesUtils.buildComment(index, commentIndex, comment))
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment))
|
||||
LinksMgr.entries.save()
|
||||
} 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(
|
||||
bot: Mobibot,
|
||||
sender: String,
|
||||
isOp: Boolean,
|
||||
channel: String,
|
||||
entry: EntryLink,
|
||||
index: Int,
|
||||
commentIndex: Int
|
||||
entryIndex: 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)
|
||||
bot.send("Comment ${EntriesUtils.buildLinkCmd(index)}.${commentIndex + 1} removed.")
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
event.sendMessage("Comment ${EntriesUtils.buildLinkLabel(entryIndex)}.${commentIndex + 1} removed.")
|
||||
LinksMgr.entries.save()
|
||||
} 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) {
|
||||
entry.setComment(commentIndex, cmd, sender)
|
||||
val comment = entry.getComment(commentIndex)
|
||||
bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false)
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
private fun setComment(
|
||||
cmd: String,
|
||||
entry: EntryLink,
|
||||
entryIndex: Int,
|
||||
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) {
|
||||
bot.send(EntriesUtils.buildComment(index, commentIndex, entry.getComment(commentIndex)))
|
||||
private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) {
|
||||
event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, entry.getComment(commentIndex)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,25 +33,29 @@
|
|||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
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.bot
|
||||
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.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.Ignore
|
||||
import net.thauvin.erik.mobibot.entries.EntriesMgr
|
||||
import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored
|
||||
import net.thauvin.erik.mobibot.entries.Entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import net.thauvin.erik.mobibot.modules.Twitter
|
||||
import org.jsoup.Jsoup
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.io.IOException
|
||||
|
||||
class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
||||
private val keywords: MutableList<String> = mutableListOf()
|
||||
class LinksMgr : AbstractCommand() {
|
||||
private val defaultTags: MutableList<String> = mutableListOf()
|
||||
private val keywords: MutableList<String> = mutableListOf()
|
||||
|
||||
override val name = Constants.LINK_CMD
|
||||
override val help = emptyList<String>()
|
||||
override val isOp = false
|
||||
override val isOpOnly = false
|
||||
override val isPublic = false
|
||||
override val isVisible = false
|
||||
|
||||
|
@ -65,52 +69,35 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
|||
const val TAGS_PROP = "tags"
|
||||
const val TAG_MATCH = ", *| +"
|
||||
|
||||
// Entries array
|
||||
@JvmField
|
||||
val entries = mutableListOf<EntryLink>()
|
||||
/** Entries array **/
|
||||
val entries = Entries()
|
||||
|
||||
// History/backlogs array
|
||||
@JvmField
|
||||
val history = mutableListOf<String>()
|
||||
/** Pinboard handler. **/
|
||||
val pinboard = Pinboard()
|
||||
|
||||
/** Twitter handler. **/
|
||||
val twitter = Twitter()
|
||||
|
||||
/** Let the user know if the entries are too old to be modified. **/
|
||||
@JvmStatic
|
||||
var startDate: String = today()
|
||||
private set
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun startup(current: String, backlogs: String, channel: String) {
|
||||
startDate = EntriesMgr.loadEntries(current, channel, entries)
|
||||
if (today() != startDate) {
|
||||
entries.clear()
|
||||
startDate = today()
|
||||
fun isUpToDate(event: GenericMessageEvent): Boolean {
|
||||
if (entries.lastPubDate != today()) {
|
||||
event.sendMessage("The links are too old to be updated.")
|
||||
return false
|
||||
}
|
||||
EntriesMgr.loadBacklogs(backlogs, history)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
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()
|
||||
if (!isDupEntry(bot, sender, link, isPrivate)) {
|
||||
val isBackup = saveDayBackup(bot)
|
||||
if (!isDupEntry(link, event)) {
|
||||
var title = ""
|
||||
val tags = ArrayList<String>(defaultTags)
|
||||
if (cmds.size == 2) {
|
||||
|
@ -129,37 +116,32 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
|||
matchTagKeywords(title, tags)
|
||||
}
|
||||
|
||||
entries.add(EntryLink(link, title, sender, login, bot.channel, tags))
|
||||
val index: Int = entries.size - 1
|
||||
val entry: EntryLink = entries[index]
|
||||
bot.send(EntriesUtils.buildLink(index, entry))
|
||||
// Links are old, clear them
|
||||
if (entries.lastPubDate != today()) {
|
||||
entries.links.clear()
|
||||
}
|
||||
|
||||
// Add Entry to pinboard.
|
||||
bot.addPin(entry)
|
||||
val entry = EntryLink(link, title, sender, login, channel, tags)
|
||||
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.
|
||||
bot.twitter.queueEntry(index)
|
||||
twitter.queueEntry(index)
|
||||
|
||||
saveEntries(bot, isBackup)
|
||||
entries.save()
|
||||
|
||||
if (Constants.NO_TITLE == entry.title) {
|
||||
bot.send(sender, "Please specify a title, by typing:", isPrivate)
|
||||
bot.send(
|
||||
sender,
|
||||
helpFormat("${EntriesUtils.buildLinkCmd(index)}:|This is the title"),
|
||||
isPrivate
|
||||
)
|
||||
event.sendMessage("Please specify a title, by typing:")
|
||||
event.sendMessage(helpFormat("${EntriesUtils.buildLinkLabel(index)}:|This is the title"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(
|
||||
command: String,
|
||||
sender: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
): Boolean = false
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches(LINK_MATCH.toRegex())
|
||||
|
@ -180,12 +162,12 @@ class LinksMgr(bot: Mobibot) : AbstractCommand(bot) {
|
|||
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) {
|
||||
for (i in entries.indices) {
|
||||
if (link == entries[i].link) {
|
||||
val entry: EntryLink = entries[i]
|
||||
bot.send(sender, bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry), isPrivate)
|
||||
for (i in entries.links.indices) {
|
||||
if (link == entries.links[i].link) {
|
||||
val entry: EntryLink = entries.links[i]
|
||||
event.sendMessage(bold("Duplicate") + " >> " + EntriesUtils.buildLink(i, entry))
|
||||
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) {
|
||||
super.setProperty(key, value)
|
||||
if (KEYWORDS_PROP == key) {
|
||||
|
|
|
@ -33,15 +33,18 @@
|
|||
package net.thauvin.erik.mobibot.commands.links
|
||||
|
||||
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.bot
|
||||
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.links.LinksMgr.Companion.entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
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 help = listOf(
|
||||
"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: ",
|
||||
helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}")
|
||||
)
|
||||
override val isOp = false
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.substring(1).split(":", limit = 2)
|
||||
val index = cmds[0].toInt() - 1
|
||||
val entryIndex = cmds[0].toInt() - 1
|
||||
|
||||
if (index < entries.size) {
|
||||
when (val cmd = cmds[1].trim()) {
|
||||
"" -> showEntry(index)
|
||||
"-" -> removeEntry(sender, login, isOp, index) // L1:-
|
||||
else -> {
|
||||
if (entryIndex < entries.links.size) {
|
||||
val cmd = cmds[1].trim()
|
||||
if (cmd.isBlank()) {
|
||||
showEntry(entryIndex, event) // L1:
|
||||
} else if (LinksMgr.isUpToDate(event)) {
|
||||
if (cmd == "-") {
|
||||
removeEntry(channel, entryIndex, event) // L1:-
|
||||
} else {
|
||||
when (cmd[0]) {
|
||||
'|' -> changeTitle(cmd, index) // L1:|<title>
|
||||
'=' -> changeUrl(cmd, login, isOp, index) // L1:=<url>
|
||||
'?' -> changeAuthor(cmd, sender, isOp, index) // L1:?<author>
|
||||
else -> addComment(cmd, sender, index) // L1:<comment>
|
||||
'|' -> changeTitle(cmd, entryIndex, event) // L1:|<title>
|
||||
'=' -> changeUrl(channel, cmd, entryIndex, event) // L1:=<url>
|
||||
'?' -> changeAuthor(channel, cmd, entryIndex, event) // L1:?<author>
|
||||
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())
|
||||
}
|
||||
|
||||
private fun addComment(cmd: String, sender: String, index: Int) {
|
||||
val entry: EntryLink = entries[index]
|
||||
val commentIndex = entry.addComment(cmd, sender)
|
||||
private fun addComment(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
val commentIndex = entry.addComment(cmd, event.user.nick)
|
||||
val comment = entry.getComment(commentIndex)
|
||||
bot.send(sender, EntriesUtils.buildComment(index, commentIndex, comment), false)
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment))
|
||||
entries.save()
|
||||
}
|
||||
|
||||
private fun changeTitle(cmd: String, index: Int) {
|
||||
private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
if (cmd.length > 1) {
|
||||
val entry: EntryLink = entries[index]
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
entry.title = cmd.substring(1).trim()
|
||||
bot.updatePin(entry.link, entry)
|
||||
bot.send(EntriesUtils.buildLink(index, entry))
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.buildLink(entryIndex, entry))
|
||||
entries.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeUrl(cmd: String, login: String, isOp: Boolean, index: Int) {
|
||||
val entry: EntryLink = entries[index]
|
||||
if (entry.login == login || isOp) {
|
||||
private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||
val link = cmd.substring(1)
|
||||
if (link.matches(LinksMgr.LINK_MATCH.toRegex())) {
|
||||
val oldLink = entry.link
|
||||
entry.link = link
|
||||
bot.updatePin(oldLink, entry)
|
||||
bot.send(EntriesUtils.buildLink(index, entry))
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
LinksMgr.pinboard.updatePin(event.bot().serverHostname, oldLink, entry)
|
||||
event.sendMessage(EntriesUtils.buildLink(entryIndex, entry))
|
||||
entries.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeAuthor(cmd: String, sender: String, isOp: Boolean, index: Int) {
|
||||
if (isOp) {
|
||||
private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
if (cmd.length > 1) {
|
||||
val entry: EntryLink = entries[index]
|
||||
val entry: EntryLink = entries.links[index]
|
||||
entry.nick = cmd.substring(1)
|
||||
bot.send(EntriesUtils.buildLink(index, entry))
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.buildLink(index, entry))
|
||||
entries.save()
|
||||
}
|
||||
} 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) {
|
||||
val entry: EntryLink = entries[index]
|
||||
if (entry.login == login || isOp) {
|
||||
bot.deletePin(index, entry)
|
||||
entries.removeAt(index)
|
||||
bot.send("Entry ${EntriesUtils.buildLinkCmd(index)} removed.")
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[index]
|
||||
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||
LinksMgr.pinboard.deletePin(entry)
|
||||
LinksMgr.twitter.removeEntry(index)
|
||||
entries.links.removeAt(index)
|
||||
event.sendMessage("Entry ${EntriesUtils.buildLinkLabel(index)} removed.")
|
||||
entries.save()
|
||||
} 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) {
|
||||
val entry: EntryLink = entries[index]
|
||||
bot.send(EntriesUtils.buildLink(index, entry))
|
||||
private fun showEntry(index: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[index]
|
||||
event.sendMessage(EntriesUtils.buildLink(index, entry))
|
||||
if (entry.tags.isNotEmpty()) {
|
||||
bot.send(EntriesUtils.buildTags(index, entry))
|
||||
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||
}
|
||||
if (entry.comments.isNotEmpty()) {
|
||||
val comments = entry.comments
|
||||
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
|
||||
|
||||
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.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
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 help = listOf(
|
||||
"To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:",
|
||||
helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]")
|
||||
)
|
||||
override val isOp = false
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
|
@ -53,33 +56,27 @@ class Tags(bot: Mobibot) : AbstractCommand(bot) {
|
|||
const val COMMAND = "tags"
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2)
|
||||
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 entry: EntryLink = LinksMgr.entries[index]
|
||||
val entry: EntryLink = LinksMgr.entries.links[index]
|
||||
if (cmd.isNotEmpty()) {
|
||||
if (entry.login == login || isOp) {
|
||||
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||
entry.setTags(cmd)
|
||||
bot.updatePin(entry.link, entry)
|
||||
bot.send(EntriesUtils.buildTags(index, entry))
|
||||
LinksMgr.saveEntries(bot, false)
|
||||
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||
LinksMgr.entries.save()
|
||||
} 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 {
|
||||
if (entry.tags.isNotEmpty()) {
|
||||
bot.send(EntriesUtils.buildTags(index, entry))
|
||||
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||
} 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
|
||||
|
||||
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.buildCmdSyntax
|
||||
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.links.LinksMgr.Companion.entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
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) {
|
||||
@Suppress("MagicNumber")
|
||||
private val maxEntries = 8
|
||||
class View : AbstractCommand() {
|
||||
private val maxEntries = 6
|
||||
override val name = VIEW_CMD
|
||||
override val help = listOf(
|
||||
"To list or search the current URL posts:",
|
||||
helpFormat("%c $name [<start>] [<query>]")
|
||||
)
|
||||
override val isOp = false
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
|
@ -56,22 +58,16 @@ class View(bot: Mobibot) : AbstractCommand(bot) {
|
|||
const val VIEW_CMD = "view"
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (entries.size != 0) {
|
||||
showPosts(bot, args, sender)
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (entries.links.isNotEmpty()) {
|
||||
showPosts(args, event)
|
||||
} 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) {
|
||||
val max = entries.size
|
||||
private fun showPosts(args: String, event: GenericMessageEvent) {
|
||||
val max = entries.links.size
|
||||
var lcArgs = args.lowercase()
|
||||
var i = 0
|
||||
if (lcArgs.isEmpty() && max > maxEntries) {
|
||||
|
@ -97,25 +93,32 @@ class View(bot: Mobibot) : AbstractCommand(bot) {
|
|||
var entry: EntryLink
|
||||
var sent = 0
|
||||
while (i < max && sent < maxEntries) {
|
||||
entry = entries[i]
|
||||
entry = entries.links[i]
|
||||
if (lcArgs.isNotBlank()) {
|
||||
if (entry.matches(lcArgs)) {
|
||||
bot.send(sender, EntriesUtils.buildLink(i, entry, true), false)
|
||||
event.sendMessage(EntriesUtils.buildLink(i, entry, true))
|
||||
sent++
|
||||
}
|
||||
} else {
|
||||
bot.send(sender, EntriesUtils.buildLink(i, entry, true), false)
|
||||
event.sendMessage(EntriesUtils.buildLink(i, entry, true))
|
||||
sent++
|
||||
}
|
||||
i++
|
||||
if (sent == maxEntries && i < max) {
|
||||
bot.send(
|
||||
sender, "To view more, try: " + bold("${bot.nick}: $name ${i + 1} $lcArgs"), false
|
||||
event.sendMessage("To view more, try: ")
|
||||
event.sendMessage(
|
||||
helpFormat(
|
||||
buildCmdSyntax(
|
||||
"%c $name ${i + 1} $lcArgs",
|
||||
event.bot().nick,
|
||||
event is PrivateMessageEvent
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
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.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.plural
|
||||
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.toUtcDateTime
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
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.
|
||||
*/
|
||||
class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
||||
class Tell(private val serialObject: String) : AbstractCommand() {
|
||||
// Messages queue
|
||||
private val messages: MutableList<TellMessage> = mutableListOf()
|
||||
|
||||
// Serialized object file
|
||||
private val serializedObject: String
|
||||
|
||||
// Maximum number of days to keep messages
|
||||
@Suppress("MagicNumber")
|
||||
private var maxDays = 7
|
||||
|
||||
// Message maximum queue size
|
||||
@Suppress("MagicNumber")
|
||||
private var maxSize = 50
|
||||
|
||||
/**
|
||||
* Cleans the messages queue.
|
||||
*/
|
||||
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())
|
||||
}
|
||||
|
||||
// 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(" ")
|
||||
if (split.size == 2) {
|
||||
val id = split[1]
|
||||
var deleted = false
|
||||
if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) {
|
||||
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)
|
||||
deleted = true
|
||||
}
|
||||
}
|
||||
if (deleted) {
|
||||
save()
|
||||
bot.send(sender, "Delivered messages have been deleted.", isPrivate)
|
||||
event.sendMessage("Delivered messages have been deleted.")
|
||||
} else {
|
||||
bot.send(sender, "No delivered messages were found.", isPrivate)
|
||||
event.sendMessage("No delivered messages were found.")
|
||||
}
|
||||
} else {
|
||||
var found = false
|
||||
for (message in messages) {
|
||||
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)
|
||||
save()
|
||||
bot.send(sender, "Your message was deleted from the queue.", isPrivate)
|
||||
event.sendMessage("Your message was deleted from the queue.")
|
||||
deleted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!deleted) {
|
||||
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 {
|
||||
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 {
|
||||
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}"),
|
||||
"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 isVisible: Boolean = isEnabled()
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isEnabled()) {
|
||||
if (args.isBlank()) {
|
||||
helpResponse(args, sender, isOp, isPrivate)
|
||||
helpResponse(channel, args, event)
|
||||
} else if (args.startsWith(View.VIEW_CMD)) {
|
||||
if (bot.isOp(sender) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) {
|
||||
viewAll(sender, isPrivate)
|
||||
if (isChannelOp(channel, event) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) {
|
||||
viewAll(event)
|
||||
} else {
|
||||
viewMessages(sender, isPrivate)
|
||||
viewMessages(event)
|
||||
}
|
||||
} else if (args.startsWith("$TELL_DEL_KEYWORD ")) {
|
||||
deleteMessage(sender, args, isOp, isPrivate)
|
||||
deleteMessage(channel, args, event)
|
||||
} else {
|
||||
newMessage(sender, args, isOp, isPrivate)
|
||||
newMessage(channel, args, event)
|
||||
}
|
||||
if (clean()) {
|
||||
save()
|
||||
|
@ -169,21 +167,19 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) {
|
||||
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)
|
||||
save()
|
||||
bot.send(
|
||||
sender, "Message [ID ${message.id}] was queued for ${bold(message.recipient)}", isPrivate
|
||||
)
|
||||
event.sendMessage("Message [ID ${message.id}] was queued for ${bold(message.recipient)}")
|
||||
} else {
|
||||
bot.send(sender, "Sorry, the messages queue is currently full.", isPrivate)
|
||||
event.sendMessage("Sorry, the messages queue is currently full.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(args, sender, isOp, isPrivate)
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,34 +187,30 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
|||
* Saves the messages queue.
|
||||
*/
|
||||
private fun save() {
|
||||
TellMessagesMgr.save(serializedObject, messages, bot.logger)
|
||||
TellMessagesMgr.save(serialObject, messages)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends messages.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun send(nickname: String, isMessage: Boolean = false) {
|
||||
if (isEnabled() && nickname != bot.nick) {
|
||||
fun send(event: GenericUserEvent) {
|
||||
val nickname = event.user.nick
|
||||
if (isEnabled() && nickname != event.getBot<PircBotX>().nick) {
|
||||
messages.stream().filter { message: TellMessage -> message.isMatch(nickname) }
|
||||
.forEach { message: TellMessage ->
|
||||
if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) {
|
||||
if (message.sender == nickname) {
|
||||
if (!isMessage) {
|
||||
bot.send(
|
||||
nickname,
|
||||
"${bold("You")} wanted me to remind you: ${reverseColor(message.message)}",
|
||||
true
|
||||
if (event !is MessageEvent) {
|
||||
event.user.send().message(
|
||||
"${bold("You")} wanted me to remind you: ${reverseColor(message.message)}"
|
||||
)
|
||||
message.isReceived = true
|
||||
message.isNotified = true
|
||||
save()
|
||||
}
|
||||
} else {
|
||||
bot.send(
|
||||
nickname,
|
||||
"${message.sender} wanted me to tell you: ${reverseColor(message.message)}",
|
||||
true
|
||||
event.user.send().message(
|
||||
"${message.sender} wanted me to tell you: ${reverseColor(message.message)}"
|
||||
)
|
||||
message.isReceived = true
|
||||
save()
|
||||
|
@ -226,11 +218,9 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
|||
} else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived
|
||||
&& !message.isNotified
|
||||
) {
|
||||
bot.send(
|
||||
nickname,
|
||||
"Your message ${reverseColor("[ID " + message.id + ']')} was sent to "
|
||||
+ "${bold(message.recipient)} on ${message.receptionDate.toUtcDateTime()}",
|
||||
true
|
||||
event.user.send().message(
|
||||
"Your message ${reverseColor("[ID ${message.id}]")} was sent to "
|
||||
+ "${bold(message.recipient)} on ${message.receptionDate}"
|
||||
)
|
||||
message.isNotified = true
|
||||
save()
|
||||
|
@ -247,66 +237,55 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
|||
fun size(): Int = messages.size
|
||||
|
||||
// View all messages.
|
||||
private fun viewAll(sender: String, isPrivate: Boolean) {
|
||||
private fun viewAll(event: GenericMessageEvent) {
|
||||
if (messages.isNotEmpty()) {
|
||||
for (message in messages) {
|
||||
bot.send(
|
||||
sender, bold(message.sender) + ARROW + bold(message.recipient)
|
||||
+ " [ID: " + message.id + ", "
|
||||
+ (if (message.isReceived) "DELIVERED" else "QUEUED") + ']',
|
||||
isPrivate
|
||||
event.sendMessage(
|
||||
"${bold(message.sender)}$ARROW${bold(message.recipient)} [ID: ${message.id}, " +
|
||||
(if (message.isReceived) "DELIVERED]" else "QUEUED]")
|
||||
)
|
||||
}
|
||||
} else {
|
||||
bot.send(sender, "There are no messages in the queue.", isPrivate)
|
||||
event.sendMessage("There are no messages in the queue.")
|
||||
}
|
||||
}
|
||||
|
||||
// View messages.
|
||||
private fun viewMessages(sender: String, isPrivate: Boolean) {
|
||||
private fun viewMessages(event: GenericMessageEvent) {
|
||||
var hasMessage = false
|
||||
for (message in messages) {
|
||||
if (message.isMatch(sender)) {
|
||||
if (message.isMatch(event.user.nick)) {
|
||||
if (!hasMessage) {
|
||||
hasMessage = true
|
||||
bot.send(sender, "Here are your messages: ", isPrivate)
|
||||
hasMessage = true; event.sendMessage("Here are your messages: ")
|
||||
}
|
||||
if (message.isReceived) {
|
||||
bot.send(
|
||||
sender,
|
||||
bold(message.sender) + ARROW + bold(message.recipient)
|
||||
+ " [${message.receptionDate.toUtcDateTime()}, ID: "
|
||||
+ bold(message.id) + ", DELIVERED]",
|
||||
isPrivate
|
||||
event.sendMessage(
|
||||
bold(message.sender) + ARROW + bold(message.recipient) +
|
||||
" [${message.receptionDate.toUtcDateTime()}, ID: ${bold(message.id)}, DELIVERED]"
|
||||
)
|
||||
} else {
|
||||
bot.send(
|
||||
sender,
|
||||
bold(message.sender) + ARROW + bold(message.recipient)
|
||||
+ " [${message.queued.toUtcDateTime()}, ID: "
|
||||
+ bold(message.id) + ", QUEUED]",
|
||||
isPrivate
|
||||
event.sendMessage(
|
||||
bold(message.sender) + ARROW + bold(message.recipient) +
|
||||
" [${message.queued.toUtcDateTime()}, ID: ${bold(message.id)}, QUEUED]"
|
||||
)
|
||||
}
|
||||
bot.send(sender, helpFormat(message.message), isPrivate)
|
||||
event.sendMessage(helpFormat(message.message))
|
||||
}
|
||||
}
|
||||
if (!hasMessage) {
|
||||
bot.send(sender, "You have no messages in the queue.", isPrivate)
|
||||
event.sendMessage("You have no messages in the queue.")
|
||||
} else {
|
||||
bot.send(sender, "To delete one or all delivered messages:", isPrivate)
|
||||
bot.send(
|
||||
sender,
|
||||
event.sendMessage("To delete one or all delivered messages:")
|
||||
event.sendMessage(
|
||||
helpFormat(
|
||||
buildCmdSyntax(
|
||||
"%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>",
|
||||
bot.nick,
|
||||
isPrivate
|
||||
event.user.nick,
|
||||
true
|
||||
)
|
||||
),
|
||||
isPrivate
|
||||
)
|
||||
)
|
||||
bot.send(sender, help.last(), isPrivate)
|
||||
event.sendMessage(help.last())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,9 +303,6 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
|||
// Arrow
|
||||
private const val ARROW = " --> "
|
||||
|
||||
// Serialized object file extension
|
||||
private const val SER_EXT = ".ser"
|
||||
|
||||
// All keyword
|
||||
private const val TELL_ALL_KEYWORD = "all"
|
||||
|
||||
|
@ -341,8 +317,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
|
|||
initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP)
|
||||
|
||||
// Load the message queue
|
||||
serializedObject = bot.logsDir + bot.name + SER_EXT
|
||||
messages.addAll(TellMessagesMgr.load(serializedObject, bot.logger))
|
||||
messages.addAll(TellMessagesMgr.load(serialObject))
|
||||
if (clean()) {
|
||||
save()
|
||||
}
|
||||
|
|
|
@ -66,12 +66,12 @@ class TellMessage internal constructor(
|
|||
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
|
||||
|
||||
/**
|
||||
* Returns {@code true) if the message was received.
|
||||
* Returns {@code true} if the message was received.
|
||||
*/
|
||||
var isReceived = false
|
||||
set(value) {
|
||||
|
|
|
@ -31,10 +31,10 @@
|
|||
*/
|
||||
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.BufferedOutputStream
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
|
@ -42,11 +42,14 @@ import java.nio.file.Files
|
|||
import java.nio.file.Paths
|
||||
import java.time.Clock
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.io.path.exists
|
||||
|
||||
/**
|
||||
* The Tell Messages Manager.
|
||||
*/
|
||||
object TellMessagesMgr {
|
||||
val logger: Logger = LoggerFactory.getLogger(TellMessagesMgr::class.java)
|
||||
|
||||
/**
|
||||
* Cleans the messages queue.
|
||||
*/
|
||||
|
@ -58,22 +61,22 @@ object TellMessagesMgr {
|
|||
/**
|
||||
* Loads the messages.
|
||||
*/
|
||||
|
||||
fun load(file: String, logger: Logger): List<TellMessage> {
|
||||
try {
|
||||
ObjectInputStream(
|
||||
BufferedInputStream(Files.newInputStream(Paths.get(file)))
|
||||
).use { input ->
|
||||
if (logger.isDebugEnabled) logger.debug("Loading the messages.")
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return input.readObject() as List<TellMessage>
|
||||
fun load(file: String): List<TellMessage> {
|
||||
val serialFile = Paths.get(file)
|
||||
if (serialFile.exists()) {
|
||||
try {
|
||||
ObjectInputStream(
|
||||
BufferedInputStream(Files.newInputStream(serialFile))
|
||||
).use { input ->
|
||||
if (logger.isDebugEnabled) logger.debug("Loading the messages.")
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return input.readObject() as List<TellMessage>
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
logger.error("An IO error occurred loading the messages queue.", e)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
logger.error("An error occurred loading the messages queue.", e)
|
||||
}
|
||||
} catch (ignore: FileNotFoundException) {
|
||||
// Do nothing
|
||||
} catch (e: IOException) {
|
||||
logger.error("An IO error occurred loading the messages queue.", e)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
logger.error("An error occurred loading the messages queue.", e)
|
||||
}
|
||||
return listOf()
|
||||
}
|
||||
|
@ -81,7 +84,7 @@ object TellMessagesMgr {
|
|||
/**
|
||||
* Saves the messages.
|
||||
*/
|
||||
fun save(file: String, messages: List<TellMessage?>?, logger: Logger) {
|
||||
fun save(file: String, messages: List<TellMessage?>?) {
|
||||
try {
|
||||
BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos ->
|
||||
ObjectOutputStream(bos).use { output ->
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Kill.kt
|
||||
* Entries.kt
|
||||
*
|
||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -30,26 +30,26 @@
|
|||
* 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) {
|
||||
override val name = "kill"
|
||||
override val help = emptyList<String>()
|
||||
override val isOp = true
|
||||
override val isPublic = false
|
||||
override val isVisible = false
|
||||
class Entries(
|
||||
var channel: String = "",
|
||||
var ircServer: String = "",
|
||||
var logsDir: String = "",
|
||||
var backlogs: String = ""
|
||||
) {
|
||||
val links = mutableListOf<EntryLink>()
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
login: String,
|
||||
args: String,
|
||||
isOp: Boolean,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isOp) {
|
||||
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 {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@JvmOverloads
|
||||
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(']')
|
||||
if (isView && entry.comments.isNotEmpty()) {
|
||||
buff.append("[+").append(entry.comments.size).append(']')
|
||||
|
@ -76,5 +76,5 @@ object EntriesUtils {
|
|||
* Build an entry's tags/categories for display on the channel.
|
||||
*/
|
||||
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 {
|
||||
comments.add(EntryComment(comment, nick))
|
||||
return comments.size - 1
|
||||
return comments.lastIndex
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a specific comment.
|
||||
*/
|
||||
fun deleteComment(index: Int) {
|
||||
fun deleteComment(index: Int): Boolean {
|
||||
if (index < comments.size) {
|
||||
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.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
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
|
||||
|
||||
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.sendMessage
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
/**
|
||||
* The `Module` abstract class.
|
||||
*/
|
||||
abstract class AbstractModule(val bot: Mobibot) {
|
||||
abstract class AbstractModule {
|
||||
/**
|
||||
* The module's commands, if any.
|
||||
*/
|
||||
|
@ -51,12 +54,7 @@ abstract class AbstractModule(val bot: Mobibot) {
|
|||
/**
|
||||
* Responds to a command.
|
||||
*/
|
||||
abstract fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
)
|
||||
abstract fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)
|
||||
|
||||
/**
|
||||
* Returns the module's property keys.
|
||||
|
@ -74,9 +72,9 @@ abstract class AbstractModule(val bot: Mobibot) {
|
|||
/**
|
||||
* Responds with the module's help.
|
||||
*/
|
||||
open fun helpResponse(sender: String, isPrivate: Boolean): Boolean {
|
||||
open fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -33,35 +33,32 @@ package net.thauvin.erik.mobibot.modules
|
|||
|
||||
import net.objecthunter.exp4j.ExpressionBuilder
|
||||
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.helpFormat
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.text.DecimalFormat
|
||||
|
||||
/**
|
||||
* The Calc module.
|
||||
*/
|
||||
class Calc(bot: Mobibot) : AbstractModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
class Calc : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Calc::class.java)
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
with(bot) {
|
||||
try {
|
||||
send(calculate(args))
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
||||
send("No idea. This is the kind of math I don't get.")
|
||||
} catch (e: UnknownFunctionOrVariableException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e)
|
||||
send("No idea. I must've some form of Dyscalculia.")
|
||||
}
|
||||
try {
|
||||
event.respond(calculate(args))
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
||||
event.respond("No idea. This is the kind of math I don't get.")
|
||||
} catch (e: UnknownFunctionOrVariableException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e)
|
||||
event.respond("No idea. I must've some form of Dyscalculia.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,40 +34,44 @@ package net.thauvin.erik.mobibot.modules
|
|||
import net.thauvin.erik.crypto.CryptoException
|
||||
import net.thauvin.erik.crypto.CryptoPrice
|
||||
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.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.
|
||||
*/
|
||||
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).
|
||||
*/
|
||||
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)"
|
||||
with(bot) {
|
||||
if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) {
|
||||
try {
|
||||
val price = currentPrice(args.split(' '))
|
||||
val amount = try {
|
||||
price.toCurrency()
|
||||
} catch (ignore: IllegalArgumentException) {
|
||||
price.amount
|
||||
}
|
||||
send(sender, PublicMessage("${price.base}: $amount [${price.currency}]"))
|
||||
} catch (e: CryptoException) {
|
||||
if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e)
|
||||
send(e.message)
|
||||
} catch (e: Exception) {
|
||||
if (logger.isErrorEnabled) logger.error(debugMessage, e)
|
||||
send("An error has occurred while retrieving the cryptocurrency market price.")
|
||||
if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) {
|
||||
try {
|
||||
val price = currentPrice(args.split(' '))
|
||||
val amount = try {
|
||||
price.toCurrency()
|
||||
} catch (ignore: IllegalArgumentException) {
|
||||
price.amount
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
event.respond("${price.base} current price is $amount [${price.currency}]")
|
||||
} catch (e: CryptoException) {
|
||||
if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e)
|
||||
event.sendMessage(e.message!!)
|
||||
} catch (e: IOException) {
|
||||
if (logger.isErrorEnabled) logger.error(debugMessage, e)
|
||||
event.sendMessage("An IO error has occurred while retrieving the cryptocurrency market price.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -31,16 +31,21 @@
|
|||
*/
|
||||
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.buildCmdSyntax
|
||||
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.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.jdom2.JDOMException
|
||||
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.net.URL
|
||||
import java.text.NumberFormat
|
||||
|
@ -51,84 +56,68 @@ import javax.xml.XMLConstants
|
|||
/**
|
||||
* The CurrencyConverter module.
|
||||
*/
|
||||
class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
class CurrencyConverter : ThreadedModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java)
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
synchronized(this) {
|
||||
if (pubDate != today()) {
|
||||
EXCHANGE_RATES.clear()
|
||||
}
|
||||
}
|
||||
super.commandResponse(sender, cmd, args, isPrivate)
|
||||
super.commandResponse(channel, cmd, args, event)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified currencies.
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
bot.apply {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
loadRates()
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
}
|
||||
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
loadRates()
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
}
|
||||
}
|
||||
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
send(sender, EMPTY_RATE_TABLE, true)
|
||||
} else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) {
|
||||
val msg = convertCurrency(args)
|
||||
send(sender, msg)
|
||||
if (msg.isError) {
|
||||
helpResponse(sender, isPrivate)
|
||||
}
|
||||
} else if (args.contains(CURRENCY_RATES_KEYWORD)) {
|
||||
send(sender, "The reference rates for ${bold(pubDate)} are:", isPrivate)
|
||||
@Suppress("MagicNumber")
|
||||
sendList(sender, currencyRates(), 3, " ", isPrivate, isIndent = true)
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
event.respond(EMPTY_RATE_TABLE)
|
||||
} else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) {
|
||||
val msg = convertCurrency(args)
|
||||
event.respond(msg.msg)
|
||||
if (msg.isError) {
|
||||
helpResponse(event)
|
||||
}
|
||||
} else if (args.contains(CURRENCY_RATES_KEYWORD)) {
|
||||
event.sendMessage("The reference rates for ${bold(pubDate)} are:")
|
||||
event.sendList(currencyRates(), 3, " ", isIndent = true)
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(sender: String, isPrivate: Boolean): Boolean {
|
||||
with(bot) {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
loadRates()
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isDebugEnabled) logger.debug(e.debugMessage, e)
|
||||
}
|
||||
override fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
loadRates()
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
}
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
send(sender, EMPTY_RATE_TABLE, isPrivate)
|
||||
} else {
|
||||
send(sender, "To convert from one currency to another:", isPrivate)
|
||||
send(
|
||||
sender,
|
||||
helpFormat(
|
||||
buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)
|
||||
),
|
||||
isPrivate
|
||||
}
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
event.sendMessage(EMPTY_RATE_TABLE)
|
||||
} else {
|
||||
val nick = event.bot().nick
|
||||
event.sendMessage("To convert from one currency to another:")
|
||||
event.sendMessage(helpFormat(buildCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)))
|
||||
event.sendMessage("For a listing of current reference rates:")
|
||||
event.sendMessage(
|
||||
helpFormat(
|
||||
buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled)
|
||||
)
|
||||
send(sender, "For a listing of current reference rates:", isPrivate)
|
||||
send(
|
||||
sender,
|
||||
helpFormat(
|
||||
buildCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled)
|
||||
),
|
||||
isPrivate
|
||||
)
|
||||
send(sender, "The supported currencies are: ", isPrivate)
|
||||
@Suppress("MagicNumber")
|
||||
sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate = isPrivate, isIndent = true)
|
||||
}
|
||||
)
|
||||
event.sendMessage("The supported currencies are: ")
|
||||
event.sendList(ArrayList(EXCHANGE_RATES.keys), 11, isIndent = true)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -161,7 +150,6 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
/**
|
||||
* Converts from a currency to another.
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
@JvmStatic
|
||||
fun convertCurrency(query: String): Message {
|
||||
val cmds = query.split(" ")
|
||||
|
@ -194,7 +182,6 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
fun currencyRates(): List<String> {
|
||||
val rates = mutableListOf<String>()
|
||||
for ((key, value) in EXCHANGE_RATES.toSortedMap()) {
|
||||
@Suppress("MagicNumber")
|
||||
rates.add("$key: ${value.padStart(8)}")
|
||||
}
|
||||
return rates
|
||||
|
|
|
@ -31,37 +31,33 @@
|
|||
*/
|
||||
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 org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* The Dice module.
|
||||
*/
|
||||
class Dice(bot: Mobibot) : AbstractModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
class Dice : AbstractModule() {
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
val botRoll = roll()
|
||||
val roll = roll()
|
||||
val botTotal = botRoll.first + botRoll.second
|
||||
val total = roll.first + roll.second
|
||||
with(bot) {
|
||||
send(
|
||||
channel,
|
||||
"$sender rolled ${total}: ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]}",
|
||||
isPrivate
|
||||
with(event.bot()) {
|
||||
event.respond(
|
||||
"you rolled ${DICE_FACES[roll.first]} ${DICE_FACES[roll.second]} for a total of ${bold(total)}"
|
||||
)
|
||||
action(
|
||||
"rolled ${botTotal}: ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]}"
|
||||
sendIRC().action(
|
||||
channel,
|
||||
"rolled ${DICE_FACES[botRoll.first]} ${DICE_FACES[botRoll.second]} for a total of ${bold(botTotal)}"
|
||||
)
|
||||
when (winLoseOrTie(botTotal, total)) {
|
||||
Result.WIN -> action("wins.")
|
||||
Result.LOSE -> action("lost.")
|
||||
else -> action("tied.")
|
||||
Result.WIN -> sendIRC().action(channel, "wins.")
|
||||
Result.LOSE -> sendIRC().action(channel, "lost.")
|
||||
else -> sendIRC().action(channel, "tied.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,45 +31,49 @@
|
|||
*/
|
||||
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.encodeUrl
|
||||
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.urlReader
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import org.jibble.pircbot.Colors
|
||||
import org.json.JSONException
|
||||
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.net.URL
|
||||
|
||||
/**
|
||||
* The GoogleSearch module.
|
||||
*/
|
||||
class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) {
|
||||
class GoogleSearch : ThreadedModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java)
|
||||
|
||||
/**
|
||||
* Searches Google.
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val results = searchGoogle(
|
||||
args, properties[GOOGLE_API_KEY_PROP],
|
||||
properties[GOOGLE_CSE_KEY_PROP]
|
||||
)
|
||||
for (msg in results) {
|
||||
send(sender, msg)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
send(sender, e.message, isPrivate)
|
||||
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val results = searchGoogle(
|
||||
args, properties[GOOGLE_API_KEY_PROP],
|
||||
properties[GOOGLE_CSE_KEY_PROP]
|
||||
)
|
||||
for (msg in results) {
|
||||
event.sendMessage(channel, msg)
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
event.sendMessage(e.message!!)
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,29 +96,33 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) {
|
|||
if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) {
|
||||
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 {
|
||||
val url = URL(
|
||||
"https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" +
|
||||
"&q=${encodeUrl(query)}&filter=1&num=5&alt=json"
|
||||
)
|
||||
val json = JSONObject(urlReader(url))
|
||||
val ja = json.getJSONArray("items")
|
||||
for (i in 0 until ja.length()) {
|
||||
val j = ja.getJSONObject(i)
|
||||
results.add(NoticeMessage(unescapeXml(j.getString("title"))))
|
||||
results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN))
|
||||
if (json.has("items")) {
|
||||
val ja = json.getJSONArray("items")
|
||||
for (i in 0 until ja.length()) {
|
||||
val j = ja.getJSONObject(i)
|
||||
results.add(NoticeMessage(unescapeXml(j.getString("title"))))
|
||||
results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN))
|
||||
}
|
||||
} else {
|
||||
results.add(ErrorMessage("No results found.", Colors.RED))
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e)
|
||||
} catch (e: JSONException) {
|
||||
throw ModuleException("searchGoogle($query)", "A JSON error has occurred searching Google.", e)
|
||||
}
|
||||
results
|
||||
} 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.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.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.urlReader
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* The Joke module.
|
||||
*/
|
||||
class Joke(bot: Mobibot) : ThreadedModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
class Joke : ThreadedModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Joke::class.java)
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
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/).
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
try {
|
||||
send(cyan(randomJoke().msg))
|
||||
sendIRC().notice(channel, cyan(randomJoke().msg))
|
||||
} catch (e: ModuleException) {
|
||||
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
|
||||
|
||||
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 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.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
|
@ -42,52 +45,51 @@ import java.net.UnknownHostException
|
|||
/**
|
||||
* The Lookup module.
|
||||
*/
|
||||
class Lookup(bot: Mobibot) : AbstractModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
class Lookup : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java)
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.matches("(\\S.)+(\\S)+".toRegex())) {
|
||||
with(bot) {
|
||||
try {
|
||||
nslookup(args).split(',').forEach {
|
||||
send(it.trim())
|
||||
}
|
||||
} catch (ignore: UnknownHostException) {
|
||||
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]?)").toRegex()
|
||||
)
|
||||
) {
|
||||
try {
|
||||
val lines = whois(args)
|
||||
if (lines.isNotEmpty()) {
|
||||
var line: String
|
||||
for (rawLine in lines) {
|
||||
line = rawLine.trim()
|
||||
if (line.isNotEmpty() && line[0] != '#') {
|
||||
send(line)
|
||||
try {
|
||||
event.respondWith(nslookup(args).prependIndent())
|
||||
} catch (ignore: UnknownHostException) {
|
||||
if (args.matches(
|
||||
("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(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]?)").toRegex()
|
||||
)
|
||||
) {
|
||||
try {
|
||||
val lines = whois(args)
|
||||
if (lines.isNotEmpty()) {
|
||||
var line: String
|
||||
var hasData = false
|
||||
for (rawLine in lines) {
|
||||
line = rawLine.trim()
|
||||
if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) {
|
||||
if (!hasData) {
|
||||
event.respondWith(line)
|
||||
hasData = true
|
||||
} else {
|
||||
event.bot().sendIRC().notice(event.user.nick, line)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
send("Unknown host.")
|
||||
}
|
||||
} catch (ioe: IOException) {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Unable to perform whois IP lookup: $args", ioe)
|
||||
}
|
||||
send("Unable to perform whois IP lookup: ${ioe.message}")
|
||||
} else {
|
||||
event.respond("Unknown host.")
|
||||
}
|
||||
} else {
|
||||
send("Unknown host.")
|
||||
} catch (ioe: IOException) {
|
||||
if (logger.isWarnEnabled) {
|
||||
logger.warn("Unable to perform whois IP lookup: $args", ioe)
|
||||
}
|
||||
event.respond("Unable to perform whois IP lookup: ${ioe.message}")
|
||||
}
|
||||
} else {
|
||||
event.respond("Unknown host.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, true)
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,24 +31,20 @@
|
|||
*/
|
||||
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 org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* The Ping module.
|
||||
*/
|
||||
class Ping(bot: Mobibot) : AbstractModule(bot) {
|
||||
class Ping : AbstractModule() {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
bot.action(randomPing())
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
event.bot().sendIRC().action(channel, randomPing())
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -32,16 +32,17 @@
|
|||
|
||||
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.helpFormat
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
/**
|
||||
* Simple module example in Kotlin.
|
||||
*/
|
||||
class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) {
|
||||
class RockPaperScissors : AbstractModule() {
|
||||
init {
|
||||
with(commands) {
|
||||
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 botHand = Hands.values()[Random.nextInt(0, Hands.values().size)]
|
||||
with(bot) {
|
||||
send("${hand.emoji} vs. ${botHand.emoji}")
|
||||
with(event.bot()) {
|
||||
sendIRC().message(channel, "${hand.emoji} vs. ${botHand.emoji}")
|
||||
when {
|
||||
hand == botHand -> {
|
||||
action("tied.")
|
||||
sendIRC().action(channel, "tied.")
|
||||
}
|
||||
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 -> {
|
||||
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
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
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.urlReader
|
||||
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 org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP])
|
||||
for (msg in messages) {
|
||||
send(sender, msg)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
send(e.message)
|
||||
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP])
|
||||
for (msg in messages) {
|
||||
event.sendMessage(channel, msg)
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
event.sendMessage(e.message!!)
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,9 +132,9 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
|||
"${STOCK_CMD.capitalise()} is disabled. The API key is missing."
|
||||
)
|
||||
}
|
||||
return if (symbol.isNotBlank()) {
|
||||
val messages = mutableListOf<Message>()
|
||||
if (symbol.isNotBlank()) {
|
||||
val debugMessage = "getQuote($symbol)"
|
||||
val messages = mutableListOf<Message>()
|
||||
var response: String
|
||||
try {
|
||||
with(messages) {
|
||||
|
@ -161,56 +164,55 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
|||
val quote = json.getJSONObject("Global Quote")
|
||||
if (quote.isEmpty) {
|
||||
add(ErrorMessage(INVALID_SYMBOL))
|
||||
return messages
|
||||
}
|
||||
} else {
|
||||
|
||||
add(
|
||||
PublicMessage(
|
||||
"Symbol: " + unescapeXml(quote.getString("01. symbol"))
|
||||
+ " [" + unescapeXml(symbolInfo.getString("2. name")) + ']'
|
||||
add(
|
||||
PublicMessage(
|
||||
"Symbol: " + unescapeXml(quote.getString("01. symbol"))
|
||||
+ " [" + unescapeXml(symbolInfo.getString("2. name")) + ']'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
val pad = 10
|
||||
val pad = 10
|
||||
|
||||
add(
|
||||
PublicMessage(
|
||||
"Price:".padEnd(pad).prependIndent()
|
||||
+ unescapeXml(quote.getString("05. price"))
|
||||
add(
|
||||
PublicMessage(
|
||||
"Price:".padEnd(pad).prependIndent()
|
||||
+ unescapeXml(quote.getString("05. price"))
|
||||
)
|
||||
)
|
||||
)
|
||||
add(
|
||||
PublicMessage(
|
||||
"Previous:".padEnd(pad).prependIndent()
|
||||
+ unescapeXml(quote.getString("08. previous close"))
|
||||
add(
|
||||
PublicMessage(
|
||||
"Previous:".padEnd(pad).prependIndent()
|
||||
+ unescapeXml(quote.getString("08. previous close"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val data = arrayOf(
|
||||
"Open" to "02. open",
|
||||
"High" to "03. high",
|
||||
"Low" to "04. low",
|
||||
"Volume" to "06. volume",
|
||||
"Latest" to "07. latest trading day"
|
||||
)
|
||||
val data = arrayOf(
|
||||
"Open" to "02. open",
|
||||
"High" to "03. high",
|
||||
"Low" to "04. low",
|
||||
"Volume" to "06. volume",
|
||||
"Latest" to "07. latest trading day"
|
||||
)
|
||||
|
||||
data.forEach {
|
||||
add(
|
||||
NoticeMessage(
|
||||
"${it.first}:".padEnd(pad).prependIndent()
|
||||
+ unescapeXml(quote.getString(it.second))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
data.forEach {
|
||||
add(
|
||||
NoticeMessage(
|
||||
"${it.first}:".padEnd(pad).prependIndent()
|
||||
+ unescapeXml(quote.getString(it.second))
|
||||
"Change:".padEnd(pad).prependIndent()
|
||||
+ unescapeXml(quote.getString("09. change"))
|
||||
+ " [" + unescapeXml(quote.getString("10. change percent")) + ']'
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
add(
|
||||
NoticeMessage(
|
||||
"Change:".padEnd(pad).prependIndent()
|
||||
+ unescapeXml(quote.getString("09. change"))
|
||||
+ " [" + unescapeXml(quote.getString("10. change percent")) + ']'
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
|
@ -218,10 +220,10 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
|||
} catch (e: NullPointerException) {
|
||||
throw ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e)
|
||||
}
|
||||
messages
|
||||
} 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.runBlocking
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
/**
|
||||
* The `ThreadedModule` class.
|
||||
*/
|
||||
abstract class ThreadedModule(bot: Mobibot) : AbstractModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isEnabled && args.isNotEmpty()) {
|
||||
abstract class ThreadedModule : AbstractModule() {
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (isEnabled && event.message.isNotEmpty()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
run(sender, cmd, args, isPrivate)
|
||||
run(channel, cmd, args, event)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the thread.
|
||||
*/
|
||||
abstract fun run(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
)
|
||||
abstract fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent)
|
||||
}
|
||||
|
|
|
@ -34,21 +34,26 @@ package net.thauvin.erik.mobibot.modules
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.TwitterTimer
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import twitter4j.TwitterException
|
||||
import twitter4j.TwitterFactory
|
||||
import twitter4j.conf.ConfigurationBuilder
|
||||
import java.util.Timer
|
||||
|
||||
/**
|
||||
* 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.
|
||||
private val entries: MutableSet<Int> = HashSet()
|
||||
|
||||
|
@ -87,16 +92,14 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
* Send a notification to the registered Twitter handle.
|
||||
*/
|
||||
fun notification(msg: String) {
|
||||
with(bot) {
|
||||
if (isEnabled && !handle.isNullOrBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
try {
|
||||
post(message = msg, isDm = true)
|
||||
if (logger.isDebugEnabled) logger.debug("Notified @$handle: $msg")
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to notify @$handle: $msg", e)
|
||||
}
|
||||
if (isEnabled && !handle.isNullOrBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
try {
|
||||
post(message = msg, isDm = true)
|
||||
if (logger.isDebugEnabled) logger.debug("Notified @$handle: $msg")
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to notify @$handle: $msg", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +110,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
* Posts on Twitter.
|
||||
*/
|
||||
@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(
|
||||
properties[CONSUMER_KEY_PROP],
|
||||
properties[CONSUMER_SECRET_PROP],
|
||||
|
@ -123,35 +126,32 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
* Post an entry to twitter.
|
||||
*/
|
||||
fun postEntry(index: Int) {
|
||||
with(bot) {
|
||||
if (isAutoPost && hasEntry(index) && LinksMgr.entries.size >= index) {
|
||||
val entry = LinksMgr.entries[index]
|
||||
val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel"
|
||||
runBlocking {
|
||||
launch {
|
||||
try {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkCmd(index))
|
||||
}
|
||||
post(message = msg, isDm = false)
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e)
|
||||
if (isAutoPost && hasEntry(index) && LinksMgr.entries.links.size >= index) {
|
||||
val entry = LinksMgr.entries.links[index]
|
||||
val msg = "${entry.title} ${entry.link} via ${entry.nick} on ${entry.channel}"
|
||||
runBlocking {
|
||||
launch {
|
||||
try {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Posting {} to Twitter.", EntriesUtils.buildLinkLabel(index))
|
||||
}
|
||||
post(message = msg, isDm = false)
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to post entry on Twitter.", e)
|
||||
}
|
||||
}
|
||||
removeEntry(index)
|
||||
}
|
||||
removeEntry(index)
|
||||
}
|
||||
}
|
||||
|
||||
fun queueEntry(index: Int) {
|
||||
if (isAutoPost) {
|
||||
addEntry(index)
|
||||
if (bot.logger.isDebugEnabled) {
|
||||
bot.logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkCmd(index))
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Scheduling {} for posting on Twitter.", EntriesUtils.buildLinkLabel(index))
|
||||
}
|
||||
@Suppress("MagicNumber")
|
||||
bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L)
|
||||
timer.schedule(TwitterTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,18 +162,12 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
/**
|
||||
* Posts to twitter.
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
try {
|
||||
send(
|
||||
sender,
|
||||
post(sender, "$args (by $sender on $channel)", false).msg,
|
||||
isPrivate
|
||||
)
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
send(sender, e.message, isPrivate)
|
||||
}
|
||||
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
try {
|
||||
event.respond(post(event.user.nick, "$args (by ${event.user.nick} on $channel)", false))
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
event.respond(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,6 +175,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
* Post all the entries to Twitter on shutdown.
|
||||
*/
|
||||
fun shutdown() {
|
||||
timer.cancel()
|
||||
if (isAutoPost) {
|
||||
for (index in entries) {
|
||||
postEntry(index)
|
||||
|
@ -213,23 +208,23 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
|||
handle: String?,
|
||||
message: String,
|
||||
isDm: Boolean
|
||||
): Message {
|
||||
): String {
|
||||
return try {
|
||||
val cb = ConfigurationBuilder()
|
||||
cb.setDebugEnabled(true)
|
||||
.setOAuthConsumerKey(consumerKey)
|
||||
.setOAuthConsumerSecret(consumerSecret)
|
||||
.setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret)
|
||||
val cb = ConfigurationBuilder().apply {
|
||||
setDebugEnabled(true)
|
||||
setOAuthConsumerKey(consumerKey)
|
||||
setOAuthConsumerSecret(consumerSecret)
|
||||
setOAuthAccessToken(token)
|
||||
setOAuthAccessTokenSecret(tokenSecret)
|
||||
}
|
||||
val tf = TwitterFactory(cb.build())
|
||||
val twitter = tf.instance
|
||||
if (!isDm) {
|
||||
val status = twitter.updateStatus(message)
|
||||
NoticeMessage(
|
||||
"You message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}"
|
||||
)
|
||||
"Your message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}"
|
||||
} else {
|
||||
val dm = twitter.sendDirectMessage(handle, message)
|
||||
NoticeMessage(dm.text)
|
||||
dm.text
|
||||
}
|
||||
} catch (e: TwitterException) {
|
||||
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.Country
|
||||
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.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.capitalizeWords
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
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.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
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
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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()) {
|
||||
with(bot) {
|
||||
try {
|
||||
val messages = getWeather(args, properties[OWM_API_KEY_PROP])
|
||||
if (messages[0].isError) {
|
||||
helpResponse(sender, isPrivate)
|
||||
} else {
|
||||
for (msg in messages) {
|
||||
send(sender, msg)
|
||||
}
|
||||
try {
|
||||
val messages = getWeather(args, properties[OWM_API_KEY_PROP])
|
||||
if (messages[0].isError) {
|
||||
helpResponse(event)
|
||||
} else {
|
||||
for (msg in messages) {
|
||||
event.sendMessage(channel, msg)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isDebugEnabled) logger.debug(e.debugMessage, e)
|
||||
send(e.message)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
event.respond(e.message)
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +93,6 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) {
|
|||
* Converts and rounds temperature from °F to °C.
|
||||
*/
|
||||
fun ftoC(d: Double?): Pair<Int, Int> {
|
||||
@Suppress("MagicNumber")
|
||||
val c = (d!! - 32) * 5 / 9
|
||||
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.
|
||||
*/
|
||||
fun mphToKmh(w: Double): Pair<Int, Int> {
|
||||
@Suppress("MagicNumber")
|
||||
val kmh = w * 1.60934
|
||||
return w.roundToInt() to kmh.roundToInt()
|
||||
}
|
||||
|
|
|
@ -31,12 +31,11 @@
|
|||
*/
|
||||
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.helpFormat
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
@ -46,7 +45,7 @@ import java.util.Collections
|
|||
/**
|
||||
* The WorldTime module.
|
||||
*/
|
||||
class WorldTime(bot: Mobibot) : AbstractModule(bot) {
|
||||
class WorldTime : AbstractModule() {
|
||||
companion object {
|
||||
// Beats (Internet Time) keyword
|
||||
const val BEATS_KEYWORD = ".beats"
|
||||
|
@ -57,6 +56,12 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) {
|
|||
// The Time command
|
||||
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
|
||||
private var dtf =
|
||||
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.
|
||||
*/
|
||||
@Suppress("MagicNumber", "ImplicitDefaultLocale")
|
||||
private fun internetTime(): String {
|
||||
val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00"))
|
||||
val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60
|
||||
+ 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.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun time(query: String): Message {
|
||||
val tz = COUNTRIES_MAP[(query.substring(query.indexOf(' ') + 1).trim()).uppercase()]
|
||||
val response: String = if (tz != null) {
|
||||
fun time(query: String = DEFAULT_ZONE): String {
|
||||
val tz = COUNTRIES_MAP[(if (query.isNotBlank()) query.trim().uppercase() else DEFAULT_ZONE)]
|
||||
return if (tz != null) {
|
||||
if (BEATS_KEYWORD == tz) {
|
||||
"The current Internet Time is: ${bold(internetTime())} $BEATS_KEYWORD"
|
||||
"The current Internet Time is ${bold(internetTime())} $BEATS_KEYWORD"
|
||||
} else {
|
||||
(ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf)
|
||||
+ bold(tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ')))
|
||||
}
|
||||
} else {
|
||||
return ErrorMessage("Unsupported country/zone. Please try again.")
|
||||
"Unsupported country/zone. Please try again."
|
||||
}
|
||||
return PublicMessage(response)
|
||||
}
|
||||
|
||||
init {
|
||||
// Initialize the countries map
|
||||
val countries = mutableMapOf<String, String>()
|
||||
countries["AD"] = "Europe/Andorra"
|
||||
countries["AE"] = "Asia/Dubai"
|
||||
countries["AF"] = "Asia/Kabul"
|
||||
countries["AG"] = "America/Antigua"
|
||||
countries["AI"] = "America/Anguilla"
|
||||
countries["AKDT"] = "America/Anchorage"
|
||||
countries["AKST"] = "America/Anchorage"
|
||||
countries["AL"] = "Europe/Tirane"
|
||||
countries["AM"] = "Asia/Yerevan"
|
||||
countries["AO"] = "Africa/Luanda"
|
||||
countries["AQ"] = "Antarctica/South_Pole"
|
||||
countries["AR"] = "America/Argentina/Buenos_Aires"
|
||||
countries["AS"] = "Pacific/Pago_Pago"
|
||||
countries["AT"] = "Europe/Vienna"
|
||||
countries["AU"] = "Australia/Sydney"
|
||||
countries["AW"] = "America/Aruba"
|
||||
countries["AX"] = "Europe/Mariehamn"
|
||||
countries["AZ"] = "Asia/Baku"
|
||||
countries["BA"] = "Europe/Sarajevo"
|
||||
countries["BB"] = "America/Barbados"
|
||||
countries["BD"] = "Asia/Dhaka"
|
||||
countries["BE"] = "Europe/Brussels"
|
||||
countries["BEAT"] = BEATS_KEYWORD
|
||||
countries["BF"] = "Africa/Ouagadougou"
|
||||
countries["BG"] = "Europe/Sofia"
|
||||
countries["BH"] = "Asia/Bahrain"
|
||||
countries["BI"] = "Africa/Bujumbura"
|
||||
countries["BJ"] = "Africa/Porto-Novo"
|
||||
countries["BL"] = "America/St_Barthelemy"
|
||||
countries["BM"] = "Atlantic/Bermuda"
|
||||
countries["BMT"] = BEATS_KEYWORD
|
||||
countries["BN"] = "Asia/Brunei"
|
||||
countries["BO"] = "America/La_Paz"
|
||||
countries["BQ"] = "America/Kralendijk"
|
||||
countries["BR"] = "America/Sao_Paulo"
|
||||
countries["BS"] = "America/Nassau"
|
||||
countries["BT"] = "Asia/Thimphu"
|
||||
countries["BW"] = "Africa/Gaborone"
|
||||
countries["BY"] = "Europe/Minsk"
|
||||
countries["BZ"] = "America/Belize"
|
||||
countries["CA"] = "America/Montreal"
|
||||
countries["CC"] = "Indian/Cocos"
|
||||
countries["CD"] = "Africa/Kinshasa"
|
||||
countries["CDT"] = "America/Chicago"
|
||||
countries["CET"] = "CET"
|
||||
countries["CF"] = "Africa/Bangui"
|
||||
countries["CG"] = "Africa/Brazzaville"
|
||||
countries["CH"] = "Europe/Zurich"
|
||||
countries["CI"] = "Africa/Abidjan"
|
||||
countries["CK"] = "Pacific/Rarotonga"
|
||||
countries["CL"] = "America/Santiago"
|
||||
countries["CM"] = "Africa/Douala"
|
||||
countries["CN"] = "Asia/Shanghai"
|
||||
countries["CO"] = "America/Bogota"
|
||||
countries["CR"] = "America/Costa_Rica"
|
||||
countries["CST"] = "America/Chicago"
|
||||
countries["CU"] = "Cuba"
|
||||
countries["CV"] = "Atlantic/Cape_Verde"
|
||||
countries["CW"] = "America/Curacao"
|
||||
countries["CX"] = "Indian/Christmas"
|
||||
countries["CY"] = "Asia/Nicosia"
|
||||
countries["CZ"] = "Europe/Prague"
|
||||
countries["DE"] = "Europe/Berlin"
|
||||
countries["DJ"] = "Africa/Djibouti"
|
||||
countries["DK"] = "Europe/Copenhagen"
|
||||
countries["DM"] = "America/Dominica"
|
||||
countries["DO"] = "America/Santo_Domingo"
|
||||
countries["DZ"] = "Africa/Algiers"
|
||||
countries["EC"] = "Pacific/Galapagos"
|
||||
countries["EDT"] = "America/New_York"
|
||||
countries["EE"] = "Europe/Tallinn"
|
||||
countries["EG"] = "Africa/Cairo"
|
||||
countries["EH"] = "Africa/El_Aaiun"
|
||||
countries["ER"] = "Africa/Asmara"
|
||||
countries["ES"] = "Europe/Madrid"
|
||||
countries["EST"] = "America/New_York"
|
||||
countries["ET"] = "Africa/Addis_Ababa"
|
||||
countries["FI"] = "Europe/Helsinki"
|
||||
countries["FJ"] = "Pacific/Fiji"
|
||||
countries["FK"] = "Atlantic/Stanley"
|
||||
countries["FM"] = "Pacific/Yap"
|
||||
countries["FO"] = "Atlantic/Faroe"
|
||||
countries["FR"] = "Europe/Paris"
|
||||
countries["GA"] = "Africa/Libreville"
|
||||
countries["GB"] = "Europe/London"
|
||||
countries["GD"] = "America/Grenada"
|
||||
countries["GE"] = "Asia/Tbilisi"
|
||||
countries["GF"] = "America/Cayenne"
|
||||
countries["GG"] = "Europe/Guernsey"
|
||||
countries["GH"] = "Africa/Accra"
|
||||
countries["GI"] = "Europe/Gibraltar"
|
||||
countries["GL"] = "America/Thule"
|
||||
countries["GM"] = "Africa/Banjul"
|
||||
countries["GMT"] = "GMT"
|
||||
countries["GN"] = "Africa/Conakry"
|
||||
countries["GP"] = "America/Guadeloupe"
|
||||
countries["GQ"] = "Africa/Malabo"
|
||||
countries["GR"] = "Europe/Athens"
|
||||
countries["GS"] = "Atlantic/South_Georgia"
|
||||
countries["GT"] = "America/Guatemala"
|
||||
countries["GU"] = "Pacific/Guam"
|
||||
countries["GW"] = "Africa/Bissau"
|
||||
countries["GY"] = "America/Guyana"
|
||||
countries["HK"] = "Asia/Hong_Kong"
|
||||
countries["HN"] = "America/Tegucigalpa"
|
||||
countries["HR"] = "Europe/Zagreb"
|
||||
countries["HST"] = "Pacific/Honolulu"
|
||||
countries["HT"] = "America/Port-au-Prince"
|
||||
countries["HU"] = "Europe/Budapest"
|
||||
countries["ID"] = "Asia/Jakarta"
|
||||
countries["IE"] = "Europe/Dublin"
|
||||
countries["IL"] = "Asia/Tel_Aviv"
|
||||
countries["IM"] = "Europe/Isle_of_Man"
|
||||
countries["IN"] = "Asia/Kolkata"
|
||||
countries["IO"] = "Indian/Chagos"
|
||||
countries["IQ"] = "Asia/Baghdad"
|
||||
countries["IR"] = "Asia/Tehran"
|
||||
countries["IS"] = "Atlantic/Reykjavik"
|
||||
countries["IT"] = "Europe/Rome"
|
||||
countries["JE"] = "Europe/Jersey"
|
||||
countries["JM"] = "Jamaica"
|
||||
countries["JO"] = "Asia/Amman"
|
||||
countries["JP"] = "Asia/Tokyo"
|
||||
countries["KE"] = "Africa/Nairobi"
|
||||
countries["KG"] = "Asia/Bishkek"
|
||||
countries["KH"] = "Asia/Phnom_Penh"
|
||||
countries["KI"] = "Pacific/Tarawa"
|
||||
countries["KM"] = "Indian/Comoro"
|
||||
countries["KN"] = "America/St_Kitts"
|
||||
countries["KP"] = "Asia/Pyongyang"
|
||||
countries["KR"] = "Asia/Seoul"
|
||||
countries["KW"] = "Asia/Riyadh"
|
||||
countries["KY"] = "America/Cayman"
|
||||
countries["KZ"] = "Asia/Oral"
|
||||
countries["LA"] = "Asia/Vientiane"
|
||||
countries["LB"] = "Asia/Beirut"
|
||||
countries["LC"] = "America/St_Lucia"
|
||||
countries["LI"] = "Europe/Vaduz"
|
||||
countries["LK"] = "Asia/Colombo"
|
||||
countries["LR"] = "Africa/Monrovia"
|
||||
countries["LS"] = "Africa/Maseru"
|
||||
countries["LT"] = "Europe/Vilnius"
|
||||
countries["LU"] = "Europe/Luxembourg"
|
||||
countries["LV"] = "Europe/Riga"
|
||||
countries["LY"] = "Africa/Tripoli"
|
||||
countries["MA"] = "Africa/Casablanca"
|
||||
countries["MC"] = "Europe/Monaco"
|
||||
countries["MD"] = "Europe/Chisinau"
|
||||
countries["MDT"] = "America/Denver"
|
||||
countries["ME"] = "Europe/Podgorica"
|
||||
countries["MF"] = "America/Marigot"
|
||||
countries["MG"] = "Indian/Antananarivo"
|
||||
countries["MH"] = "Pacific/Majuro"
|
||||
countries["MK"] = "Europe/Skopje"
|
||||
countries["ML"] = "Africa/Timbuktu"
|
||||
countries["MM"] = "Asia/Yangon"
|
||||
countries["MN"] = "Asia/Ulaanbaatar"
|
||||
countries["MO"] = "Asia/Macau"
|
||||
countries["MP"] = "Pacific/Saipan"
|
||||
countries["MQ"] = "America/Martinique"
|
||||
countries["MR"] = "Africa/Nouakchott"
|
||||
countries["MS"] = "America/Montserrat"
|
||||
countries["MST"] = "America/Denver"
|
||||
countries["MT"] = "Europe/Malta"
|
||||
countries["MU"] = "Indian/Mauritius"
|
||||
countries["MV"] = "Indian/Maldives"
|
||||
countries["MW"] = "Africa/Blantyre"
|
||||
countries["MX"] = "America/Mexico_City"
|
||||
countries["MY"] = "Asia/Kuala_Lumpur"
|
||||
countries["MZ"] = "Africa/Maputo"
|
||||
countries["NA"] = "Africa/Windhoek"
|
||||
countries["NC"] = "Pacific/Noumea"
|
||||
countries["NE"] = "Africa/Niamey"
|
||||
countries["NF"] = "Pacific/Norfolk"
|
||||
countries["NG"] = "Africa/Lagos"
|
||||
countries["NI"] = "America/Managua"
|
||||
countries["NL"] = "Europe/Amsterdam"
|
||||
countries["NO"] = "Europe/Oslo"
|
||||
countries["NP"] = "Asia/Kathmandu"
|
||||
countries["NR"] = "Pacific/Nauru"
|
||||
countries["NU"] = "Pacific/Niue"
|
||||
countries["NZ"] = "Pacific/Auckland"
|
||||
countries["OM"] = "Asia/Muscat"
|
||||
countries["PA"] = "America/Panama"
|
||||
countries["PDT"] = "America/Los_Angeles"
|
||||
countries["PE"] = "America/Lima"
|
||||
countries["PF"] = "Pacific/Tahiti"
|
||||
countries["PG"] = "Pacific/Port_Moresby"
|
||||
countries["PH"] = "Asia/Manila"
|
||||
countries["PK"] = "Asia/Karachi"
|
||||
countries["PL"] = "Europe/Warsaw"
|
||||
countries["PM"] = "America/Miquelon"
|
||||
countries["PN"] = "Pacific/Pitcairn"
|
||||
countries["PR"] = "America/Puerto_Rico"
|
||||
countries["PS"] = "Asia/Gaza"
|
||||
countries["PST"] = "America/Los_Angeles"
|
||||
countries["PT"] = "Europe/Lisbon"
|
||||
countries["PW"] = "Pacific/Palau"
|
||||
countries["PY"] = "America/Asuncion"
|
||||
countries["QA"] = "Asia/Qatar"
|
||||
countries["RE"] = "Indian/Reunion"
|
||||
countries["RO"] = "Europe/Bucharest"
|
||||
countries["RS"] = "Europe/Belgrade"
|
||||
countries["RU"] = "Europe/Moscow"
|
||||
countries["RW"] = "Africa/Kigali"
|
||||
countries["SA"] = "Asia/Riyadh"
|
||||
countries["SB"] = "Pacific/Guadalcanal"
|
||||
countries["SC"] = "Indian/Mahe"
|
||||
countries["SD"] = "Africa/Khartoum"
|
||||
countries["SE"] = "Europe/Stockholm"
|
||||
countries["SG"] = "Asia/Singapore"
|
||||
countries["SH"] = "Atlantic/St_Helena"
|
||||
countries["SI"] = "Europe/Ljubljana"
|
||||
countries["SJ"] = "Atlantic/Jan_Mayen"
|
||||
countries["SK"] = "Europe/Bratislava"
|
||||
countries["SL"] = "Africa/Freetown"
|
||||
countries["SM"] = "Europe/San_Marino"
|
||||
countries["SN"] = "Africa/Dakar"
|
||||
countries["SO"] = "Africa/Mogadishu"
|
||||
countries["SR"] = "America/Paramaribo"
|
||||
countries["SS"] = "Africa/Juba"
|
||||
countries["ST"] = "Africa/Sao_Tome"
|
||||
countries["SV"] = "America/El_Salvador"
|
||||
countries["SX"] = "America/Lower_Princes"
|
||||
countries["SY"] = "Asia/Damascus"
|
||||
countries["SZ"] = "Africa/Mbabane"
|
||||
countries["TC"] = "America/Grand_Turk"
|
||||
countries["TD"] = "Africa/Ndjamena"
|
||||
countries["TF"] = "Indian/Kerguelen"
|
||||
countries["TG"] = "Africa/Lome"
|
||||
countries["TH"] = "Asia/Bangkok"
|
||||
countries["TJ"] = "Asia/Dushanbe"
|
||||
countries["TK"] = "Pacific/Fakaofo"
|
||||
countries["TL"] = "Asia/Dili"
|
||||
countries["TM"] = "Asia/Ashgabat"
|
||||
countries["TN"] = "Africa/Tunis"
|
||||
countries["TO"] = "Pacific/Tongatapu"
|
||||
countries["TR"] = "Europe/Istanbul"
|
||||
countries["TT"] = "America/Port_of_Spain"
|
||||
countries["TV"] = "Pacific/Funafuti"
|
||||
countries["TW"] = "Asia/Taipei"
|
||||
countries["TZ"] = "Africa/Dar_es_Salaam"
|
||||
countries["UA"] = "Europe/Kiev"
|
||||
countries["UG"] = "Africa/Kampala"
|
||||
countries["UK"] = "Europe/London"
|
||||
countries["UM"] = "Pacific/Wake"
|
||||
countries["US"] = "America/New_York"
|
||||
countries["UTC"] = "UTC"
|
||||
countries["UY"] = "America/Montevideo"
|
||||
countries["UZ"] = "Asia/Tashkent"
|
||||
countries["VA"] = "Europe/Vatican"
|
||||
countries["VC"] = "America/St_Vincent"
|
||||
countries["VE"] = "America/Caracas"
|
||||
countries["VG"] = "America/Tortola"
|
||||
countries["VI"] = "America/St_Thomas"
|
||||
countries["VN"] = "Asia/Ho_Chi_Minh"
|
||||
countries["VU"] = "Pacific/Efate"
|
||||
countries["WF"] = "Pacific/Wallis"
|
||||
countries["WS"] = "Pacific/Apia"
|
||||
countries["YE"] = "Asia/Aden"
|
||||
countries["YT"] = "Indian/Mayotte"
|
||||
countries["ZA"] = "Africa/Johannesburg"
|
||||
countries["ZM"] = "Africa/Lusaka"
|
||||
countries["ZULU"] = "Zulu"
|
||||
countries["ZW"] = "Africa/Harare"
|
||||
@Suppress("MagicNumber")
|
||||
// Initialize the zones map
|
||||
val zones = mutableMapOf<String, String>()
|
||||
zones["AD"] = "Europe/Andorra"
|
||||
zones["AE"] = "Asia/Dubai"
|
||||
zones["AF"] = "Asia/Kabul"
|
||||
zones["AG"] = "America/Antigua"
|
||||
zones["AI"] = "America/Anguilla"
|
||||
zones["AKDT"] = "America/Anchorage"
|
||||
zones["AKST"] = "America/Anchorage"
|
||||
zones["AL"] = "Europe/Tirane"
|
||||
zones["AM"] = "Asia/Yerevan"
|
||||
zones["AO"] = "Africa/Luanda"
|
||||
zones["AQ"] = "Antarctica/South_Pole"
|
||||
zones["AR"] = "America/Argentina/Buenos_Aires"
|
||||
zones["AS"] = "Pacific/Pago_Pago"
|
||||
zones["AT"] = "Europe/Vienna"
|
||||
zones["AU"] = "Australia/Sydney"
|
||||
zones["AW"] = "America/Aruba"
|
||||
zones["AX"] = "Europe/Mariehamn"
|
||||
zones["AZ"] = "Asia/Baku"
|
||||
zones["BA"] = "Europe/Sarajevo"
|
||||
zones["BB"] = "America/Barbados"
|
||||
zones["BD"] = "Asia/Dhaka"
|
||||
zones["BE"] = "Europe/Brussels"
|
||||
zones["BEAT"] = BEATS_KEYWORD
|
||||
zones["BF"] = "Africa/Ouagadougou"
|
||||
zones["BG"] = "Europe/Sofia"
|
||||
zones["BH"] = "Asia/Bahrain"
|
||||
zones["BI"] = "Africa/Bujumbura"
|
||||
zones["BJ"] = "Africa/Porto-Novo"
|
||||
zones["BL"] = "America/St_Barthelemy"
|
||||
zones["BM"] = "Atlantic/Bermuda"
|
||||
zones["BMT"] = BEATS_KEYWORD
|
||||
zones["BN"] = "Asia/Brunei"
|
||||
zones["BO"] = "America/La_Paz"
|
||||
zones["BQ"] = "America/Kralendijk"
|
||||
zones["BR"] = "America/Sao_Paulo"
|
||||
zones["BS"] = "America/Nassau"
|
||||
zones["BT"] = "Asia/Thimphu"
|
||||
zones["BW"] = "Africa/Gaborone"
|
||||
zones["BY"] = "Europe/Minsk"
|
||||
zones["BZ"] = "America/Belize"
|
||||
zones["CA"] = "America/Montreal"
|
||||
zones["CC"] = "Indian/Cocos"
|
||||
zones["CD"] = "Africa/Kinshasa"
|
||||
zones["CDT"] = "America/Chicago"
|
||||
zones["CET"] = "CET"
|
||||
zones["CF"] = "Africa/Bangui"
|
||||
zones["CG"] = "Africa/Brazzaville"
|
||||
zones["CH"] = "Europe/Zurich"
|
||||
zones["CI"] = "Africa/Abidjan"
|
||||
zones["CK"] = "Pacific/Rarotonga"
|
||||
zones["CL"] = "America/Santiago"
|
||||
zones["CM"] = "Africa/Douala"
|
||||
zones["CN"] = "Asia/Shanghai"
|
||||
zones["CO"] = "America/Bogota"
|
||||
zones["CR"] = "America/Costa_Rica"
|
||||
zones["CST"] = "America/Chicago"
|
||||
zones["CU"] = "Cuba"
|
||||
zones["CV"] = "Atlantic/Cape_Verde"
|
||||
zones["CW"] = "America/Curacao"
|
||||
zones["CX"] = "Indian/Christmas"
|
||||
zones["CY"] = "Asia/Nicosia"
|
||||
zones["CZ"] = "Europe/Prague"
|
||||
zones["DE"] = "Europe/Berlin"
|
||||
zones["DJ"] = "Africa/Djibouti"
|
||||
zones["DK"] = "Europe/Copenhagen"
|
||||
zones["DM"] = "America/Dominica"
|
||||
zones["DO"] = "America/Santo_Domingo"
|
||||
zones["DZ"] = "Africa/Algiers"
|
||||
zones["EC"] = "Pacific/Galapagos"
|
||||
zones["EDT"] = "America/New_York"
|
||||
zones["EE"] = "Europe/Tallinn"
|
||||
zones["EG"] = "Africa/Cairo"
|
||||
zones["EH"] = "Africa/El_Aaiun"
|
||||
zones["ER"] = "Africa/Asmara"
|
||||
zones["ES"] = "Europe/Madrid"
|
||||
zones["EST"] = "America/New_York"
|
||||
zones["ET"] = "Africa/Addis_Ababa"
|
||||
zones["FI"] = "Europe/Helsinki"
|
||||
zones["FJ"] = "Pacific/Fiji"
|
||||
zones["FK"] = "Atlantic/Stanley"
|
||||
zones["FM"] = "Pacific/Yap"
|
||||
zones["FO"] = "Atlantic/Faroe"
|
||||
zones["FR"] = "Europe/Paris"
|
||||
zones["GA"] = "Africa/Libreville"
|
||||
zones["GB"] = "Europe/London"
|
||||
zones["GD"] = "America/Grenada"
|
||||
zones["GE"] = "Asia/Tbilisi"
|
||||
zones["GF"] = "America/Cayenne"
|
||||
zones["GG"] = "Europe/Guernsey"
|
||||
zones["GH"] = "Africa/Accra"
|
||||
zones["GI"] = "Europe/Gibraltar"
|
||||
zones["GL"] = "America/Thule"
|
||||
zones["GM"] = "Africa/Banjul"
|
||||
zones["GMT"] = "GMT"
|
||||
zones["GN"] = "Africa/Conakry"
|
||||
zones["GP"] = "America/Guadeloupe"
|
||||
zones["GQ"] = "Africa/Malabo"
|
||||
zones["GR"] = "Europe/Athens"
|
||||
zones["GS"] = "Atlantic/South_Georgia"
|
||||
zones["GT"] = "America/Guatemala"
|
||||
zones["GU"] = "Pacific/Guam"
|
||||
zones["GW"] = "Africa/Bissau"
|
||||
zones["GY"] = "America/Guyana"
|
||||
zones["HK"] = "Asia/Hong_Kong"
|
||||
zones["HN"] = "America/Tegucigalpa"
|
||||
zones["HR"] = "Europe/Zagreb"
|
||||
zones["HST"] = "Pacific/Honolulu"
|
||||
zones["HT"] = "America/Port-au-Prince"
|
||||
zones["HU"] = "Europe/Budapest"
|
||||
zones["ID"] = "Asia/Jakarta"
|
||||
zones["IE"] = "Europe/Dublin"
|
||||
zones["IL"] = "Asia/Tel_Aviv"
|
||||
zones["IM"] = "Europe/Isle_of_Man"
|
||||
zones["IN"] = "Asia/Kolkata"
|
||||
zones["IO"] = "Indian/Chagos"
|
||||
zones["IQ"] = "Asia/Baghdad"
|
||||
zones["IR"] = "Asia/Tehran"
|
||||
zones["IS"] = "Atlantic/Reykjavik"
|
||||
zones["IT"] = "Europe/Rome"
|
||||
zones["JE"] = "Europe/Jersey"
|
||||
zones["JM"] = "Jamaica"
|
||||
zones["JO"] = "Asia/Amman"
|
||||
zones["JP"] = "Asia/Tokyo"
|
||||
zones["KE"] = "Africa/Nairobi"
|
||||
zones["KG"] = "Asia/Bishkek"
|
||||
zones["KH"] = "Asia/Phnom_Penh"
|
||||
zones["KI"] = "Pacific/Tarawa"
|
||||
zones["KM"] = "Indian/Comoro"
|
||||
zones["KN"] = "America/St_Kitts"
|
||||
zones["KP"] = "Asia/Pyongyang"
|
||||
zones["KR"] = "Asia/Seoul"
|
||||
zones["KW"] = "Asia/Riyadh"
|
||||
zones["KY"] = "America/Cayman"
|
||||
zones["KZ"] = "Asia/Oral"
|
||||
zones["LA"] = "Asia/Vientiane"
|
||||
zones["LB"] = "Asia/Beirut"
|
||||
zones["LC"] = "America/St_Lucia"
|
||||
zones["LI"] = "Europe/Vaduz"
|
||||
zones["LK"] = "Asia/Colombo"
|
||||
zones["LR"] = "Africa/Monrovia"
|
||||
zones["LS"] = "Africa/Maseru"
|
||||
zones["LT"] = "Europe/Vilnius"
|
||||
zones["LU"] = "Europe/Luxembourg"
|
||||
zones["LV"] = "Europe/Riga"
|
||||
zones["LY"] = "Africa/Tripoli"
|
||||
zones["MA"] = "Africa/Casablanca"
|
||||
zones["MC"] = "Europe/Monaco"
|
||||
zones["MD"] = "Europe/Chisinau"
|
||||
zones["MDT"] = "America/Denver"
|
||||
zones["ME"] = "Europe/Podgorica"
|
||||
zones["MF"] = "America/Marigot"
|
||||
zones["MG"] = "Indian/Antananarivo"
|
||||
zones["MH"] = "Pacific/Majuro"
|
||||
zones["MK"] = "Europe/Skopje"
|
||||
zones["ML"] = "Africa/Timbuktu"
|
||||
zones["MM"] = "Asia/Yangon"
|
||||
zones["MN"] = "Asia/Ulaanbaatar"
|
||||
zones["MO"] = "Asia/Macau"
|
||||
zones["MP"] = "Pacific/Saipan"
|
||||
zones["MQ"] = "America/Martinique"
|
||||
zones["MR"] = "Africa/Nouakchott"
|
||||
zones["MS"] = "America/Montserrat"
|
||||
zones["MST"] = "America/Denver"
|
||||
zones["MT"] = "Europe/Malta"
|
||||
zones["MU"] = "Indian/Mauritius"
|
||||
zones["MV"] = "Indian/Maldives"
|
||||
zones["MW"] = "Africa/Blantyre"
|
||||
zones["MX"] = "America/Mexico_City"
|
||||
zones["MY"] = "Asia/Kuala_Lumpur"
|
||||
zones["MZ"] = "Africa/Maputo"
|
||||
zones["NA"] = "Africa/Windhoek"
|
||||
zones["NC"] = "Pacific/Noumea"
|
||||
zones["NE"] = "Africa/Niamey"
|
||||
zones["NF"] = "Pacific/Norfolk"
|
||||
zones["NG"] = "Africa/Lagos"
|
||||
zones["NI"] = "America/Managua"
|
||||
zones["NL"] = "Europe/Amsterdam"
|
||||
zones["NO"] = "Europe/Oslo"
|
||||
zones["NP"] = "Asia/Kathmandu"
|
||||
zones["NR"] = "Pacific/Nauru"
|
||||
zones["NU"] = "Pacific/Niue"
|
||||
zones["NZ"] = "Pacific/Auckland"
|
||||
zones["OM"] = "Asia/Muscat"
|
||||
zones["PA"] = "America/Panama"
|
||||
zones["PDT"] = "America/Los_Angeles"
|
||||
zones["PE"] = "America/Lima"
|
||||
zones["PF"] = "Pacific/Tahiti"
|
||||
zones["PG"] = "Pacific/Port_Moresby"
|
||||
zones["PH"] = "Asia/Manila"
|
||||
zones["PK"] = "Asia/Karachi"
|
||||
zones["PL"] = "Europe/Warsaw"
|
||||
zones["PM"] = "America/Miquelon"
|
||||
zones["PN"] = "Pacific/Pitcairn"
|
||||
zones["PR"] = "America/Puerto_Rico"
|
||||
zones["PS"] = "Asia/Gaza"
|
||||
zones["PST"] = "America/Los_Angeles"
|
||||
zones["PT"] = "Europe/Lisbon"
|
||||
zones["PW"] = "Pacific/Palau"
|
||||
zones["PY"] = "America/Asuncion"
|
||||
zones["QA"] = "Asia/Qatar"
|
||||
zones["RE"] = "Indian/Reunion"
|
||||
zones["RO"] = "Europe/Bucharest"
|
||||
zones["RS"] = "Europe/Belgrade"
|
||||
zones["RU"] = "Europe/Moscow"
|
||||
zones["RW"] = "Africa/Kigali"
|
||||
zones["SA"] = "Asia/Riyadh"
|
||||
zones["SB"] = "Pacific/Guadalcanal"
|
||||
zones["SC"] = "Indian/Mahe"
|
||||
zones["SD"] = "Africa/Khartoum"
|
||||
zones["SE"] = "Europe/Stockholm"
|
||||
zones["SG"] = "Asia/Singapore"
|
||||
zones["SH"] = "Atlantic/St_Helena"
|
||||
zones["SI"] = "Europe/Ljubljana"
|
||||
zones["SJ"] = "Atlantic/Jan_Mayen"
|
||||
zones["SK"] = "Europe/Bratislava"
|
||||
zones["SL"] = "Africa/Freetown"
|
||||
zones["SM"] = "Europe/San_Marino"
|
||||
zones["SN"] = "Africa/Dakar"
|
||||
zones["SO"] = "Africa/Mogadishu"
|
||||
zones["SR"] = "America/Paramaribo"
|
||||
zones["SS"] = "Africa/Juba"
|
||||
zones["ST"] = "Africa/Sao_Tome"
|
||||
zones["SV"] = "America/El_Salvador"
|
||||
zones["SX"] = "America/Lower_Princes"
|
||||
zones["SY"] = "Asia/Damascus"
|
||||
zones["SZ"] = "Africa/Mbabane"
|
||||
zones["TC"] = "America/Grand_Turk"
|
||||
zones["TD"] = "Africa/Ndjamena"
|
||||
zones["TF"] = "Indian/Kerguelen"
|
||||
zones["TG"] = "Africa/Lome"
|
||||
zones["TH"] = "Asia/Bangkok"
|
||||
zones["TJ"] = "Asia/Dushanbe"
|
||||
zones["TK"] = "Pacific/Fakaofo"
|
||||
zones["TL"] = "Asia/Dili"
|
||||
zones["TM"] = "Asia/Ashgabat"
|
||||
zones["TN"] = "Africa/Tunis"
|
||||
zones["TO"] = "Pacific/Tongatapu"
|
||||
zones["TR"] = "Europe/Istanbul"
|
||||
zones["TT"] = "America/Port_of_Spain"
|
||||
zones["TV"] = "Pacific/Funafuti"
|
||||
zones["TW"] = "Asia/Taipei"
|
||||
zones["TZ"] = "Africa/Dar_es_Salaam"
|
||||
zones["UA"] = "Europe/Kiev"
|
||||
zones["UG"] = "Africa/Kampala"
|
||||
zones["UK"] = "Europe/London"
|
||||
zones["UM"] = "Pacific/Wake"
|
||||
zones["US"] = "America/New_York"
|
||||
zones["UTC"] = "UTC"
|
||||
zones["UY"] = "America/Montevideo"
|
||||
zones["UZ"] = "Asia/Tashkent"
|
||||
zones["VA"] = "Europe/Vatican"
|
||||
zones["VC"] = "America/St_Vincent"
|
||||
zones["VE"] = "America/Caracas"
|
||||
zones["VG"] = "America/Tortola"
|
||||
zones["VI"] = "America/St_Thomas"
|
||||
zones["VN"] = "Asia/Ho_Chi_Minh"
|
||||
zones["VU"] = "Pacific/Efate"
|
||||
zones["WF"] = "Pacific/Wallis"
|
||||
zones["WS"] = "Pacific/Apia"
|
||||
zones["YE"] = "Asia/Aden"
|
||||
zones["YT"] = "Indian/Mayotte"
|
||||
zones["ZA"] = "Africa/Johannesburg"
|
||||
zones["ZM"] = "Africa/Lusaka"
|
||||
zones["ZULU"] = "Zulu"
|
||||
zones["ZW"] = "Africa/Harare"
|
||||
ZoneId.getAvailableZoneIds().stream()
|
||||
.filter { tz: String ->
|
||||
tz.length <= 3 && !countries.containsKey(tz)
|
||||
tz.length <= 3 && !zones.containsKey(tz)
|
||||
}
|
||||
.forEach { tz: String ->
|
||||
countries[tz] = tz
|
||||
zones[tz] = tz
|
||||
}
|
||||
COUNTRIES_MAP = Collections.unmodifiableMap(countries)
|
||||
COUNTRIES_MAP = Collections.unmodifiableMap(zones)
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
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 {
|
||||
val msg = time(args)
|
||||
if (isPrivate) {
|
||||
send(sender, msg.msg, true)
|
||||
} else {
|
||||
if (msg.isError) {
|
||||
send(sender, msg.msg, false)
|
||||
} else {
|
||||
send(msg.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.equals(ZONES_ARGS, true)) {
|
||||
event.sendMessage("The supported countries/zones are: ")
|
||||
event.sendList(COUNTRIES_MAP.keys.sorted().map { it.padEnd(4) }, 14, isIndent = true)
|
||||
} else {
|
||||
event.respond(time(args))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -408,9 +387,9 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) {
|
|||
init {
|
||||
with(help) {
|
||||
add("To display a country's current date/time:")
|
||||
add(helpFormat("%c $TIME_CMD <country code>"))
|
||||
add("For a listing of the supported countries:")
|
||||
add(helpFormat("%c $TIME_CMD"))
|
||||
add(helpFormat("%c $TIME_CMD [<country code or zone>]"))
|
||||
add("For a listing of the supported countries/zones:")
|
||||
add(helpFormat("%c $TIME_CMD $ZONES_ARGS"))
|
||||
}
|
||||
commands.add(TIME_CMD)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Sanitize.kt
|
||||
* ExceptionSanitizer.kt
|
||||
*
|
||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||
* 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.modules.ModuleException
|
||||
|
||||
object Sanitize {
|
||||
object ExceptionSanitizer {
|
||||
/**
|
||||
* Returns a sanitized exception to avoid displaying api keys, etc. in CI logs.
|
||||
*/
|
||||
fun sanitizeException(e: ModuleException, vararg sanitize: String): ModuleException {
|
||||
var sanitizedException = e
|
||||
fun ModuleException.sanitize(vararg sanitize: String): ModuleException {
|
||||
val search = sanitize.filter { it.isNotBlank() }.toTypedArray()
|
||||
if (search.isNotEmpty()) {
|
||||
val obfuscate = search.map { it.obfuscate() }.toTypedArray()
|
||||
with(e) {
|
||||
if (cause?.message != null) {
|
||||
sanitizedException = ModuleException(
|
||||
with(this) {
|
||||
if (!cause?.message.isNullOrBlank()) {
|
||||
return ModuleException(
|
||||
debugMessage,
|
||||
cause!!.javaClass.name + ": " + cause!!.message!!.replaceEach(search, obfuscate),
|
||||
this
|
||||
)
|
||||
} else if (message != null) {
|
||||
sanitizedException = ModuleException(debugMessage, message!!.replaceEach(search, obfuscate), this)
|
||||
} else if (!message.isNullOrBlank()) {
|
||||
return ModuleException(debugMessage, message!!.replaceEach(search, obfuscate), this)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sanitizedException
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -31,12 +31,14 @@
|
|||
*/
|
||||
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 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 java.io.FileNotFoundException
|
||||
import java.net.MalformedURLException
|
||||
import java.net.UnknownHostException
|
||||
|
@ -48,26 +50,24 @@ class FeedReaderTest {
|
|||
@Test
|
||||
fun readFeedTest() {
|
||||
var messages = readFeed("https://feeds.thauvin.net/ethauvin")
|
||||
assertThat(messages.size).describedAs("messages = 10").isEqualTo(10)
|
||||
assertThat(messages[1].msg).describedAs("feed entry url").contains("ethauvin")
|
||||
assertThat(messages.size, "size = 10").isEqualTo(10)
|
||||
assertThat(messages[1].msg, "feed entry url").contains("ethauvin")
|
||||
|
||||
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)
|
||||
assertThat(messages.size).describedAs("messages = 84").isEqualTo(84)
|
||||
assertThat(messages.last().msg).describedAs("example entry url").contains("http://example.com/test/")
|
||||
assertThat(messages.size, "messages = 84").isEqualTo(84)
|
||||
assertThat(messages.last().msg, "example entry url").contains("http://example.com/test/")
|
||||
|
||||
assertThatThrownBy { readFeed("blah") }.describedAs("invalid URL")
|
||||
.isInstanceOf(MalformedURLException::class.java)
|
||||
assertThat { readFeed("blah") }.isFailure().isInstanceOf(MalformedURLException::class.java)
|
||||
|
||||
assertThatThrownBy { readFeed("https://www.example.com") }.describedAs("not a feed")
|
||||
.isInstanceOf(FeedException::class.java)
|
||||
assertThat { readFeed("https://www.example.com") }.isFailure().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)
|
||||
|
||||
assertThatThrownBy { readFeed("https://www.doesnotexists.com") }.describedAs("unknown host")
|
||||
assertThat { readFeed("https://www.doesnotexists.com") }.isFailure()
|
||||
.isInstanceOf(UnknownHostException::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* PinboardUtilsTest.kt
|
||||
* PinboardTest.kt
|
||||
*
|
||||
* Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -32,45 +32,39 @@
|
|||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.PinboardUtils.toTimestamp
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import net.thauvin.erik.pinboard.PinboardPoster
|
||||
import org.testng.Assert.assertFalse
|
||||
import org.testng.Assert.assertTrue
|
||||
import org.testng.annotations.Test
|
||||
import java.net.URL
|
||||
import java.util.Date
|
||||
|
||||
class PinboardUtilsTest : LocalProperties() {
|
||||
class PinboardTest : LocalProperties() {
|
||||
private val pinboard = Pinboard()
|
||||
|
||||
@Test
|
||||
fun pinboardTest() {
|
||||
fun testPinboard() {
|
||||
val apiToken = getProperty("pinboard-api-token")
|
||||
val pinboard = PinboardPoster(apiToken)
|
||||
val url = "https://www.example.com/"
|
||||
val ircServer = "irc.test.com"
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
PinboardUtils.deletePin(pinboard, entry)
|
||||
pinboard.deletePin(entry)
|
||||
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 {
|
||||
val response = Utils.urlReader(
|
||||
URL(
|
|
@ -31,6 +31,8 @@
|
|||
*/
|
||||
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.bold
|
||||
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.urlReader
|
||||
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jibble.pircbot.Colors
|
||||
import org.pircbotx.Colors
|
||||
import org.testng.annotations.BeforeClass
|
||||
import org.testng.annotations.Test
|
||||
import java.io.File
|
||||
|
@ -86,59 +87,59 @@ class UtilsTest {
|
|||
val dir = "dir"
|
||||
val sep = '/'
|
||||
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)
|
||||
assertThat(url.appendIfMissing(sep)).describedAs("appendIfMissing(url)").isEqualTo("$url$sep")
|
||||
assertThat("$url$sep".appendIfMissing(sep)).describedAs("appendIfMissing($url$sep)").isEqualTo("$url$sep")
|
||||
assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep")
|
||||
assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBold() {
|
||||
assertThat(bold(1)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD)
|
||||
assertThat(bold(2L)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD)
|
||||
assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
||||
assertThat(bold("test")).describedAs("bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD)
|
||||
assertThat(bold(1), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD)
|
||||
assertThat(bold(2L), "bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD)
|
||||
assertThat(bold(ascii), "bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
||||
assertThat(bold("test"), "bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBuildCmdSyntax() {
|
||||
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")
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testCapitalise() {
|
||||
assertThat("test".capitalise()).describedAs("capitalize(test)").isEqualTo("Test")
|
||||
assertThat("Test".capitalise()).describedAs("capitalize(Test)").isEqualTo("Test")
|
||||
assertThat(test.capitalise()).describedAs("capitalize($test)").isEqualTo(test)
|
||||
assertThat("".capitalise()).describedAs("capitalize()").isEqualTo("")
|
||||
assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test")
|
||||
assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test")
|
||||
assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test)
|
||||
assertThat("".capitalise(), "capitalize()").isEqualTo("")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun textCapitaliseWords() {
|
||||
assertThat(test.capitalizeWords()).describedAs("captiatlizeWords(test)").isEqualTo("This Is A Test.")
|
||||
assertThat("Already Capitalized".capitalizeWords()).describedAs("already capitalized")
|
||||
assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.")
|
||||
assertThat("Already Capitalized".capitalizeWords(), "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
|
||||
fun testColorize() {
|
||||
assertThat(colorize(ascii, Colors.REVERSE)).describedAs("colorize(reverse)").isEqualTo(
|
||||
assertThat(colorize(ascii, Colors.REVERSE), "colorize(reverse)").isEqualTo(
|
||||
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)
|
||||
assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)")
|
||||
assertThat(colorize(ascii, Colors.BOLD), "colorized(bold)")
|
||||
.isEqualTo(Colors.BOLD + ascii + Colors.BOLD)
|
||||
assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo("")
|
||||
assertThat(colorize("", Colors.RED)).describedAs("colorize()").isEqualTo("")
|
||||
assertThat(colorize(ascii, DEFAULT_COLOR)).describedAs("colorize(none)").isEqualTo(ascii)
|
||||
assertThat(colorize(" ", Colors.NORMAL)).describedAs("colorize(blank)")
|
||||
assertThat(colorize(null, Colors.RED), "colorize(null)").isEqualTo("")
|
||||
assertThat(colorize("", Colors.RED), "colorize()").isEqualTo("")
|
||||
assertThat(colorize(ascii, DEFAULT_COLOR), "colorize(none)").isEqualTo(ascii)
|
||||
assertThat(colorize(" ", Colors.NORMAL), "colorize(blank)")
|
||||
.isEqualTo(Colors.NORMAL + " " + Colors.NORMAL)
|
||||
}
|
||||
|
||||
|
@ -157,9 +158,9 @@ class UtilsTest {
|
|||
val p = Properties()
|
||||
p["one"] = "1"
|
||||
p["two"] = "two"
|
||||
assertThat(p.getIntProperty("one", 9)).describedAs("getIntProperty(one)").isEqualTo(1)
|
||||
assertThat(p.getIntProperty("two", 2)).describedAs("getIntProperty(two)").isEqualTo(2)
|
||||
assertThat(p.getIntProperty("foo", 3)).describedAs("getIntProperty(foo)").isEqualTo(3)
|
||||
assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1)
|
||||
assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2)
|
||||
assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -169,26 +170,26 @@ class UtilsTest {
|
|||
|
||||
@Test
|
||||
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}")
|
||||
assertThat(helpFormat(test, isBold = false, isIndent = true)).describedAs("indent")
|
||||
assertThat(helpFormat(test, isBold = false, isIndent = true), "indent")
|
||||
.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())
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsoLocalDate() {
|
||||
assertThat(cal.time.toIsoLocalDate()).describedAs("isoLocalDate(date)").isEqualTo("1952-02-17")
|
||||
assertThat(localDateTime.toIsoLocalDate()).describedAs("isoLocalDate(localDate)").isEqualTo("1952-02-17")
|
||||
assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17")
|
||||
assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObfuscate() {
|
||||
assertThat(ascii.obfuscate().length).describedAs("obfuscate is right length").isEqualTo(ascii.length)
|
||||
assertThat(ascii.obfuscate()).describedAs("obfuscate()").isEqualTo("x".repeat(ascii.length))
|
||||
assertThat(" ".obfuscate()).describedAs("obfuscate(blank)").isEqualTo(" ")
|
||||
assertThat(ascii.obfuscate().length, "obfuscate is right length").isEqualTo(ascii.length)
|
||||
assertThat(ascii.obfuscate(), "obfuscate()").isEqualTo("x".repeat(ascii.length))
|
||||
assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -197,7 +198,7 @@ class UtilsTest {
|
|||
val weeks = "weeks"
|
||||
|
||||
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() {
|
||||
val search = arrayOf("one", "two", "three")
|
||||
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(","))
|
||||
|
||||
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"))
|
||||
|
||||
assertThat(test.replaceEach(search, emptyArray())).describedAs("replaceEach(search, empty)")
|
||||
assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)")
|
||||
.isEqualTo(test)
|
||||
}
|
||||
|
||||
|
@ -234,8 +235,8 @@ class UtilsTest {
|
|||
|
||||
@Test
|
||||
fun testToIntOrDefault() {
|
||||
assertThat("10".toIntOrDefault(1)).describedAs("toIntOrDefault(10, 1)").isEqualTo(10)
|
||||
assertThat("a".toIntOrDefault(2)).describedAs("toIntOrDefault(a, 2)").isEqualTo(2)
|
||||
assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10)
|
||||
assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -247,26 +248,26 @@ class UtilsTest {
|
|||
|
||||
@Test
|
||||
fun testUptime() {
|
||||
assertThat(uptime(547800300076L)).describedAs("full")
|
||||
assertThat(uptime(547800300076L), "full")
|
||||
.isEqualTo("17 years 2 months 2 weeks 1 day 6 hours 45 minutes")
|
||||
assertThat(uptime(2700000L)).describedAs("minutes").isEqualTo("45 minutes")
|
||||
assertThat(uptime(24300000L)).describedAs("hours minutes").isEqualTo("6 hours 45 minutes")
|
||||
assertThat(uptime(110700000L)).describedAs("days hours minutes").isEqualTo("1 day 6 hours 45 minutes")
|
||||
assertThat(uptime(1320300000L)).describedAs("weeks days hours minutes")
|
||||
assertThat(uptime(2700000L), "minutes").isEqualTo("45 minutes")
|
||||
assertThat(uptime(24300000L), "hours minutes").isEqualTo("6 hours 45 minutes")
|
||||
assertThat(uptime(110700000L), "days hours minutes").isEqualTo("1 day 6 hours 45 minutes")
|
||||
assertThat(uptime(1320300000L), "weeks days hours 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
|
||||
@Throws(IOException::class)
|
||||
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}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUtcDateTime() {
|
||||
assertThat(cal.time.toUtcDateTime()).describedAs("utcDateTime(date)").isEqualTo("1952-02-17 12:30")
|
||||
assertThat(localDateTime.toUtcDateTime()).describedAs("utcDateTime(localDate)").isEqualTo("1952-02-17 12:30")
|
||||
assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").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
|
||||
|
||||
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 java.time.Duration
|
||||
import java.time.LocalDateTime
|
||||
|
@ -51,18 +56,21 @@ class TellMessageTest {
|
|||
val recipient = "recipient"
|
||||
val sender = "sender"
|
||||
val tellMessage = TellMessage(sender, recipient, message)
|
||||
assertThat(tellMessage).extracting("sender", "recipient", "message")
|
||||
.containsExactly(sender, recipient, message)
|
||||
assertThat(isValidDate(tellMessage.queued)).describedAs("queued is valid date/time").isTrue
|
||||
assertThat(tellMessage.isMatch(sender)).describedAs("match sender").isTrue
|
||||
assertThat(tellMessage.isMatch(recipient)).describedAs("match recipient").isTrue
|
||||
assertThat(tellMessage.isMatch("foo")).describedAs("foo is no match").isFalse
|
||||
assertThat(tellMessage).all {
|
||||
prop(TellMessage::sender).isEqualTo(sender)
|
||||
prop(TellMessage::recipient).isEqualTo(recipient)
|
||||
prop(TellMessage::message).isEqualTo(message)
|
||||
}
|
||||
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
|
||||
assertThat(tellMessage.receptionDate).describedAs("reception date not set").isEqualTo(LocalDateTime.MIN)
|
||||
assertThat(tellMessage.receptionDate, "reception date not set").isEqualTo(LocalDateTime.MIN)
|
||||
tellMessage.isReceived = true
|
||||
assertThat(tellMessage.isReceived).describedAs("is received").isTrue
|
||||
assertThat(isValidDate(tellMessage.receptionDate)).describedAs("received is valid date/time").isTrue
|
||||
assertThat(tellMessage.isReceived, "is received").isTrue()
|
||||
assertThat(isValidDate(tellMessage.receptionDate), "received is valid date/time").isTrue()
|
||||
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
|
||||
|
||||
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.SyndCategoryImpl
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.testng.annotations.Test
|
||||
import java.security.SecureRandom
|
||||
import java.util.Date
|
||||
|
@ -58,21 +65,30 @@ class EntryLinkTest {
|
|||
entryLink.addComment("c$i", "u$i")
|
||||
i++
|
||||
}
|
||||
assertThat(entryLink.comments.size).describedAs("getComments().size() == 5").isEqualTo(i)
|
||||
assertThat(entryLink.comments.size, "getComments().size() == 5").isEqualTo(i)
|
||||
i = 0
|
||||
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++
|
||||
}
|
||||
|
||||
val r = SecureRandom()
|
||||
while (entryLink.comments.size > 0) {
|
||||
entryLink.deleteComment(r.nextInt(entryLink.comments.size))
|
||||
}
|
||||
assertThat(entryLink.comments).describedAs("hasComments()").isEmpty()
|
||||
assertThat(entryLink.comments, "hasComments()").isEmpty()
|
||||
entryLink.addComment("nothing", "nobody")
|
||||
entryLink.setComment(0, "something", "somebody")
|
||||
assertThat(entryLink.getComment(0)).describedAs("get first comment").extracting("nick", "comment")
|
||||
.containsExactly("somebody", "something")
|
||||
val comment = entryLink.getComment(0)
|
||||
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
|
||||
|
@ -80,19 +96,19 @@ class EntryLinkTest {
|
|||
val tag = "test"
|
||||
val tags = listOf(SyndCategoryImpl().apply { name = tag })
|
||||
val link = EntryLink("link", "title", "nick", "channel", Date(), tags)
|
||||
assertThat(link.tags.size).describedAs("check tag size").isEqualTo(tags.size)
|
||||
assertThat(link.tags[0].name).describedAs("check tag name").isEqualTo(tag)
|
||||
assertThat(link.pinboardTags).describedAs("check pinboard tags").isEqualTo("nick,$tag")
|
||||
assertThat(link.tags.size, "check tag size").isEqualTo(tags.size)
|
||||
assertThat(link.tags[0].name, "check tag name").isEqualTo(tag)
|
||||
assertThat(link.pinboardTags, "check pinboard tags").isEqualTo("nick,$tag")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMatches() {
|
||||
assertThat(entryLink.matches("mobitopia")).describedAs("match mobitopia").isTrue
|
||||
assertThat(entryLink.matches("skynx")).describedAs("match nick").isTrue
|
||||
assertThat(entryLink.matches("www.mobitopia.org")).describedAs("match url").isTrue
|
||||
assertThat(entryLink.matches("foo")).describedAs("match foo").isFalse
|
||||
assertThat(entryLink.matches("")).describedAs("match empty").isFalse
|
||||
assertThat(entryLink.matches(null)).describedAs("match null").isFalse
|
||||
assertThat(entryLink.matches("mobitopia"), "match mobitopia").isTrue()
|
||||
assertThat(entryLink.matches("skynx"), "match nick").isTrue()
|
||||
assertThat(entryLink.matches("www.mobitopia.org"), "match url").isTrue()
|
||||
assertThat(entryLink.matches("foo"), "match foo").isFalse()
|
||||
assertThat(entryLink.matches("<empty>"), "match empty").isFalse()
|
||||
assertThat(entryLink.matches(null), "match null").isFalse()
|
||||
}
|
||||
|
||||
|
||||
|
@ -100,20 +116,19 @@ class EntryLinkTest {
|
|||
fun testTags() {
|
||||
val tags: List<SyndCategory> = entryLink.tags
|
||||
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).describedAs("hasTags() is true").isNotEmpty
|
||||
assertThat(entryLink.tags, "size is 5").size().isEqualTo(5)
|
||||
entryLink.setTags("-tag5")
|
||||
entryLink.setTags("+mobitopia")
|
||||
entryLink.setTags("tag4")
|
||||
entryLink.setTags("-mobitopia")
|
||||
assertThat(entryLink.pinboardTags).describedAs("getPinboardTags()")
|
||||
assertThat(entryLink.pinboardTags, "getPinboardTags()")
|
||||
.isEqualTo(entryLink.nick + ",tag1,tag2,tag3,tag4,mobitopia")
|
||||
val size = entryLink.tags.size
|
||||
entryLink.setTags("")
|
||||
assertThat(entryLink.tags.size).describedAs("empty tag").isEqualTo(size)
|
||||
assertThat(entryLink.tags.size, "empty tag").isEqualTo(size)
|
||||
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
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFailure
|
||||
import assertk.assertions.isInstanceOf
|
||||
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -44,11 +46,11 @@ import org.testng.annotations.Test
|
|||
class CalcTest {
|
||||
@Test
|
||||
fun testCalculate() {
|
||||
assertThat(calculate("1 + 1")).describedAs("calculate(1+1)").isEqualTo("1+1 = %s", bold(2))
|
||||
assertThat(calculate("1 -3")).describedAs("calculate(1 -3)").isEqualTo("1-3 = %s", bold(-2))
|
||||
assertThat(calculate("pi+π+e+φ")).describedAs("calculate(pi+π+e+φ)")
|
||||
.isEqualTo("pi+π+e+φ = %s", bold("10.62"))
|
||||
assertThatThrownBy { calculate("one + one") }.describedAs("calculate(one+one)")
|
||||
.isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
||||
assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${bold(2)}")
|
||||
assertThat(calculate("1 -3"), "calculate(1 -3)").isEqualTo("1-3 = ${bold(-2)}")
|
||||
assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)")
|
||||
.isEqualTo("pi+π+e+φ = ${bold("10.62")}")
|
||||
assertThat { calculate("one + one") }
|
||||
.isFailure().isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,13 @@
|
|||
*/
|
||||
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 org.assertj.core.api.Assertions.assertThat
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
|
@ -43,11 +48,17 @@ class CryptoPricesTest {
|
|||
@Throws(ModuleException::class)
|
||||
fun testMarketPrice() {
|
||||
var price = currentPrice(listOf("BTC"))
|
||||
assertThat(price).extracting("base", "currency").containsExactly("BTC", "USD")
|
||||
assertThat(price.amount.signum()).describedAs("BTC > 0").isGreaterThan(0)
|
||||
assertThat(price, "BTC in USD").all {
|
||||
prop(CryptoPrice::base).isEqualTo("BTC")
|
||||
prop(CryptoPrice::currency).isEqualTo("USD")
|
||||
prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0)
|
||||
}
|
||||
|
||||
price = currentPrice(listOf("ETH", "EUR"))
|
||||
assertThat(price).extracting("base", "currency").containsExactly("ETH", "EUR")
|
||||
assertThat(price.amount.signum()).describedAs("ETH > 0").isGreaterThan(0)
|
||||
assertThat(price, "ETH in EUR").all {
|
||||
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
|
||||
|
||||
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.currencyRates
|
||||
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.Test
|
||||
|
||||
|
@ -50,14 +61,28 @@ class CurrencyConverterTest {
|
|||
|
||||
@Test
|
||||
fun testConvertCurrency() {
|
||||
assertThat(convertCurrency("100 USD to EUR").msg)
|
||||
.describedAs("100 USD to EUR").matches("\\$100\\.00 = €\\d{2,3}\\.\\d{2}")
|
||||
assertThat(convertCurrency("100 USD to USD").msg).describedAs("100 USD to USD")
|
||||
.contains("You're kidding, right?")
|
||||
assertThat(convertCurrency("100 USD").msg).describedAs("100 USD").contains("Invalid query.")
|
||||
assertThat(
|
||||
convertCurrency("100 USD to EUR").msg,
|
||||
"100 USD to EUR"
|
||||
).matches("\\$100\\.00 = €\\d{2,3}\\.\\d{2}".toRegex())
|
||||
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()
|
||||
assertThat(rates.size).describedAs("currencyRates.size == 33").isEqualTo(33)
|
||||
assertThat(rates).describedAs("currencyRates(EUR< USD)").contains("EUR: 1")
|
||||
.anyMatch { it.matches("USD: .*".toRegex()) }
|
||||
assertThat(rates).all {
|
||||
size().isEqualTo(33)
|
||||
any { it.matches("[A-Z]{3}: +[\\d.]+".toRegex()) }
|
||||
contains("EUR: 1")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,14 +33,15 @@
|
|||
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
|
||||
|
||||
class DiceTest {
|
||||
@Test
|
||||
fun testWinLoseOrTie() {
|
||||
assertThat(Dice.winLoseOrTie(6, 6)).describedAs("6 vs. 6").isEqualTo(Dice.Result.TIE)
|
||||
assertThat(Dice.winLoseOrTie(6, 5)).describedAs("6 vs. 5").isEqualTo(Dice.Result.WIN)
|
||||
assertThat(Dice.winLoseOrTie(5, 6)).describedAs("5 vs. 6").isEqualTo(Dice.Result.LOSE)
|
||||
assertThat(Dice.winLoseOrTie(6, 6), "6 vs. 6").isEqualTo(Dice.Result.TIE)
|
||||
assertThat(Dice.winLoseOrTie(6, 5), "6 vs. 5").isEqualTo(Dice.Result.WIN)
|
||||
assertThat(Dice.winLoseOrTie(5, 6), "5 vs. 6").isEqualTo(Dice.Result.LOSE)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,11 +31,20 @@
|
|||
*/
|
||||
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.Sanitize.sanitizeException
|
||||
import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
|
@ -49,23 +58,33 @@ class GoogleSearchTest : LocalProperties() {
|
|||
val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP)
|
||||
try {
|
||||
var messages = searchGoogle("mobitopia", apiKey, cseKey)
|
||||
assertThat(messages).describedAs("mobitopia results not empty").isNotEmpty
|
||||
assertThat(messages[0].msg).describedAs("found mobibtopia").containsIgnoringCase("mobitopia")
|
||||
assertThat(messages, "mobitopia results not empty").isNotEmpty()
|
||||
assertThat(messages[0].msg, "found mobibtopia").contains("mobitopia", true)
|
||||
|
||||
messages = searchGoogle("aapl", apiKey, cseKey)
|
||||
assertThat(messages).describedAs("aapl results not empty").isNotEmpty
|
||||
assertThat(messages[0].msg).describedAs("found apple").containsIgnoringCase("apple")
|
||||
assertThatThrownBy { searchGoogle("test", "", "apiKey") }
|
||||
.describedAs("no API key")
|
||||
assertThat(messages, "aapl results not empty").isNotEmpty()
|
||||
assertThat(messages[0].msg, "found apple").contains("apple", true)
|
||||
|
||||
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()
|
||||
assertThatThrownBy { searchGoogle("test", "apiKey", "") }
|
||||
.describedAs("no CSE API key")
|
||||
|
||||
assertThat { searchGoogle("test", "apiKey", "") }.isFailure()
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
assertThatThrownBy { searchGoogle("", "apikey", "apiKey") }
|
||||
.describedAs("no query").isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
} catch (e: ModuleException) {
|
||||
// Avoid displaying api keys in CI logs
|
||||
if ("true" == System.getenv("CI")) {
|
||||
throw sanitizeException(e, apiKey, cseKey)
|
||||
throw e.sanitize(apiKey, cseKey)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
|
|
|
@ -31,8 +31,11 @@
|
|||
*/
|
||||
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 org.assertj.core.api.Assertions.assertThat
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
|
@ -42,7 +45,9 @@ class JokeTest {
|
|||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun testRandomJoke() {
|
||||
assertThat(randomJoke().msg).describedAs("randomJoke() > 0").isNotEmpty
|
||||
assertThat(randomJoke().msg).describedAs("randomJoke()").containsIgnoringCase("chuck")
|
||||
assertThat(randomJoke().msg, "randomJoke() > 0").all {
|
||||
isNotEmpty()
|
||||
contains("chuck", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,9 +31,11 @@
|
|||
*/
|
||||
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.whois
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
|
@ -44,14 +46,13 @@ class LookupTest {
|
|||
@Throws(Exception::class)
|
||||
fun testLookup() {
|
||||
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
|
||||
@Throws(Exception::class)
|
||||
fun testWhois() {
|
||||
val result = whois("17.178.96.59", Lookup.WHOIS_HOST)
|
||||
assertThat(result).describedAs("whois(17.178.96.59/Apple Inc.")
|
||||
.anyMatch { it.contains("Apple Inc.") }
|
||||
assertThat(result, "whois(17.178.96.59/Apple Inc.").any { it.contains("Apple Inc.") }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,16 @@
|
|||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Sanitize.sanitizeException
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import assertk.all
|
||||
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.Test
|
||||
import java.io.IOException
|
||||
|
@ -58,37 +66,41 @@ class ModuleExceptionTest {
|
|||
|
||||
@Test(dataProvider = "dp")
|
||||
fun testGetDebugMessage(e: ModuleException) {
|
||||
assertThat(e.debugMessage).describedAs("get debug message").isEqualTo(debugMessage)
|
||||
assertThat(e.debugMessage, "get debug message").isEqualTo(debugMessage)
|
||||
}
|
||||
|
||||
@Test(dataProvider = "dp")
|
||||
fun testGetMessage(e: ModuleException) {
|
||||
assertThat(e).describedAs("get message").hasMessage(message)
|
||||
assertThat(e, "get message").hasMessage(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSanitizeMessage() {
|
||||
val apiKey = "1234567890"
|
||||
var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me"))
|
||||
assertThat(sanitizeException(e, apiKey, "", "me")).describedAs("sanitized url")
|
||||
.hasMessageContainingAll("xxxxxxxxxx", "userID=xx", "java.io.IOException")
|
||||
.hasMessageNotContainingAny(apiKey, "me")
|
||||
assertThat(e.sanitize(apiKey, "", "me").message, "sanitized url").isNotNull().all {
|
||||
contains("xxxxxxxxxx", "userID=xx", "java.io.IOException")
|
||||
doesNotContain(apiKey, "me")
|
||||
}
|
||||
|
||||
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())
|
||||
assertThat(sanitizeException(e, apiKey)).describedAs("no cause message").hasMessage(message)
|
||||
assertThat(e.sanitize(apiKey), "no cause message").hasMessage(message)
|
||||
|
||||
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
|
||||
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"))
|
||||
assertThat(sanitizeException(e, " ", apiKey, "foo").message).describedAs("key in cause")
|
||||
.doesNotContain(apiKey).endsWith("xxx is xxxxxxxxxx")
|
||||
assertThat(e.sanitize(" ", apiKey, "foo").message, "key in cause").isNotNull().all {
|
||||
doesNotContain(apiKey)
|
||||
endsWith("xxx is xxxxxxxxxx")
|
||||
}
|
||||
assertThat(e.sanitize(), "empty").isEqualTo(e)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,10 @@
|
|||
*/
|
||||
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 org.assertj.core.api.Assertions.assertThat
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
|
@ -41,13 +43,13 @@ import org.testng.annotations.Test
|
|||
class PingTest {
|
||||
@Test
|
||||
fun testPingsArray() {
|
||||
assertThat(Ping.PINGS).describedAs("Pings array is not empty.").isNotEmpty
|
||||
assertThat(Ping.PINGS, "Pings array is not empty.").isNotEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRandomPing() {
|
||||
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
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import org.testng.annotations.Test
|
||||
|
||||
class RockPaperScissorsTest {
|
||||
@Test
|
||||
fun testWinLoseOrDraw() {
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper")).describedAs("scissors vs. paper")
|
||||
.isEqualTo("win")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock")).describedAs("paper vs. rock").isEqualTo("win")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors")).describedAs("rock vs. scissors")
|
||||
.isEqualTo("win")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors")).describedAs("paper vs. scissors")
|
||||
.isEqualTo("lose")
|
||||
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")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose")
|
||||
assertThat(RockPaperScissors.winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,11 +31,20 @@
|
|||
*/
|
||||
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.Sanitize.sanitizeException
|
||||
import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
|
@ -52,24 +61,25 @@ class StockQuoteTest : LocalProperties() {
|
|||
val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP)
|
||||
try {
|
||||
val messages = getQuote("apple inc", apiKey)
|
||||
assertThat(messages).describedAs("response not empty").isNotEmpty
|
||||
assertThat(messages[0].msg).describedAs("same stock symbol").matches("Symbol: AAPL .*")
|
||||
assertThat(messages[1].msg).describedAs("price label").matches(buildMatch("Price"))
|
||||
assertThat(messages[2].msg).describedAs("previous label").matches(buildMatch("Previous"))
|
||||
assertThat(messages[3].msg).describedAs("open label").matches(buildMatch("Open"))
|
||||
try {
|
||||
getQuote("blahfoo", apiKey)
|
||||
} catch (e: ModuleException) {
|
||||
assertThat(e.message).describedAs("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL)
|
||||
assertThat(messages, "response not empty").isNotEmpty()
|
||||
assertThat(messages[0].msg, "same stock symbol").matches("Symbol: AAPL .*".toRegex())
|
||||
assertThat(messages[1].msg, "price label").matches(buildMatch("Price").toRegex())
|
||||
assertThat(messages[2].msg, "previous label").matches(buildMatch("Previous").toRegex())
|
||||
assertThat(messages[3].msg, "open label").matches(buildMatch("Open").toRegex())
|
||||
|
||||
assertThat(getQuote("blahfoo", apiKey).first(), "invalid symbol").all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||
}
|
||||
assertThatThrownBy { getQuote("test", "") }.describedAs("no API key")
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
assertThatThrownBy { getQuote("", "apikey") }.describedAs("no symbol")
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
assertThat(getQuote("", "apikey").first(), "empty symbol").all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||
}
|
||||
assertThat { getQuote("test", "") }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
} catch (e: ModuleException) {
|
||||
// Avoid displaying api keys in CI logs
|
||||
if ("true" == System.getenv("CI")) {
|
||||
throw sanitizeException(e, apiKey)
|
||||
throw e.sanitize(apiKey)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
|
|
|
@ -31,9 +31,11 @@
|
|||
*/
|
||||
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.modules.Twitter.Companion.twitterPost
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.testng.annotations.Test
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
|
@ -56,7 +58,7 @@ class TwitterTest : LocalProperties() {
|
|||
@Throws(ModuleException::class)
|
||||
fun testPostTwitter() {
|
||||
val msg = "Testing Twitter API from $ci"
|
||||
assertThat(
|
||||
assertThat {
|
||||
twitterPost(
|
||||
getProperty(Twitter.CONSUMER_KEY_PROP),
|
||||
getProperty(Twitter.CONSUMER_SECRET_PROP),
|
||||
|
@ -65,7 +67,7 @@ class TwitterTest : LocalProperties() {
|
|||
getProperty(Twitter.HANDLE_PROP),
|
||||
msg,
|
||||
true
|
||||
).msg
|
||||
).describedAs("twitterPost($msg)").isEqualTo(msg)
|
||||
)
|
||||
}.isSuccess().isEqualTo(msg)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,16 @@
|
|||
*/
|
||||
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.core.OWM
|
||||
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.getWeather
|
||||
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 kotlin.random.Random
|
||||
|
||||
|
@ -51,50 +59,63 @@ class Weather2Test : LocalProperties() {
|
|||
@Test
|
||||
fun testFtoC() {
|
||||
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
|
||||
fun testGetCountry() {
|
||||
assertThat(getCountry("foo")).describedAs("not a country").isEqualTo(OWM.Country.UNITED_STATES)
|
||||
assertThat(getCountry("fr")).describedAs("fr is france").isEqualTo(OWM.Country.FRANCE)
|
||||
assertThat(getCountry("foo"), "not a country").isEqualTo(OWM.Country.UNITED_STATES)
|
||||
assertThat(getCountry("fr"), "fr is france").isEqualTo(OWM.Country.FRANCE)
|
||||
|
||||
val country = OWM.Country.values()
|
||||
repeat(3) {
|
||||
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
|
||||
fun testMphToKmh() {
|
||||
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
|
||||
@Throws(ModuleException::class)
|
||||
fun testWeather() {
|
||||
var messages = getWeather("98204", getProperty(OWM_API_KEY_PROP))
|
||||
assertThat(messages[0].msg).describedAs("is Everett").contains("Everett, United States").contains("US")
|
||||
assertThat(messages[messages.size - 1].msg).describedAs("is Everett zip code").endsWith("98204%2CUS")
|
||||
assertThat(messages[0].msg, "is Everett").all {
|
||||
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))
|
||||
assertThat(messages[0].msg).describedAs("is San Francisco").contains("San Francisco").contains("US")
|
||||
assertThat(messages[messages.size - 1].msg).describedAs("is San Fran city code").endsWith("5391959")
|
||||
assertThat(messages[0].msg, "is San Francisco").all {
|
||||
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))
|
||||
assertThat(messages[0].msg).describedAs("is UK").contains("London, United Kingdom").contains("GB")
|
||||
assertThat(messages[messages.size - 1].msg).describedAs("is London city code").endsWith("2643743")
|
||||
assertThat(messages[0].msg, "is UK").all {
|
||||
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)) }
|
||||
.describedAs("foo not found").hasCauseInstanceOf(APIException::class.java)
|
||||
assertThatThrownBy { getWeather("test", "") }
|
||||
.describedAs("no API key").isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
assertThatThrownBy { getWeather("test", null) }
|
||||
.describedAs("null API key").isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
try {
|
||||
getWeather("Foo, US", getProperty(OWM_API_KEY_PROP))
|
||||
} catch (e: ModuleException) {
|
||||
assertThat(e.cause, "cause is API exception").isNotNull().isInstanceOf(APIException::class.java)
|
||||
}
|
||||
|
||||
assertThat { getWeather("test", "") }.isFailure()
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
assertThat { getWeather("test", null) }.isFailure()
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
|
||||
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
|
||||
|
||||
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.modules.WorldTime.Companion.BEATS_KEYWORD
|
||||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP
|
||||
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 java.time.ZoneId
|
||||
import java.time.zone.ZoneRulesException
|
||||
|
||||
/**
|
||||
* The `WordTimeTest` class.
|
||||
|
@ -46,16 +50,23 @@ import java.time.zone.ZoneRulesException
|
|||
class WordTimeTest {
|
||||
@Test
|
||||
fun testTime() {
|
||||
assertThat(time("PST").msg).describedAs("PST").endsWith(bold("Los Angeles"))
|
||||
assertThat(time("BLAH").isError).describedAs("BLAH").isTrue
|
||||
assertThat(time("BEAT").msg).describedAs(BEATS_KEYWORD).matches("[\\w ]+: .?@\\d{3}+.? .beats")
|
||||
assertThat(time(), "no zone").matches(
|
||||
("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " +
|
||||
"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
|
||||
@Throws(ZoneRulesException::class)
|
||||
fun testCountries() {
|
||||
fun testZones() {
|
||||
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
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isTrue
|
||||
import org.testng.annotations.Test
|
||||
|
||||
class TestMessage {
|
||||
|
@ -41,15 +42,15 @@ class TestMessage {
|
|||
var msg = Message()
|
||||
|
||||
msg.isError = true
|
||||
assertThat(msg.isNotice).describedAs("message is notice").isTrue
|
||||
assertThat(msg.isNotice, "message is notice").isTrue()
|
||||
|
||||
msg = Message("foo", isError = true)
|
||||
assertThat(msg.isNotice).describedAs("message is notice too").isTrue
|
||||
assertThat(msg.isNotice, "message is notice too").isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testErrorMessage() {
|
||||
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
|
||||
#Wed Sep 15 17:27:25 PDT 2021
|
||||
version.buildmeta=1400
|
||||
#Mon Nov 08 13:50:12 PST 2021
|
||||
version.buildmeta=2220
|
||||
version.major=0
|
||||
version.minor=8
|
||||
version.patch=0
|
||||
version.prerelease=beta
|
||||
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