From 685dbd30eda0bf5b6db676d332c9e74e8d02060c Mon Sep 17 00:00:00 2001 From: cvs2svn Date: Mon, 13 Sep 2010 21:59:24 +0000 Subject: [PATCH 001/858] This commit was manufactured by cvs2svn to create tag 'v0_4_5'. Sprout from master 2010-09-13 21:59:23 UTC Erik C. Thauvin 'Added version command.' Delete: .cvsignore ChangeLog.txt README.txt ant/jreleaseinfo-1.3.0.jar build.properties build.xml lib/MathEvaluator.jar lib/commons-cli-1.1.jar lib/commons-codec-1.3.jar lib/commons-httpclient-3.1.jar lib/commons-logging-1.1.1.jar lib/commons-net-1.4.1.jar lib/delicious-1.14.jar lib/google.jar lib/googleapi.jar lib/jakarta-oro-2.0.8.jar lib/jdom-1.1.jar lib/jweather-0.2.5.jar lib/log4j-1.2.13.jar lib/pircbot.jar lib/rome-0.4.jar lib/rome-fetcher-0.4.jar lib/twitter4j-1.0.4.jar lib/ws-commons-util-1.0.2.jar lib/xmlrpc-client-3.1.jar lib/xmlrpc-common-3.1.jar licenses/Apache LICENSE.txt licenses/Google License.txt licenses/GoogleTagLib License.txt licenses/JDOM License.txt licenses/JWeather License.txt licenses/License.txt licenses/PircBot License.html licenses/ROME License.txt licenses/Twitter4J LICENSE.txt licenses/delicious-java License.txt mobibot.fb mobibot.iml properties/.cvsignore src/net/thauvin/erik/mobibot/CurrencyConverter.java src/net/thauvin/erik/mobibot/DeliciousPoster.java src/net/thauvin/erik/mobibot/EntryComment.java src/net/thauvin/erik/mobibot/EntryLink.java src/net/thauvin/erik/mobibot/FeedReader.java src/net/thauvin/erik/mobibot/GoogleSearch.java src/net/thauvin/erik/mobibot/Jaiku.java src/net/thauvin/erik/mobibot/StockQuote.java src/net/thauvin/erik/mobibot/SwingWorker.java src/net/thauvin/erik/mobibot/Twitter.java src/net/thauvin/erik/mobibot/Weather.java website/index.html website/simple.css --- .cvsignore | 8 - ChangeLog.txt | 294 ----------- README.txt | 23 - ant/jreleaseinfo-1.3.0.jar | Bin 23925 -> 0 bytes build.properties | 12 - build.xml | 52 -- lib/MathEvaluator.jar | Bin 5762 -> 0 bytes lib/commons-cli-1.1.jar | Bin 36174 -> 0 bytes lib/commons-codec-1.3.jar | Bin 46725 -> 0 bytes lib/commons-httpclient-3.1.jar | Bin 305001 -> 0 bytes lib/commons-logging-1.1.1.jar | Bin 60841 -> 0 bytes lib/commons-net-1.4.1.jar | Bin 180792 -> 0 bytes lib/delicious-1.14.jar | Bin 23859 -> 0 bytes lib/google.jar | Bin 20290 -> 0 bytes lib/googleapi.jar | Bin 624236 -> 0 bytes lib/jakarta-oro-2.0.8.jar | Bin 65261 -> 0 bytes lib/jdom-1.1.jar | Bin 153115 -> 0 bytes lib/jweather-0.2.5.jar | Bin 44542 -> 0 bytes lib/log4j-1.2.13.jar | Bin 358180 -> 0 bytes lib/pircbot.jar | Bin 76700 -> 0 bytes lib/rome-0.4.jar | Bin 161797 -> 0 bytes lib/rome-fetcher-0.4.jar | Bin 18430 -> 0 bytes lib/twitter4j-1.0.4.jar | Bin 86705 -> 0 bytes lib/ws-commons-util-1.0.2.jar | Bin 34407 -> 0 bytes lib/xmlrpc-client-3.1.jar | Bin 45124 -> 0 bytes lib/xmlrpc-common-3.1.jar | Bin 104038 -> 0 bytes licenses/Apache LICENSE.txt | 202 -------- licenses/Google License.txt | 150 ------ licenses/GoogleTagLib License.txt | 29 -- licenses/JDOM License.txt | 56 --- licenses/JWeather License.txt | 340 ------------- licenses/License.txt | 29 -- licenses/PircBot License.html | 60 --- licenses/ROME License.txt | 14 - licenses/Twitter4J LICENSE.txt | 24 - licenses/delicious-java License.txt | 29 -- mobibot.fb | 21 - mobibot.iml | 202 -------- properties/.cvsignore | 1 - .../erik/mobibot/CurrencyConverter.java | 231 --------- .../thauvin/erik/mobibot/DeliciousPoster.java | 154 ------ .../thauvin/erik/mobibot/EntryComment.java | 137 ------ src/net/thauvin/erik/mobibot/EntryLink.java | 459 ------------------ src/net/thauvin/erik/mobibot/FeedReader.java | 132 ----- .../thauvin/erik/mobibot/GoogleSearch.java | 169 ------- src/net/thauvin/erik/mobibot/Jaiku.java | 102 ---- src/net/thauvin/erik/mobibot/StockQuote.java | 158 ------ src/net/thauvin/erik/mobibot/SwingWorker.java | 131 ----- src/net/thauvin/erik/mobibot/Twitter.java | 93 ---- src/net/thauvin/erik/mobibot/Weather.java | 196 -------- website/index.html | 59 --- website/simple.css | 52 -- 52 files changed, 3619 deletions(-) delete mode 100644 .cvsignore delete mode 100644 ChangeLog.txt delete mode 100644 README.txt delete mode 100644 ant/jreleaseinfo-1.3.0.jar delete mode 100644 build.properties delete mode 100644 build.xml delete mode 100644 lib/MathEvaluator.jar delete mode 100644 lib/commons-cli-1.1.jar delete mode 100644 lib/commons-codec-1.3.jar delete mode 100644 lib/commons-httpclient-3.1.jar delete mode 100644 lib/commons-logging-1.1.1.jar delete mode 100644 lib/commons-net-1.4.1.jar delete mode 100644 lib/delicious-1.14.jar delete mode 100644 lib/google.jar delete mode 100644 lib/googleapi.jar delete mode 100644 lib/jakarta-oro-2.0.8.jar delete mode 100644 lib/jdom-1.1.jar delete mode 100644 lib/jweather-0.2.5.jar delete mode 100644 lib/log4j-1.2.13.jar delete mode 100644 lib/pircbot.jar delete mode 100644 lib/rome-0.4.jar delete mode 100644 lib/rome-fetcher-0.4.jar delete mode 100644 lib/twitter4j-1.0.4.jar delete mode 100644 lib/ws-commons-util-1.0.2.jar delete mode 100644 lib/xmlrpc-client-3.1.jar delete mode 100644 lib/xmlrpc-common-3.1.jar delete mode 100644 licenses/Apache LICENSE.txt delete mode 100644 licenses/Google License.txt delete mode 100644 licenses/GoogleTagLib License.txt delete mode 100644 licenses/JDOM License.txt delete mode 100644 licenses/JWeather License.txt delete mode 100644 licenses/License.txt delete mode 100644 licenses/PircBot License.html delete mode 100644 licenses/ROME License.txt delete mode 100644 licenses/Twitter4J LICENSE.txt delete mode 100644 licenses/delicious-java License.txt delete mode 100644 mobibot.fb delete mode 100644 mobibot.iml delete mode 100644 properties/.cvsignore delete mode 100644 src/net/thauvin/erik/mobibot/CurrencyConverter.java delete mode 100644 src/net/thauvin/erik/mobibot/DeliciousPoster.java delete mode 100644 src/net/thauvin/erik/mobibot/EntryComment.java delete mode 100644 src/net/thauvin/erik/mobibot/EntryLink.java delete mode 100644 src/net/thauvin/erik/mobibot/FeedReader.java delete mode 100644 src/net/thauvin/erik/mobibot/GoogleSearch.java delete mode 100644 src/net/thauvin/erik/mobibot/Jaiku.java delete mode 100644 src/net/thauvin/erik/mobibot/StockQuote.java delete mode 100644 src/net/thauvin/erik/mobibot/SwingWorker.java delete mode 100644 src/net/thauvin/erik/mobibot/Twitter.java delete mode 100644 src/net/thauvin/erik/mobibot/Weather.java delete mode 100644 website/index.html delete mode 100644 website/simple.css diff --git a/.cvsignore b/.cvsignore deleted file mode 100644 index 3369402..0000000 --- a/.cvsignore +++ /dev/null @@ -1,8 +0,0 @@ -DevSuite -build -dist -log4j.properties -mobibot.properties -fetcher.properties -*.ser -logs \ No newline at end of file diff --git a/ChangeLog.txt b/ChangeLog.txt deleted file mode 100644 index 744bba7..0000000 --- a/ChangeLog.txt +++ /dev/null @@ -1,294 +0,0 @@ -2005-11-08 14:58 erik - - * buildnum.properties, mobibot.iml, mobibot.ipr, mobibot.iws, - lib/delicious-1.6.jar, lib/delicious-1.7.jar, - lib/delicious-1.9.jar, src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java: Added ability to - set the port. Added NickServ registartion. Updated to delicious - 1.9 API. Update URL to mobitopia.org. - -2005-08-08 21:53 erik - - * lib/delicious-1.7.jar: Updated to delicious 1.7 - -2005-05-11 02:05 erik - - * buildnum.properties, mobibot.iml, mobibot.ipr, mobibot.iws, - lib/commons-httpclient-3.0-rc1.jar, - lib/commons-httpclient-3.0-rc2.jar, lib/delicious-1.5.jar, - lib/delicious-1.6.jar, - src/net/thauvin/erik/mobibot/ReleaseInfo.java: Updated to - commons-httpclient 3.0rc2 Updated to delicious 1.6 - -2005-05-10 22:47 erik - - * mobibot.iml, mobibot.iws, lib/commons-net-1.2.2.jar, - lib/commons-net-1.4.0.jar: Updated to commons-net 1.4.0 - -2005-05-05 12:47 erik - - * properties/mobibot.properties, - src/net/thauvin/erik/mobibot/CurrencyConverter.java, - src/net/thauvin/erik/mobibot/DeliciousPoster.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/Mobibot.java, buildnum.properties, - mobibot.fb, mobibot.ipr, mobibot.iws, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - src/net/thauvin/erik/mobibot/StockQuote.java: Updated locations. - -2005-03-06 13:04 erik - - * mobibot.iws, lib/delicious-1.4.jar: Update to delcious-java 1.5. - -2005-03-06 13:04 erik - - * lib/delicious-1.5.jar, buildnum.properties, mobibot.iml, - mobibot.iws, src/net/thauvin/erik/mobibot/ReleaseInfo.java: - Update to delicious-java 1.5. - -2005-03-06 08:30 erik - - * ChangeLog.txt: Updated ChangeLog. - -2005-03-06 08:28 erik - - * buildnum.properties, mobibot.iws, - src/net/thauvin/erik/mobibot/DeliciousPoster.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - src/net/thauvin/erik/mobibot/SwingWorker.java: Added threading - while posting to del.icio.us. - -2005-03-05 13:52 erik - - * ChangeLog.txt, mobibot.iws, licenses/delicious-java License.txt, - website/index.html: Added delicious-java license. Updated - ChangeLog. - -2005-03-05 13:40 erik - - * lib/commons-codec-1.3.jar, lib/commons-httpclient-2.0.1.jar, - lib/commons-httpclient-3.0-rc1.jar, lib/delicious-1.4.jar, - properties/mobibot.properties, build.properties, - buildnum.properties, mobibot.iml, mobibot.ipr, mobibot.iws, - src/net/thauvin/erik/mobibot/DeliciousPoster.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - website/index.html: Added support for del.icio.us - -2004-11-16 07:46 erik - - * buildnum.properties, mobibot.iws, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - website/index.html: Added the ability to ignore nicknames. - -2004-10-30 13:37 erik - - * buildnum.properties, mobibot.iws, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - website/index.html: Added the ability to ignore links from - specified nicknames. - -2004-10-04 07:22 erik - - * build.properties, buildnum.properties, mobibot.iws, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java: Added - action(channel, action) method. Added input validation to - action/send methods. - -2004-09-28 02:15 erik - - * lib/MathEvaluator.jar: Fixed a problem with the MathEvaluator - library where "atan(), asin(), acos()" never worked right. - -2004-09-27 18:36 erik - - * .cvsignore, build.properties, build.xml, buildnum.properties, - mobibot.iml, mobibot.ipr, mobibot.iws, - ant/jreleaseinfo-1.2.0.jar, lib/EXML.jar, lib/fetchrss.jar, - lib/jdom-1.0.jar, lib/jdom.jar, lib/pircbot.jar, - lib/rome-0.4.jar, lib/rome-fetcher-0.4.jar, lib/rsslibj.jar, - licenses/EXML-license.txt, licenses/ROME License.txt, - licenses/RSSJLib License.txt, licenses/fetchrss License.txt, - properties/fetcher.properties, - src/net/thauvin/erik/mobibot/CurrencyConverter.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/FeedReader.java, - src/net/thauvin/erik/mobibot/GoogleSearch.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/ReleaseInfo.java, - src/net/thauvin/erik/mobibot/StockQuote.java, - src/net/thauvin/erik/mobibot/Weather.java, website/index.html: - Rome is now used to create and read the various feed. - -2004-08-03 01:07 erik - - * lib/commons-httpclient-2.0-final.jar, build.xml, mobibot.iml, - mobibot.ipr, mobibot.iws, lib/commons-httpclient-2.0.1.jar, - src/net/thauvin/erik/mobibot/Mobibot.java: Commons HTTPClinet - 2.0.1 update. Added automated backup for the data file. - -2004-07-07 07:10 erik - - * lib/commons-logging.jar: Commons Logging 1.0.4 update. - -2004-07-07 07:10 erik - - * lib/pircbot.jar: PircBot 1.4.0 update. - -2004-07-07 07:10 erik - - * lib/: commons-net-1.2.0.jar, commons-net-1.2.2.jar: Commons Net - 1.2.2 update. - -2004-07-05 19:03 erik - - * lib/: jweather-0.2.4.jar, jweather-0.2.5.jar: jweather 0.2.5 - upgrade - -2004-05-03 10:53 erik - - * lib/: commons-net-1.1.0.jar, commons-net-1.2.0.jar, - jweather-0.2.3.jar, jweather-0.2.4.jar: commons-net 1.1.0 and - jweather 0.2.4 - -2004-03-10 09:05 erik - - * .cvsignore: Ignore all serial files. - -2004-03-10 09:04 erik - - * src/net/thauvin/erik/mobibot/Mobibot.java: Removed angled - brackets around URLs as it was breaking Trillian. Added pong - command. - -2004-03-10 09:03 erik - - * src/net/thauvin/erik/mobibot/Weather.java: Added invalid station - ID message. - -2004-03-10 09:03 erik - - * src/net/thauvin/erik/mobibot/: FeedReader.java, - GoogleSearch.java: Removed angled brackets around URLs as it was - breaking Trillian. - -2004-03-02 05:53 erik - - * mobibot.iws, src/net/thauvin/erik/mobibot/Mobibot.java: Now uses - setAutoNickChange() - -2004-03-02 05:52 erik - - * lib/pircbot.jar: PircBot 1.3.0 - -2004-02-25 17:21 erik - - * ChangeLog.txt: Initial import. - -2004-02-25 16:27 erik - - * mobibot.iws, src/net/thauvin/erik/mobibot/Mobibot.java: Added - random ping response. - -2004-02-25 04:12 erik - - * src/net/thauvin/erik/mobibot/: CurrencyConverter.java, - GoogleSearch.java, Mobibot.java, Weather.java: Added -serial - command line argument. Added ability to search the current URL - posts. Added ping command. Added more efficient arguments - parsing in the public and private commands. Added ability for - the originator to modify a post's URL. Removed the various - URL-based attributes from the constructor. Fixed the nick - command. - -2004-02-24 05:09 erik - - * README.txt: The properties file can now be specified from the - command line. - -2004-02-24 04:58 erik - - * website/index.html: Added reference to Commons CLI. - -2004-02-24 04:56 erik - - * mobibot.iml, mobibot.iws, lib/commons-cli-1.0.jar, - src/net/thauvin/erik/mobibot/Mobibot.java: The properties file - can now be specified from the command line. - -2004-02-24 04:55 erik - - * src/net/thauvin/erik/mobibot/CurrencyConverter.java: Added the - ability to list the current rates. - -2004-02-18 03:40 erik - - * src/net/thauvin/erik/mobibot/: Mobibot.java, Weather.java: The - weather command help is now returned when a station id is not - specified. - -2004-02-17 06:10 erik - - * README.txt: Added (very) minimal instructions. - -2004-02-17 03:22 erik - - * website/index.html: Added wiki reference. - -2004-02-16 20:04 erik - - * .cvsignore, build.properties, build.xml, mobibot.fb, mobibot.iml, - mobibot.ipr, mobibot.iws, lib/EXML.jar, - lib/commons-httpclient-2.0-final.jar, lib/commons-logging.jar, - lib/commons-net-1.1.0.jar, lib/fetchrss.jar, lib/google.jar, - lib/MathEvaluator.jar, lib/googleapi.jar, - lib/jakarta-oro-2.0.8.jar, lib/jdom.jar, lib/jweather-0.2.3.jar, - lib/log4j-1.2.8.jar, lib/pircbot.jar, lib/rsslibj.jar, - licenses/Commons License.txt, licenses/EXML-license.txt, - licenses/Google License.txt, licenses/GoogleTagLib License.txt, - licenses/JDOM License.txt, licenses/JWeather License.txt, - licenses/License.txt, licenses/Log4j License.txt, - licenses/PircBot License.html, licenses/RSSJLib License.txt, - licenses/fetchrss License.txt, properties/log4j.properties, - properties/mobibot.properties, - src/net/thauvin/erik/mobibot/CurrencyConverter.java, - src/net/thauvin/erik/mobibot/EntryComment.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/FeedReader.java, - src/net/thauvin/erik/mobibot/GoogleSearch.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/StockQuote.java, - src/net/thauvin/erik/mobibot/Weather.java, website/index.html, - website/simple.css: Initial import. - -2004-02-16 20:04 erik - - * .cvsignore, build.properties, build.xml, mobibot.fb, mobibot.iml, - mobibot.ipr, mobibot.iws, lib/EXML.jar, - lib/commons-httpclient-2.0-final.jar, lib/commons-logging.jar, - lib/commons-net-1.1.0.jar, lib/fetchrss.jar, lib/google.jar, - lib/MathEvaluator.jar, lib/googleapi.jar, - lib/jakarta-oro-2.0.8.jar, lib/jdom.jar, lib/jweather-0.2.3.jar, - lib/log4j-1.2.8.jar, lib/pircbot.jar, lib/rsslibj.jar, - licenses/Commons License.txt, licenses/EXML-license.txt, - licenses/Google License.txt, licenses/GoogleTagLib License.txt, - licenses/JDOM License.txt, licenses/JWeather License.txt, - licenses/License.txt, licenses/Log4j License.txt, - licenses/PircBot License.html, licenses/RSSJLib License.txt, - licenses/fetchrss License.txt, properties/log4j.properties, - properties/mobibot.properties, - src/net/thauvin/erik/mobibot/CurrencyConverter.java, - src/net/thauvin/erik/mobibot/EntryComment.java, - src/net/thauvin/erik/mobibot/EntryLink.java, - src/net/thauvin/erik/mobibot/FeedReader.java, - src/net/thauvin/erik/mobibot/GoogleSearch.java, - src/net/thauvin/erik/mobibot/Mobibot.java, - src/net/thauvin/erik/mobibot/StockQuote.java, - src/net/thauvin/erik/mobibot/Weather.java, website/index.html, - website/simple.css: Initial revision - diff --git a/README.txt b/README.txt deleted file mode 100644 index 804b473..0000000 --- a/README.txt +++ /dev/null @@ -1,23 +0,0 @@ -Some very basic instructions: - - ant jar - - mkdir run - - cp dist/mobibot.jar run - cp -R lib run - cp properties/*.properties run - - cd run - - mkdir logs - - { configure the properties } - vi *.properties - - { help } - java -jar mobibot.jar -h - - { launch } - /usr/bin/nohup java -jar mobibot.jar & - \ No newline at end of file diff --git a/ant/jreleaseinfo-1.3.0.jar b/ant/jreleaseinfo-1.3.0.jar deleted file mode 100644 index 0c00c5f78cf6bdde3832614f6ea4584017fe5fe6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23925 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 diff --git a/build.properties b/build.properties deleted file mode 100644 index daa4b7e..0000000 --- a/build.properties +++ /dev/null @@ -1,12 +0,0 @@ -# Project -proj.name=mobibot -proj.version=0.4 -proj.package=net.thauvin.erik.mobibot -proj.run=${proj.package}.Mobibot - -# Locations -path.classes=build -path.src=src -path.dist=dist -path.lib=lib -path.ant=ant \ No newline at end of file diff --git a/build.xml b/build.xml deleted file mode 100644 index d3386f6..0000000 --- a/build.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/MathEvaluator.jar b/lib/MathEvaluator.jar deleted file mode 100644 index 9011d7609bfb4d1f714823ed3bffa04d6c6137a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5762 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 diff --git a/lib/commons-cli-1.1.jar b/lib/commons-cli-1.1.jar deleted file mode 100644 index e633afbe6842aa92b1a8f0ff3f5b8c0e3283961b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36174 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 edFKBT8{e^?v~S{1Y(% diff --git a/lib/commons-codec-1.3.jar b/lib/commons-codec-1.3.jar deleted file mode 100644 index 957b6752af9a60c1bb2a4f65db0e90e5ce00f521..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46725 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 C?NEIH diff --git a/lib/commons-httpclient-3.1.jar b/lib/commons-httpclient-3.1.jar deleted file mode 100644 index 7c59774aed4f5dd08778489aaad565690ff7c132..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 305001 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 tc` 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 te}nOVCD`j|-apix?}<1@fDFJGl diff --git a/lib/commons-logging-1.1.1.jar b/lib/commons-logging-1.1.1.jar deleted file mode 100644 index 8758a96b70cfba9466bacca19c0d99b87cf53734..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60841 zcmbTd1CS`evMoBcZDWsZ&+M^n+qP}nwr$(Ctv$BizW1JY-aY^SxN-jLsOavB%8csH zTCplKt4dB17z7I7Kdvxf7qNq|ONMubl8A7T&yp?}5v97s9t|2E$N z0sx@=cQI)J8F3L|MI~Bkk!ZO|i$QvXF6c-2cffG=AUrS01+)a_;ynZpDnSjZf`y4O zhm3D;Yw*%*;^IHncwCL0$!3ye$h$&S)_qyV27=?<6@fg0pFz1eSs59yAf%HJjQ%H) zFIm%6<#2s1_Yh*^1@*K~Z7wDW*?`ll@6DUM$cw3eLK2c|aoqYy6tVkUSvkhm8Qt*f zOyK)5B2PlC;LC)%NYL`7Y#D(%;bAXy9EJx}IZX+%2t7~fDki_K(BcZ=bG83QG(v$P4|M<*XMw6@mV=+=`y-dlku@_6)#!Q@jEE zZyNys1pYrv3G&xQwhpHMwBr9;0{R~k`gZz;X2$=6Ao9No8roW0+uAt(4+_}--wIZ? zrl#gL|I;TC{LhN!)^=9^?DoH4(bRMhVhs-fphFD+fbduQpXiYi7ZR3H6yDa*ble<4 z^?9jXb0|?X2P?^DHpe~T_<4bFS)zPrE7Hems+ruCFruN21BXamcqN`h7o8~5!t+Lt8)sdXHsutSb z)!Wn6@oAsNM|)0Db@J(J`#u!@6&CWslQ6r?PEt91TA)gKT(Ae| zZ6#_m`&A|li`UF;Cdsg}V_%KVP!@$s+PBW56FX?#bplEb676E2;E4TC)bdZsS67xJ zRY*1th*d{35}J#q$Ji#JBEPjUagF$6IFex~ILTFV9H}^@h9BmhI^9Sgu#P}Ajx8)0 zZk0I{VKo#!mO@A0;$EwvYJbB*Q}uZl$xq(erT(ac+hDt!DfRh$YzrF#!+6E~e!nOa zow10h{ehtFy+WgE3&4x|EfQru*S9Mn7_kJT%;;$s%6B-mo-h?xyYd+{nMojDiqiT)tCz~LTP z9p^j1mQgY^lE9IOvtE!wAQkxn6Hnr#FYBR{R-eXaYPR-?PUz1Z8GHwpdo~VYca&=Yk@uQ_Q+E0*XQ^|rIv$#4c zPE}_E<~T}RPyth*nNy{`>un*^Q{CvTLlYr;G0`o>WT77#H3n1&R?~O0G>YC!y3y**b%FX1#x+!~)AKCqB{* z)xUK;(Pwnuh;`LssWdB$ekyf0p-`BibBa_}S-{1CZ<8}_ffaxr7A019z{sou{BB7h zMGYsajGr$fm&kWcDotm-K4{sBiAY`mWM}Sy@MRKJ5CpiGg@P9 zg7Wc|cq5XG0F>YDTaos&X0rj{;&ubs5ttXwNc7-EH6tFlL)Jj-k~+R#w}?kZ{cXH~ zcj#T%m&THIV19pZysR^IkZz)bw)z1vStrI4=eGiYfqoVoZS^bW8Ew`1H}A-drmh~^ zk%;QJoLOmqu@T`%=fS!HN;4dRb%KX%t|M#}=%`!HO1r}dbb&wwHRI1zU?$#DS9We%SSd|Lu! z`v>?&tB-LRK22}V0mh%mKOhFXKyv;;o#_Tp8(g33EiddCG`K95)j66QKElrez~{{u zP(BKGjR6syTmHE!PLC`LM!Js|s&k^pwr-mlmp!{GYVBB=3cI|2=hMoCy^aWSyc*lS zG4PMGjE3~Ox+Z__-25+G3*DSo^lPLIy+sOLM8I#q_}?nBlQHN)!1ke+l{96uB(@b< zy9Ti}5Y0BUDJ<`N&w_AqP==hG#&en~0yd=IQUs^iFOf=hM6E3&?Wsoe+#=>8N3x%o z+;qakFE&fSc+K*fv_N|Gd0qz~qGhi*ee%+J!`ZgYiCK&srwB2qpYuqHpbS#OCGi-) z6wO4uOM*m*kJISf{gv&V`%r{$g4N<_5n7;nVEWOkp%?08t1zH?(j&Q&WkXJyoT=a; z*JI=%?Bu|0nEDG%)$rOu%!#d7fyU%<2n+px*y@R7H%(~wWeL>dH3%8SpsH0Uo*k^Q z&cfSz!2ft4U;GK~I!cT|FxOl{Uq18!-m2ISgV5l3iryyt&c;zv@Mgaz=_zHzdfqbf zyx${}H~&*u=X;tTGmH@MEZ~YkzQxmFa9{w{cw4*@t((e(&P)6X_XSbF%%-i8Nj+a!JG=+U0mo{Cfupx96fQp98$*I z2d=Arm~N@b%%}8`zvemnQ6L@>OYN9k$;Imy(GaJU$wR%>SjB*^<{#Jlu&E-D+IiB0BF}g0bH>R(J(k679Q7b+NjNFp^tC$R6b<1~(0r6ot#=iq3Qmc(-5!8@c~Sl^#N z_%3F+X9!!E01gtRb*St=$FJ5-`9&UF!7isp@0Y+Vx=3E(rXVUzN6W!wRiNmC=wlht z-eEIz?g3J$F|_>FJUjn&3(qT5MywcHF-4H$$A&djQQhQK>$K(h+Uds!hyn(5fPlt? zr;RB%<4@u0LL3zGf^H$DJHQfRWp9Iq2`kf`;8hH(-qGzS(fR8+*eT(K*1 zWgA5`v~X$w_m5R}_g+LmR(k<$)#J~&qHlXPhMq?p^<3632R zkFBrSCFsvg5@*Xe%*A(3a!LK0@TED^Wc^DjhZh{#u<%Xu)>#PcY^s_&=v=N10goD( z*YaE)vQv-@7j|<{CN)GJAwH&)#ip5_ZgB_ofwe0G=JCk*tm)xbKseCMBX&2Tpy-6P zJs#o>%|HrpkN@78%cqwQy@$*MIGl07B$hPxM>s&cN{a(m_Wt3;tpvXZN8c0F%}V2@ zsn{=q7O!MKkPy=jH^S3ku%MB2?6c@Yi%HiLwPC~R5zWIxk<=;}_{bleP?5 z7}}g}WO$AJdh9{KPL`65OR`*CpMV0Zcb$VI1djf&h*br)!ea}POpiZE?fFD*C(;OA zpnZGOSN+&j;QoR3VIW^_2&cwF`W;nx%-RVn-%qe(0JcH54uR;in77+n)Dl&YX2E`4 zZ4X4K7t|EI*Kr-Pi%+i&x9gWJeS(wO4BVi_;-^Oq*(No8n-;=uKysp36E#X+VL79D zcwcZRt`%3e%L&W~hK;NOuKKLexq^tc?!k3|Q>nEYd)#SsJ-rKn0)~Nih3kgoL)Avi z80$>PSu>)xO-|A-*hn0$y=eJ_H9uJX%>BR4C8fx>3rK#;CKgb`d`e+>l4V)%2 zBwAmXWlAj1)!ahqjV!0nw>Qxc$`h>4M^I#oS}t0r=i&g938}_RQ^LzfF1FvD?2U_X z&H>WISz(QT@A>j)A2D7C={eAK$Ro4{p z)r@h!a+?qYIltoOy+OuDF9Id)t^e!H@$t(qqDNu~5UrliSME8t?Kjq>JO&Y^ zB6cF@FB73fMh?vNVEz;#h?m)Jhj+ZT4-4-vB&8Bk-GRB>1HA8SCX~eJC7E5j0m;Bm z|5A+CAF*EGJF>>=_M2gOb`)U+ByIpf z6=Y6^UT{5q99pHDJBAbT=mP6d&Pn1VI0v7AamK7Z2UN9aAx}PPsD=xder&Omwo?ws z4}y;N#tI`&_SFiYaSpn;)l2U~k2p5m24aqpQDJFml7_SZIJt?HAk5BVAz^NG4)Q>hLZ<14p82_~_vIvk9Bw#moSA*&*PfU8M&q#jWBYPa1&M;rc-b!FwCNT)?S z+WB^HL#j=Exs10#M3YqHJ=VsCT=ppBiOt)-R&Bz_4W0|*U$1t6ULFWc7vI;QXx{a9*&e}-Y<618km%a>KU*-|3zyUy}Pn* zRp*{Ojmny7)w0$#Vv;!xm$|yGW$YL|jcv6PwdVn1D}39+PhEL(6k$^w?WMy4I-1PMJ1>7uoy~iL&gVKI5t3Q~?qsyl{c$Ny3E+vtE@^(*Jd2B)MV^*`zRo za5gtFxwR(|e%T1LKVNU@;uid{O%JNlJ_kG0bWbW+CcVaJEL<(U7C)VE$)@?qgm@#b zl;^B7pS@2gVL{(vt|TR_z3evFY02i004mNN{D355AUAM`fx`aAf1)A0$_7(yRDLoc zExg^#9EgFIjVY_-N+BON2{|cdwDj9uRk(ouE}grBP^# z9jBiQ$;gN#ijZQTBP*IeJ}#J2ABcl>5GiL=9a8a+Ons(lYoCq(wz`YyTDHDx&NiqE zIz2VLeO7u2;=?s<^^g!knUr4DK4yt`(VZ@(na49ho!Siy?v0Y$rFma@NkPpiNXX^1 zWw&E`)uQMg-~|uHs*n)QbUOpgJUT0!KVT5TaX8Q|U@SV%2Zaw5WP{LAwW3H^PWHGb zg7_V*;J7`buHvttMI+C%Mp)|jY*Fl#d4E)Hf%qN^%$y-bzps(o0Wf@*KG+U8cFQ5q z-soGpurI%oN}lPLxqHO~pu>-_AiFT;7_Y0h>b4Br(_NgIc;?;*N(3WfK_pi zv>f1Z+xd=IR)5*~>6^n{LeBcwXZo%>;RNjledJ{Y)g1sbYmU!n=A3-15|5Sqjsq+G zu8@jLKMQEk;^IZRW5a~| zaYTiZz#^x=%^$^EM0vAVDU7%z#goLvqjblP3HQT@3MK(Xj~>O`2dNI3e%H*@#$;j= zwW1S{y-E1u6JNwdbJmA@vRt7w7jU(!Y5{a5?+^@p1GdhHIuL?q_r#w% z==!V>`Kz(FqS`_2hE;>P&WI<02=AW|zRSYwFTOpRKLj#n(J{;IKi}I(kFkMfde*7p3<4suDtxF0fpzy|{hVW*J$s@wWJ+H1u^wLHS-NPmhCW`Py2^KM967g!s5vMPEcyg3}hhQQkA-@4!|jW$_`LLchnq= zhQuh;FxP8k&uB0wqng8JrJ|aC|LM_0bwl#ll9M}(3P1mIS+9UI&_jA|M25<;Vd%lR zZ46)O!rcjD$;@%omLgp2>1i-#??T2DP6#^T2lOLHhR27(Ko*r2;D-%)Zh&)x#-y*I ziMj+zS6r`8C*DFHieVu>B!!`!{+btmu?%j=dS#>(d$p`s(si1JXSpOr)kc~i72R}p z=;5?v7EX3}XGD|@Z#^<JyR*<{Xn-B*TPrJ7MKIr=hwHx(UuwP|}Rq4BQfa$<3vA zg%VQAGS|K-qnL(6a*bhj-RgCH=QsV%(=EmciF5hEbV~aKLy-Y)Y9Z$Xa>?;)S z;Wkq(?TrORG-pIIWg(P<`lwfS;Q?p0&gdkH$X{A^qee>@ty4yV4opTcCL$;2g`Rf3 zh|PaUD(#80S42^bp}?IjDu#c0rgsa#c*W^} z9%~lVZYUB^V9R4mAv zzeUbkrVnIO>MGyInQ1&jQ6@`1OEtGOf2EkQe8t>Ru@@MewbvQu!rB?8Yg6rLmdWT{ zy2aaBy`?IBx{P!?b*?yI!}5u&#_|c>oqTt(cn$1|*#m~fm$-~I_$E0q+#9ia?}b9C zy!B$CQuD+t3sY)rce<}SXLzVnBvC<0q&GG!DL#+V)b7HF$jPiNQkkHr6vtMX{oFKG z-%)q#e?4m+7ErE*K;vn>)S@_ou7WAYV>PU|D;PDP^~_#u@OnnkRWM2geMqKP1pze( z*QSO>Q~}YZD$$luqqUiZf!|EM752}PV)u?xzS$X6v?4W?w5f&$DAl=fFzzVbN~I^G z`2aw0eFlqnMmyM$X@5U$SA$_EeLfhf=b$z&2}}McV+enhYbVu~y4YXeVbU zBtgEX2WFS;M^261s*Kh&U1O$PKxRFn4o>ntR39;F$}AE^^>ZW5i;@4aW3v(jZ>o@f z@`gxfwyo%z?trPH8J2`tx?p(Pf?TvIlyS>bh(u{#2-XnxL_wC2%tl@7Vohjl%Ou>j zVH+h_t?^zmj0dXAA*UBk^qByc=rWgo9WbunHqyW+ef&U5;R9Lx#OoGCJXXUkPjEHU z?F$>TOku$^GAHblc|v*!T3l)aHRV+ZTVzFv>mIaQMuQqrB5)3M#c5c`YdkDW9#Ae( zysRNC?;y8N%(wScp$ui<)GR_d%%%}rf(VaIA1*{966CS@%~1AKp+W2Q0g%KW6_CzX zJ~^V7ghrHO*qKVb-C#`WtK1V)kkrO1Td-!Y@=cb&cb^(_cU54>TC*vlxZw%bSs0Zk zaBh~$()yBW>b~RZt48Zgw!Pzunh67KG}Y9uK4Y$Hl z+&S_#aJ&(&0e0`LW*Lzm5^o1ajrmy~Rm|$}4_^Kz!?uvl-gE<=7yuS}e(fy7DhO2) z%dWUh)8LJ@RAgiDJL6O;p;81Z_CVMPWPX1moakHComs$O({1TR7>lFH%cE_7?Q8$txzV3iMAym5|241qr4i@uXp|w#r z?g72c0K5BChabF0z*hBMZ^0SB@S8VPMbzA+HwBbKrstT<5_d(X&0cAg*w4UFw?a#| zRMLXxGfQ1lQmf3&sFH)a5VxzK3wj+T`Q>U@-vVR4w4AUX3@O07Ns>arNtzRB%kRnD zO{>@Ms{IL6_mVNFfo698udP(SKcNVc7*)f5GlY?$he@c088L*Jpoej)hTRz;(9j}S zY7xS(p2MWIOyLP_HO{o@Nc>( z%>UgnsaDanLpDbFW^I)oXenlsAV?yLu--tbq0D!nGN%Yq+En=UV%;E~YSX}yiY7M? zuKTEM>a~bz>eY`aZ-$w@FJNjk0`C1VFN*K|7=QP{Mo3e)*%Y&QzV18A@#EU#`0e;T zT`TM32Ez7!r;m_n&*}$95{v<>(1kj*iIK>F7X)t_oOXMtg^d=a`(PbhAL26U>V9}(t5M8Ek z(&)j9-7FPK`WhriCAMa8YP5cKJYLBmEhsrWDrb&Ohk8bY0QT6W!%39i{UPi)OTq@9 zVQyn1apq2=J|k@z4C%a4w>On!AyomY-KCnGvk;||G?z;cyA-nW+r+ zy4u!6G6B=cQwi`TFjnG9CQMhg+oDES*odRuusMuy*`R5Vs{<}YwGt`~xH?m~C5str z%hyAj>Rc{SgL2TrY0t`?&V+WyED7M~D$FDJjYvv3!FYm7O+bBE5}?^uorlc>wIi$+ zBPiYV~+#-C3S9jThSJ(rN$s755aCuE^@eth08n=&_q2YH5bgmROME1k;|B^Y(?N%2V#7>ifVLBR@L2~xR(50*qIL6H zN7s2lG(o5k1i@;A9OLuzuKq~OAq{!C|0W-r&}#pbf!YJRPt;)S^)Za&&=Hx$qvv;f zqOJLR=PQh83W0DDxEbw>|U<9(Od!@ z0~md_f}!nj;p3>zpJ$x!6^~$fNj;dwAg0}XL4rdv>4fuoRi}Rs3 z+Ab`j-Te9&!i*wPD=ircGfZd%wq1ueYzsi_Th1MVBtLGYU>wpSPg0{?*dyGqOL=}5 z2mG2*v#8s1pD;9rmL)Gu=tZ;s9aAb^e(01cISFG+mZYKhW-6gd zMNA)}zFFr9yQLPGY8m6#=(yh6c9>DSE-0TmjS$!aD1Gb)pmxo`XY~?3m7=cvlzT|> zFca)BK5Ig-Rp7f+njvxWFcs{uUTeYw83b!qgr7A*7!HA^O@8kBV2qZlJl0US1R%L0 zy=*wFf7CW0+s&tOohDLjJCS#318&L&Uy^8cUC4X9gZ}6e>++3$IiY%W8Uq@+e7vkV z%~Jl{-~GwwHVl^dg#K4vmobrh#{~xfkVE?Sc>OP>GfVt;UQbq0SHl@a{0_BDS%#Pk z2&&nOZ)(wxccIaYFchzk_G_+JiG)#OY%nxc3o$-g$7JN9@;4y8^WJ^A8VdE|Q5zF^ z_oA?!NF61a9q;_g;y#g7o~?tS?~F^mNWZA){jRvKx$gab$;6k7)P5z3z!@<p(;D$1JLo;{Y9M3wQ6kigz$1Btjd&>9Hci z+=C{Nlp}?#IrRgEn%Ly~q;7rbHRh}ZoXDn*!?h-ZC*+Ju`E%@t4zAt^P0r4WKSgAP zYolRbXy6#=lJ;6Evj&G$4XNfIQ>jef zq0#?X2(P*-xk_N!;mcku)f#LGI%Yv40(U6s&9- zj!H%8f}!;9P|$ah{+>gRN+$Oi`o3 z5YEVx%7W{2rtJDbj-&gyQWB zg^Iafowe@{ve{4+x0NORGE9Ap)lP`jW|UJ0Eo86J!~d{`dW9?)fAh)P17lsQo8o^& z2F2euxDul=9dKzlz%zqA zM{Sxfhs4Rbk+9Cz)^DA@2YR%q$!W!<^ppC=^Qj&miV4ozq;#G6nZLdGc!%GyhjwoC zxAKS!G2{_);#qlthlPckPrJrEAD`P$IWuqq4{}MGke1EAK4~syw#|r%ThWyX?9`8x z|C8UCjLvI5N^8fH>P)RK&T_d}zQ_?|eRkqT>HtkCnoD2KNue)uv_T~`&xp_#R5@Ui zcT8zjxH>MgG}lQ1iJAE}2kaKdg%HiM6=W9WfS<=OP>O}*0~pqejrMf~{b^l9XJJg@ zEAIOhL^8jY)#eux^vGg_7Vk(j6is4SM$94OfR8m^49RIj6n)UXZ}K=d76nxpE;9pu2a^d4ZC-z(LDRzAyoh(V}!Vi6E8oG~^?4dP^8RQS-?%8>DM21f9Dpc z%NF_-r0r~l=?Zmt75#4BsOBPs?+sM7|EwfEi3n~tS*$#!J>G$ThHT}$LfsoPlbS_o z4T0HM6umXzl!WV(qBbE$sTK5Se4@D&v#_n?3iy;7B%8g~2Kc+9`?S>*cz-9jGnh3* zH=M2Slg3x}P2~q$ST8cvv_2h6&uUWG3YRB#+H{^jgX8lLx9t{OPRBsz1%e`OqnvvC zv|@!|`HcQYrdQ%MM)WY<-h^bnNPXpr9Sd&l3}4|Tcg8G{N0=U zZ7J0^gf@l{vuD=H4Pj)bU5JAR#7hd5TL@zhdM{dJUN~p$7yQ4@wde~ns``J|0agF5 z1G4@bG1T8_Rz%;>$=1PL{O^8W5{Ca;*GiV3lo;ek;Ylnh88KUaR~ZV9q+I*m8bJ^& zNLZHFmMatLL^3``g8GU21*uIz`w74&(G{+}cFjrec*FC8b8Xn;%iAm0kLaScX4a*- z2oSg`t!Tc%Fv}=DCSE3e^Z-r^wt&tl?v(LXA48NZ(j$Rqz_%nH8mX08N*nw!TDs(q zSpq{f9eA6==fHO|)P1on8Lzm|JugITB#P>ceR!eL|3VKu+Fue%W5oor$l6!OuAZXb z8pKEuB62MI-7Fuw#82{pPNbUAVJ)p1a(l6hreq0!qc zlDWXFtolw6KqQtg7p4*R=-rTRQ*wPBv@Wjat0bH+eAkb^7mIXbg!t2w-#?T!knK?XwD1>(#+M=Qq5eQ4I}Qd- z<149)>MhzBZh>5Pr!QX9rP+$J=$oj_0 zLy1B)#ly;VCL@fd077-S#)3Cw+_Rvl;FZPzbEQYpdIs; zf13%dz8vh<81M$Mc1LpJHiLuHaFM_9{#k7R{ty5!ieR@DjRJ%!1lil|bx_ThpktMaY z%Tc}KY*XLRRLfs3?G9G3zGM4WTp--G=k))@h3NksE|~s_3)%m~g=WW9huSZK18^0Z zA|-oBUJEH0NJtZ9B#4{Eb0aPzE+Lav>GxV@L~*Y^SKp+&nFS1j_c>{o>zDS^opjw^ zU#)>rR#qXUySx|ggxGq#J#jx3zfzcGhS?1wLp5ocx3p2rRr9XYJ8oH+8_AE5 zd>`idVSY3TIS&Roch69Lat7AuH%xS3OlT$?tnATraYf-L3%8WvaPgP+Us2gOapLUD ztrC?7xPzfHRl`5~EzSdS>rCF`b7Dy;xgvu(O`~0@@ule){f)>r$&IBm5r?w?A!q!S z=90|fIop^N&AN-fdvMJ|j%QX_d~!WY6;M!MVyDKS+H19Ti!+yNwS0oyxg+hs<%6&; z0CPmm3WMIT^;`4R}klt?Nz}`r^5c+r&M`Tjd%RdIGL^+r!m<+~a(& zGTQsgfJ5FOMHaQzC_vg32U$q@XP;ns6zvXPpm?MfMJ{*j?)i!yPtr`~lAb7gTSuJl z`?%NWryGgYw>Kbv|MT1l#DAVU0ckQX9VGg@V?i1KfcgJ7!~Yi~s<|QEl$MuIw_bEk zjNPmTu>djAAdNr;`tqtE#I}wb za_xg4e%DSQ-|Zf(j#>A+tG=a8*r<;T_^f|^$5-98=7 zz|hscyEfj|>st!A-?aG}yt?%(L`mJzHchTX`*{fv@@9?I3I(hd%vnuAa zC1+2~A-0yc;%KXrm*%Lelh_Q_D(AQMXsy#{Xw1*jo$l6)EpD$# z!FS;y8>f$?HjtBl2SSIOQ@%rJNhCKd&+nQ!=lI!TRrKyQW~cc~@Q>TlTH5#Xf?is$ zs0T7_n7tkU4vZlXMNfKJ%!0^#Ei{C}Vfzk@lF0oIjQq$$X9fjyAML-4YNj1EKdc7E zA%K49BtKR*a%m7i8`0LPhEA%ZMk?{9SQ6==EwwaEqMap{fR^a|sf4cRWS>A=a$$h= zB5evoepxU;Y;tyhVv{|Qo8FN&eAwvBAd2qj%pi-N+);MW^VXr(*F?J{%rdxgd&;c6 zbWtFK{?zqMq)ojtjI`i3pBE`Y_XkC~Lv5llg(2d&LoGkjM7wHfm{PlHWf)64t0uC! zgIx$6P`heTSPq>~JFTl0EmrLYu^IxjITDS!tVXx4rNhSlO2a~`eE%k(0t`g=D&0TY z|6Yusqo<1j6JB(6mj8^04o#W_c>bt?zi4nuL{8Ctqlf}7;E*Yvt~yh5S&qqc#~iND z9upw?v)TddX}DA!7y_?mMP->-k0n3=!4jQ~T0?7jZYgSRc3Z2ntOZGbC~IRwcye{7 zHoLK_!r`h*_(@usDnm6Q&sj4FXtquXYvJ&frc}nvv1V_KGLW}yriDat8{lN^sEny5c82GZV{x>6z77m-{Kqg|BKS_VhrC0`JkN{dl zj1jv+4}F}OG}wx;)Ib7$iWfX%6k>RF?&#zy_=XsmciXr@xGP8^LxEvYB#Z{J0a)TO zEx-UPiKB#}n>TaZpD<*`Tu*ohM4ha@%PAvE>LJ0)m@O<~{#@WQ5zMgP2AL?xjJ6 z`$tkB#-BmP5Eor)VQq`8tT-%}TpKItcVsCn*gDi0Vbst=gIg_J%SM)IzxnD@R47{i z7S;bKrUw&D;lwT&m(xdCNg;$$*t^=|FR z&E%m+P8E)fOR8Njz#O5YVeO-pg}FAwX2$KPCq|Iika6Ql`X39NE3LCd)3|#w^T;2E zpbmia7sco`TN8qnt>H4Rx5 z5XTdi4dCc)9}ginN2f47A0r(S928nRC)hUE^M_?C_rF4}Q#Xg0&Ggw7EZ8fCSNy8f z6T6^suj7rHi!T3_Y6TbCLAU7#&G7`X2#_Kq^v*TV?as7@2v6S1u0X=w9bkhLg5%P? zLz4RF&=lGNl5U{8A!HqbjGNXECtBE@Y~r~E1D4SB^ZiWST8o>^K-b#JLz`6wX`RO0 zuA=dl?!OnpfepMLX1w-Q)lZSG-87YirK(z6)=ps!>we|$mPZqhYL637g$S160YXEH zr8#iPMZM_Qh!!Y=k>Hl8aab?yf5#(V{U?alB;$pxS-TZWX>p>+I zu|yV2+Ls- zM$f5P-Pw<@a%gx*UyU1v0|!0A{#eYgJW66zylx;ca=5UG+QFZ-zWhj@1p`jBUv0t- z%Hik2MUG-dF1lynPzx|lmu4j;5%0E0|L1YLrqy=EDaBY+3yX_%lXt~Cq7O4*#%`8!*BxKtl#s3XbfwQfM-t0Pe{+%uWjxu!S^u}Le5yUZ+<3kK(c zjh0ES3Hww$2}a%_WOYSQ`euez0%2*C7c^ata5xnm9q-21B+J_p;kJ(?LE88Z?iF^}tKT7Oe#o%Dac1!K`~C(m zO*S!s`GCG5KP-Dp-=C=8A{m*@ZGb-^wc(~)+9TV>Xv8p|ygD|0r?2gshl{$m-V-G}B0d?ZTXtc;2LeOZO3ktWZsfVJzd! z*`r_2_0D$@qOBv|F;}9OV_b&@l+zHF1+90qc z>_=oOux6h7P<(N>Fny{y$nwkga$+?TXQ$tWWy01=TamA3Edt_y5Ro&Ex8i(sjdS0G zLKs(KRi4mTm)Th89O_caMi~iwuXPv^;5@ZX;%w3JIFcMcoHl3l!&+q+iF^wxj0@gl z18Rv1CGW@aVM)Go;2#pkI>l&Rs#?w;$c`ZcyGBz#h9ZCSQzE}4!cLAGC2o;DQ0piw z_gY2wQuG9yBB_(qQdgQ_4MffH<33OrHKDr6B=v8q?hX zo>~)(ppaLYaHbH}n;~O-TtMy+rYl`#-~ID*rZ z<^#9o3u{iF>s& zF7_!uB*`>#4YecT{#N+$O>7$T4Q~n)&LF(j8^lgAcU$qM%EnZucY3F0rjIG404i!F zv`!oWL(%ZG8%7uG5ZV&oX_s=rI-s?0CA>y-RisZ(Wt1$A`>L{)Zn00`c%gc#8gaUJa$6Zz<@J~6Ozys$W?>~o-j$nRI^@hvs0SO z;T-k`(+S%ZPNQUK4n|&x5;Ez;giW8W6*A?3yW_ar)?9Tt0SB?>mD;5?Bdn9| zP>E84(Rx?Wa7Xi4GaJ#~;IY435{B~p;x`5R7`jYBu)5tNSC#49((tE9+M-7fK=Cd#+7D%z)TqM9uyxnDvtWprFCBiQRSrhO-WpSW}Xncsw_7-u%PW@x~EjKs+h`;FO{L7z_ZjZ|$N z{dG=O68Z7YqEyKfSh2StDCkf8Ruq9tcytOEXGma%bLZmV`l>q!9x7y`Al=xP%^3mi zTB}V_rSnQKU$BCOQMYQ54!I+bJV$LT$BrM^F?Jqz0=E;6caoK8mhT(L^-M;_xyi{sB_In7zEgfGW=Q?zg4NeS7+IF4_WOYa22GA8lN zp~e#QSZwP2LVAfG;ckCpZ`-Gquxo2LNVp#D_>u{9g2pdx@f&1k1fJ&)PT69r(){U( zg>-I4H}enSK7PTn7}-^4;5`Rpwdq6bC?Nu?ClRtHHk>UnSZ_}5fFfUkok#Y#g8{I8kpxn9OL>>HvdN<-FIVdo_|YPbgiNvTg-#sZy}o5WU;s3 ziM2#OB?P*hzLKe?r}*b*cAR|JldiZIE z(~)WOPva`+Ahvy(f9SDe0$ca2`Q1V_08!;Xv{gG}HhS%wEz7=gD!mb!eJ?F~MXq=I zKa}YF;IzI}=OyquHdg0eMQdCVHhNu~+2lVARXw5hzkqp@pZNOljNiyG!Q|e?MN@Le z74EjXt$t9xNhfFbq11gKd{d8A8)#l(Wm0~cCVj^hPSFT8UikhrJV}dOYPv=`N|ibT z;`7POtLCpr#)%UB(Tsc!C>7fV@|ZV_+wDT@^2}51ut46&J<~4Yh4s!z4$Z$MIJ>-T zg?ne?1N7!1i1omH@r(5bW#mCw*n<}1_jAMf8*PHV0!t6aqeQq1wAsm( z+(uG1MfVQzNO=>W-hBsTP(-1(b-Z&j*->?bIQCg-&_}tb$~28xm+LtU@GMxi)=@9wwba z{lliL7zCyr~hNwOkk0@Uw?_3CgBK0QNYQNh6qfcR{qMNt5ZN!8#N8OGMZd%!oPi zc-Z>*mPJCX7}68;soT=Lx|$<+3J{i&)$yGK+O)bZ;_eMS6RMR!|46gVNPT)*0#??E zUIXy{K1*lt^8jP=ne+2_ms7)E;ZrR1<`r9k6v+iEuFk zMO{;W8AjF`NfVThiHVawr*MA!#Irj3Zb#6t_EzjTz@TyxLF%Hz4GL zX?ld#eyPv=$m~Y!1C#Y3duw9z%s+2C=1`7p*aXivf#aRTk_!{{i^FbjX;!w=ET$7P zRUAjcuLR2>)TSkJ78AW8s+XG39(Kqwlk6N}P0tVV1gIozOyM5XQESWUbTO{MCwE2! z1O=ZasmNb}WFKDu;6@-@JZY?dxrym^WqsZc@&?v_IqkbK%W0fJUf{DtOI)86uRW=J zUcL%mbTP3=A$K8|nk9TG(lWE~cIKFyIs=$q(lWd7j&#UVUjxc8r>Kokb~y{z&mQJ2 zEK5UIU4;;$SfRC1iNLya_fztK;L@sqV+Rkct|#PR%?kp>8oORerxzJI2CSH|jSGZjY^AMP^#a(AleaIJ2A*F6*lLzA6_{#)d4|zXqaTnf9FO>xl zObBjF2)q~s;rN$o3dvNkIIQ2x9+etiybK9v4xe?vq8>x49z|oQJprMT0uYvnjsZuu zq`5SHUA%#iT!NTYoA0Nu8gDBJYXzZ&UhwQS@n?l1krtLle5Z zfDiCcWE`1J!2E%68u^YN#esAgylp?@KiDexUjM~m&pPE#*ck-8zV-v5*P2&w8#Fww zx8=WO!;4~$$n0n%2{qGk3v2szHlav7u6i`fWAYD%ozp|!S7YW^SnaWgS>8=0U#t(Y z75g0tfpVn%BkX#nh~UB>Fg?E>7_vW|4fHL>{K$(F^Y06Ra#_I)DUcrN=#q`|&8E<< z3V@j)b}FF!D|+09aY{=3&^LZx#PK*uE4QvnPfqB9i0=`^k8tP&-!O(lt4D0#K*a}G zWkjH?1ILLXT6LlVb4ur5n)Wf#e6h1=cWRmvKUD1*Abj5AJSXNk8#jJ{4ao2SH zkTo#7>K4U%=BBjMGU7HEsKP*@Neh9GKrl7e8=@_ zI|My|WC|m_#P4TtJoSi_+0>V(m9`C(TW@9x^f4b5N(S|gjnIpg+5#jggHb;<8Mvyh z>INq=%NN(ytKvNOu_2i}_qA5?p&Fy^x{_eCb655j3<^lb-c*4C-+CI+X!p(r>?Wa< zc7wGLYX%4}4#{7&)ypN7EfkU?%M6Bz2D;E_eHx5ZBN`;vHr~*m=(QxkC}xtu(B!@2 zJPMjSxTU=Lw;2oGdQ7EV(~wHLV3RBmBshQqB*0gwNSml=iu^A31&dK=RcC@SlUdhlv;bVmRR_8!n&*nOz+@HQ zvK)XuCE5{mR9hrZW{CmV1W0H_QP01eYQ%BYcASz9q^(jU_5+355`a)JMgX*iCH5gX z1AGk)D#1wJ5k)b;oH^0TIwh5glVrA1&ymr3dSOD4KWI&B&?GUCKWg>}t0_kpz2wM+ zRB-*gjM0$EJNso9M=4_Y%Ns_EJB(qIjUC{~Ep^VX+FUxSJ1i-po3?Z^y<66^5x0}8 zhN6 zIkuMYoH9M&og7rzmQWi@Go;mpVcg3a_XXS5{wbLb*bmiw6>)=!x%JSIu2?gE5LYRwaIN0Cv~2_%`gtP^lg?OL-Pi|ht7rNWCt^oB&E z!W$r&5VZ@w_f*K6-2*tLyNkPTAADaFQzjkL^A1-eD%m(dhi=`&P;nXT?QFbK|J7qW zkxHBzVO&R-kGU&yc1e=@; z_#A87K?Q!#0hZsPo`L1t`s?gn2WV2=bLqFiOyR(;@wQbH_TvA*C$UHFL>x; z+6fmxYy;dH^?Ai4o=@uw`_xYm?+#nq*4~wvY+D;P59`iP_PfeL?snH~zjLY{v z(|+*NFH}Gh0hP}aEqrwBp9lG(-eo7%LfzDkQH>dkapSs{EL0)D|C zISb-#^y#>j3EqCrYJ+SDtT6;Em6o}|QIk`S9cm5(#@eDW7}}v-oqP-~O~ARXZpx&| zyw1ZsiO~u_eFgD(E(lP9Y$pR;p9ge52YJI<`L{s57xd|fiOS!Pnd-%HZ)kKO?G`Am z^SpLZ>kh59ANeoy45*cd+D~G^pjDbO5P0!-tym_2 zZF&5zniD42gYj7*vp!bC|7$6oT_8J{YT1KZNZVh)86o2Wkz3Zfk4w2gOgp%9Y5BFR z1NNm-z)J*X|DhtBuEO3+yBlOz0qHfS1Eg1}HNyS^#2~;UpIn6wDf!|>IMkzhtxAWi zrGhQYaT#m)?ZV|B&y$LMu}5Z`NVnux!4B1p$}Q4MTNk( zB;2wna_+mLamjmdBoV-UCI}zxM6tF3->)ZGn@{2SSlJ3vJh*)1lmSA-fYaCx)C#5 zce?}wJ+Apve+&uS9APqg9-@`e?8EccX9k1C!8`KZ(x0UcSzo1{Qoatjl7Bo+JvdDU z5-GTqRFwJLPo`O=_b68)_ax#ttQs)X|CAaE;tG!TBT7w7)+vU~KHQ%pe>5j( zC%qy7MQ|8DC8umn!bo`MRNQ_((~+o{{_BQLy%>7zFY?{xrFvOST0_fUcr*ASi zD@^v2r3xm{z(6)TrkBWsHf*AhE6WBZ*-#>1Ea_LO9%#-zmuX_ZicG`hV+lSK?F{zzuk>@hWL4PK;#qliQ#cF246=;oqV}U zQ>8{5pDq&k2Ya!BapDSPCcK>CL7#od#U%(=G$~VkGNz%!cZou^}BXvvIcm>^kic>iaW6 zJ^OT~_e|_2iA8kZW68f-Y&B_U*|x--aX?vJZb zDN+Pn-(Q7YO98Xhe!FWA^y;ra63tk9O4lL^$C-Mx&1e>_ah$uLFdI1V^Dcec8!~50 zH-R6`ANv^B1a3JB5vtAGcQw!SUPU~BzAHWf-Az2~{FeiO)^sr|xWf+Drfvz(>@!O_ zeQz6yYPCGU2-bY0%|Nd>*Z4XWI0KeVrvFT?Il6>CKzQ@M5b;)WM!h#`?RuXPd-A%Y zYRzi*IyWj`45VB!@>52P48DI$aQfTo4NAvkkZP3>4ca&8_ zjGRZu@dDEgmaBbIozJw>$t&TW?pb~ zfpq+i?vB|%1cf+97st{}ezjqr?=>5v{MMu|rPsk-WX1pPd4dmmWP@X~KbE*90BZ8` zX1jU26n3gga=L2^=qrqRCG}UfX{qhxlQZ0#FDc}cy$!u}p#p|bF7+07t13BQCj0#X?X zNvs83M9d4);2_|M(yB6uW3|D$P9f(v?aJ*8V{1 zleERnmKr@(r4;EioLFQa9hDlwXy{WnW}O-vOBKbBBHOGLBpZ>m=*opXvuoJjr1!)=D+$ zRyP6iFVxS;POb#>oE*!~w{``!2)2Zv!n=80@ zL;Hx%3lLi*cpyBhU6r^)geY-G z^-&Vj6zqhDJM+4+?S16dg{HjzQ><*aR0W?EMV9#Fra1#XfB?Rowv*b0KM=Tu*+a6DPFJ3+byO$*YterRM1HEd!$E>%kDU9hsF?+o%T8 z!cnBU&+|w!r&|cR?{S_O)#ionHRvCUc>OX%NM`srO<5>S89QxKO`vORRhn4r0MBa6_^5vcK(OPMf#Ns+>`fT4HTrph{}2_rh8thmLV^7MSm4}0Q}}Ck+VJCi z$v~s}Btk>4tgs)`ujYGVlQ(hp6@CIFYF5M<@ZjA4=FKB4IG%-d2%WSMg8xP5=(*#@ z^?-#F>2V~T5yjbW2CrJ?QhkJS_KNV_Rx^;4#KT_T$7`&U%!8M_XyAD6@*Un%Yjo)= z2=j1sTj0-2513Z+5TdAA{yL*ZJme_vJY&%Nst>K#AGsv3(epa}M;HC8 zRhqYs$@4n%2R5b`>ojjIGhhtDYQq>xl6#N=UvyKwvVnN^M6_|;^ClOV3d%E3 z0fZx@%ZLBV01V@ZctUap%~ey(biS|k?qYLg;No`h-pnlI#>rHwlw4GU9()v5v-XTX z$g6DSF)UEae&!6vSi(3-qp)}&B5E^k7qK<36w=2rJ(u+WeO>=b9B4a~bqt-2F}=4g zVIHE1G!Z3}WjCUP;;m56EU=o+TUxdbZOT}pEJkT8Omkmkks?u|Pv+bxH#b(MLU}ok z-CDuvDk3$wA2|~?gA%BDH14u|sI}K(m@es+vM)CEB_n#t=hg14XMgCK2TP` zS<-w2I#RcMP_%w>xJ8jsWMM0Gg*tnw<(#mCk#2Js2@S?(LT1TXXfm88O;^!%WmY1P zzyl+w&ZH_InUf%rE+TB$F#{FBTUD{uf)xy^@ycYPL{TrOCs+@;Wu9c)sw{Dz?;8&% z52;MDbhU#X7Yc+3o`fPvA)nHLDd1#m64M1g>VZSQ?;$<{j+#+m=_)J_h&zG~JBmqr ziXBPGjcyB2-!go7;3)OSbP*&(Zl+3&5^ote;<@FosGwG&`~bYrqp-5U+O8Ur>KWjy zr(lY)1|l>D=>%iUUd(Hk;Gc~=^r$~X2!4yBj!QFUy65)WoGJ3VupMHEGd zu%{GfW=2?J%TN`mb}Ztok<85^g3(n`;`>UjPEyLUNH80=2=i@ZlTAo#735%pN0Jxz zEc`Rqf&CitoCYnQObM)U+L;dxX3GS;5=`?3p_5De|XR7~>zvmmdxy!*Mr2Sede5qd8b$gtw zd|dsaTKfs}+72X$iaRyvj?%v2jPAbSj7`0$$5<2HeK>l8A7+WVmYWO4NT8Gxj6_Zs zk)NGEXEe_g&9Kob_%ilD8nFjk)!!$bUu0gho!2@oTc|cp(8@zI3ukvVSE5_|@y-~7 z+xkqdAEwbxfIKFH(#IaOrC@ep2M<3=`R2g~GCBqF6@KDJ@sBL#Sz5;cXe;~QquD!FL0iePLk35T>4S}Mr zKwC86O{KSMl2hP)VFuGfcK35&aUY*(E!`nodk))X&U@}K=$4oe!Ks2l+lE{G|Brn9 zkNCm}Ev{;aU!W2B>kj#!#APV{M@I+aU#RhaU0#ZtHjDi5JeO&l3?->e#X*u3@c7{n zu(Np*%EUzysQ3!X2ll_RlxnZ=E9s(7<@h!w?mLm;<6QVg8KvT4xL5dtaVszh1BGUDRu!JB%!URWV&^ z;1oJ}hgn`I->9$dQIJ&On$e2&JA@SuW6FjTzW_y-^kA9&Ci2Sfr7(aK#?5Jrsr<`* zPDv=8$*+H17BSwLDCabQ(3)>rFgDhW9$6=D#6IIl=T{EA{LhFxqARCmKz8t*X+dpb z1Y%I6us!d>`9};mBEgtt3K-AHJES_;OMXiBjo%2k!SkFQYDlu&nI^ZMZ`lnD7MHWE zujOv6-;kM_8K-HF(+x1S{|l}ex)P0r(F!eJ3;0l~qWpz45eX47qF*P^NTNC!)1zbB zw?+u&A3nUh&aD$CbhBe3$L`z7FRA||SJp~MH`0*0hy#V27&J-igPKs@nj1A@0ZMX$ z$?#axj`&X)_iB#Ke7-x?T=~h!ychBdy$M^3$TwZ4^XM{c`yG14fp5S~S5IY(76EjG zt!m-z9XgoB3Yq#jD{-T6E9!DPtI{PP;WlT$UK3BxE5N<#1xbs8x3FfQ=#WC{kZWQD zt_q_UbzNMh$0y^M zKO@X&aX;@&yTXyT=*a0URC?;{AyA6u>>+-NIAm?B?ubp1V`0NYy{fXKVz9dO_7$& zF&A63eKee|XI;vtmd4G}hckb!^IKVN7Th{VxR`HNUO7pLJn=J`LR(CxzK6&a={+2+ zbg8qVia`gm+ocFu>Qc;CMw}=!Ba0ol48{=@PjO2YtPIT9%|#Z0G=Fgx^`aM)J#ng7 z2#Ar10|XP)5eQAS_4H<;b~nwmc6GOQ{PW6AON{mPw6+wkF}&}hyh0ynqTu+I)-RO7DonDE4yY~0rf_GIon1@AQ zbG5r6)tWG5SlC00h6bC}F0me-o3uZX<_0IQMjY6+%}$GO-ZwX5^=6B+6_TAkZd-MG zB?sjmAvh0+O$;UeF#y-b25=bxbwc68Y8m4>!i7N#*hT zvO)JnO@9=|rD4W(!Nq$a{Dp^iq{P!0GvB9=ROc=vd+dwE=Ah zAx@m;BMOSHS+@cnrZ`a@a_jhQC1x}re81RZ9PQ7KYEin?Kb1>`TEzR*GxscxYA6p z><>@!&aBc+gnl?K`2OULYXN0^%k2^jQX0lO&d3e7sVfz$<4g&ks~)<`e8`j)E34kg zDXJ#x7Tu{_8YyAX2G2q)utSIpTh!wV6>9KDFoxoBx<~8m7*C{2B}V{Tyt0I)G(;e; zWM*A%j#EKJ-Ru?*BmHzjOPwIq5ddFh0F-08nwAz*{DrLH8s6kyH5D^cI&%@lRqU~W zIEiv3y>n53uq&lqCAL%FpC*V9ptR&M5&kvrJW2EpIH$nk#vHI(0m{^?GDZProJ*u` zrKJ^VS;Q4oC9iNxvIMCNpn4-mZrH>QA-nKPc-#cyCd8PcXM((AepPCqk(&o$Arl*V znDCuL|FxQDy9$Nqs{7k+_$rDr{y zG7^=HFa)|fYMFhm<#ke6&hgy8L3*RCPjI31ZH)Cqt2|pbHQE+tdt?3jqNx0tF}Fs4 zLwDg5!Q9~0A@hKDt?qC|hC)pLmd#$Jj{YS%2~bl|aPH{c&68cw546b-oOAZ+U^)AG z1c(lWmSI=`QSFCwDYrNVWl>%Zmul8lh*x7O7p$^Yc2`qWw%(F6o+IESXFSwCqJLGT z0l4z3dseOng1%u{rD~2e$RFpx4ad>!THS*-g_#L|&Mhc~!QJ=9yk&Ds!eu_Ye5?|H zTVx3QPP!ScBvis2#*J2yHkj*;paXHuMqmcT+Y7|RV|+#R$sM_6dj;~M!d_*nc<7qv zV%Zn|^d%+@gnIx>SIIhz7g59ae6}Q?dM%#%OyQ@@dux?yo z*gt6h-DSfuHg#nkn%`+7P{G?;+L;&_-oTyCZl-+9!qmYO@o8=!>9w3YqxLm{9a{h) zix5!k-Jp}Dd8uB~NxM+)p{fMHpINauSxN+8bb}L4(ircr#A#>~Wjx)ubfGXc8WK8< zY>^m((abDEQ*pYGxYgvnK<4@=YZ*y8DOM|)%+J>@Rxyi>!YsQkcZ_B#>SAh5`d~?f zAgkcmt)aJ7xtpW3Dns|cGd@-MM9NpXLP2z;pnH+npzlt|=~(}oLtA;KP47IjjaJsd zo+A|r@!Kyecb<>kcpMv!-l-CuWdCz`LbZzXo)RuN`>ks!yK;ki)=_MkbG9M#X56_) zNJH)|_|Qrz{}EUxfT!&O6Hau^*=E;HgVo<{&G|65>Ee1fA!s^itj+f7x|J)>yoSfJI(ToOl|jA!x#Hfwrxdpj83@n(}j3B zFJQZHr6Wm-{e|L99=!5HtCA8@3?}NSw0;8$`z^UFp)fzONO*K?SI0NQBwEqlEC5h# z1#eIu&zyge!3Q+-RFR+uZ}5V)FIMmLPzg|;w{i?kPv(*1-SH}r-diX9DDX~KP6GA$ zW%H_bnEYz8udAqgpV|CLZ{1Fc0KC$V(18x!XM!O1kX1h2TOQ#zPKw}mFl=;_w0E#) zTPxJ-7##&HIs_u)Y1y2c7X~xe{$JYVT^bZF7)mT-CYa=^{=dCa`;FLnJWhdP0v}}8 z{!(rwy2b*C4DW~l9x^i-yBpwXDDUVAdZ{@tgtBj9ZFntu0hG)3eE|)h{96EvD!Mj1 zz^^K-@*OC{tL;>fa&|R}yFJ171$jyc6u(1s9B>_Wh&!r2y}JT&{(L|uFf?hv2;@8( zH5Q0DWIf}%1988aKg<1+)P4w*t#$$Z!>C2DW+xbwzaixm-GYv1 zolSn<>9nP{^PKN!7KOKpTotXn9ymU)LPH0=1T#KCX>SbJmt=LXvipC3#0?*r9s&4D z6Ca^GGIsJ#&!y!)NXoiEqIC67S07owLU!_7+x>iMt*Ks#^^~I=J*{QU?qe0F@?Q3u z2hgU@#c97m(YgvyeF6sf21?ik3pYDtso)m7TU1VUEAQLZrEE*~h_Cv+c+!v?X&LW& zG)I-1NzYpGW#^TY3@A3Wlos3p|9-0mVMn_l?`1w&*{uWzb_IWx))V+tQy8WmPyHSw zi$+LiBCY=osyu_PpmA3ksvQi?fn<3MU7@m;q^bjs^#KXLSwC-`abPcZ%9l*H;PJy-GLS);q$Tp~yFjnp5!FBgr{hj=*Qal}XFd<$*Us^$d13laFKdF(6^GXxXJMV zjynb3a90f-NR89O3%vLKWraTaWQOi4zRw1ZqV*j{p2mN3z@3+pdy4E$ur@e-QhRTZ&2DMZOqGy&w~{Q)=&M+(oNaxEZC7wf>`*d;bD| z#{~T67@(wG;Nw;k>aR%Lg-cerZN+y!zfrEee>d($zVkxw6ce}$AJ7Zljb&4+tcK^h z4n?~Ll6XqV!y@Gz6FlCds^Y^v!OWYk@si(pgCu8TKM6!X&MlvBP2Dzfa2qgr+h-m9 z=}pG0?t#69sn)cT%If67fkvU`Z9@k`%A?xI^Geavpb!_eiKiz@dQzI)?STdrw z4*8a_?^L)`-F1dJQ@T%m&z^dv-1R^GK$;JIyhr{xaX%7umba9fO zmQC43(mbQ(&QTG6AatS;c^Cy&|H^aQww%nDmU};-)7an@3SZv^RnNxxBt?uAsr9yy zwMp}nl|4{!3(Ldnu*+-)-@!)Sxd^*`06W{l;sE_%DnV544KUUe!-{Q4bID7ov#tLL zCBw@g+0|A$;u2)(@iY&%%~E;0DO^TheUDk-+!b0n`G6Gmf>Ug@4RCpnS)|=HT)Onk z67iO$q^atg?8zvqf3K;~pWLbE_N ztKd=|d)F>20xD)v;>TViq@L96(Ej51PzkH+oOqbk@oW;g>3wtiH8L_jkd$&daS5(! z?~jBUi#96u-|6ZSmWo#$RH-P%Tk#HzN16E?TxuCP#2|S{QIu5fGNl9p4T#F8d0e$4 zc$jsI_IO&ka29?x{HLoB%J;k+pEW1+=F?QYMKHfNd_XK~^p2$EyHA!gYK=Kfr#$hs z@b?>1B~MSe&1gaj96@W>$ckfJA*%17#GV!M*jI=pCnz^B*e~Qj7eJon*`#^jnx5W0 zmnUlVOmGS>!LHB8)8^+_|%H9f+jKKX2jO$PGZ{Uj)rW=ZzQm|GB<3uomcrV z^-V~*=Gd(M|qQ{?$Ij zK=;1xk~k#YAn1oc9-qEz@hgvKjf2B_tT8U=;_6gJ+{iU&^hdWVk!ciAPxv6B%v(oO zYAdq4FMg30v?!4p?Dd-7+jXu0KHt7V`>D_zeDtpxs_j?YK@Td^8@{yWR*Z1U`aA z_Em5sNWrW*mocf>4Z&0RdCTB#>^b)!?*0G5L}yL6+Bf1m2q!j_m2Luey}5(|(@N{1X#rbU{- zZjo|!dUh+h7k~WzGQ!dwhcw#g-Cf>isgo6u)I>j{oQRCv4;7 z;Qqh)yKE(O#YH*fZ<__yCeTP>Ir~bkBEi2~^=P^~A-XH#$< zU|UUSH3cY;?L0{!=VkB6P$midT0omRZxnfPk1Zp0R0TH~YBi5B|2%*XoeB&?i0Av2 zAd-YrDspw!#1ZW6t1T6MMJF&{SSZ$`Hr6-hiw1Dl{Bws9g4S#Mkk!^TO!PQTOKKN7 zI71oL57}V}q$d_V-L4xF>g{AK+vykj6%Hw&E8NrDDXSWoTkW z_k#t}=4>$xjgcX^9_H2P-wob%36yBRdLziiq;TAYw4TMupt@^-M{htZHZ>Mh=NjwK zNvj@y1Sd>onM42hHta{&xG?S0tEu;Oh&6+quDqz-T$`UOb>pbLVc9=~_x_@360j#9 z3yT4IY?{y{!jcZ$X-Ra6Wlp|ns)rsPqY-3*r6qh0k%D9$uS35+5E@!zPq;!@X@syM z^<;${M707cB3_`U2>%N?&OBg|;aY?QOSu@>YxkXt4~ev59j>mWe&ZE zsMk+LKjM*uxjRU}gV8}8A~j}=1mk=`IFbgx?q5n-SB9bKgdGNbzm43Ui z^Zq|CTLohiV+Ug!Lt~@=y>^>bZe=x<(S3HFTUu$*LZDkv*7RUo9iX%(vSifcM?-`l z+w|$LHjf=vTsj|Zx~uJf`g$x1beD_>{sG}-aUz1*OGb?hk@!5|G4`B3F4S+tCk?^z z@a;J6oVxGem0s)qaDSobM;UPVyJ#f{slYBmI2~x?+>*feJ7v+_N=_QLXUyBP_yhBb z?O~ocd6DDLP9D1Ba0qr$+k`iea|GBix^8|iwPTet=1E#KgdqevRmchr+k;B8vcYIZo}7t@Dj+IYLTHk7AKI;wzb?s@n%lBA9oq=HfIOaVcQxGQoW^I9zO_^(%Nh}Cm&f> zH4rj{>hOy{Q7UvQ#ee?HOS110M}!0|{i{(GRxU#ybRF^*Jj!8)m%+K}TU8v)YFE=y zQn6C?a4HSS3=fa+RNj2jjo-|K-dBb!;HDdgNoUsq3A^?r>(Aj1p@QnYa5!>O90ddj zS~uBmN!f{-;6H1$=P&|KPx)&9Nx4l`vn@WeJGUN1=;2GkFdZ6R+K=33F21B$eTh3y z+Y?La{`K8A3NK&NX=5cOfqKxJ?ZIRvR~kB~5kKPGeL{DI&;p4v5Qo~}NCrVwpC@uH z(|gntA&P3=6LEj`T6Wg9!!DzY>Jo$i zo!kOOJ)}C2`N4M>WxFqvR8BcYS#J@1>cu|TpkUtm7qvr8dxeZyc%aRb%}qbT8py)$ zs^F6$`;E+gI+{I#aGpzM<<-U+ebi(A@vujft5ihSa}#GmpW6zU>1E>g_^Bl<3b#Z4A@mjS?R zh62zN1UosGC2`3BamJ6-N5wi(al;}tl{Nf_QRI#i?{L|! z-6tkD?EdXbUt+N}$Rr6~sexM-yUW!SxI92YH#C!|$P zvMk>ut6(IH&#m0#xAQ9~L9sS8eo9)(rs(tX7;RZQYokkXjdZQ$qWUHVN_C8@nlXDG zxV}QP;+CaeFFju!)!AV(ikR4)4{G-h;D0UEIj6>oO8kP(Fwp;*%ICj=&i}ZQH2%+R zzgtP$a#0W2M<#>SI@MaEmkq~9$-2||ocC}2V5UDgsxTSSP$sjdbz||kl*0OVRByk( z!f(-SFWQyzRd?utG{1&Ykn`@u>p1K5_ek}6|F=)XSZ3HBMqMK*PeB-J;a{B*0s`YK zIFCjbOs6GjWv*uQGd@Rl_M)x&dKrTOOo+c_1B->Gp-!Fu{i*l8A`|<%Rue5h2}kv z-D~Zfm{;Se89=|?p#86I*AnWdT#6`3BgdFCv&2@C528YU^;aGi$z z8q`wUkMDPP!q*d#lq@;Vr0|?lq?Te3T$h9y)Y`lM7AaU_AQM{`7BoO#x#W8lOl+bA?8BCZu z5l``~bN3@>T)ldN20FHMahs1@SF4wCjO+{IEfCXV5YV%aK5Ajl8*>N@uTEwkF<<12 zY1*cs(V!Y)-J`WIu1C|@(CjL=3#)l#6xSm<=aCi1f65T3K7&bv%m3E>`XKi2t?U&r zPRfJb`pWhTw5UB{?G=pM&uqdqRNz= zDApZ$ZK8p;J|{Bt9pb-E(Iu$Rk+k2wV>d{D{xJQ&ZgBsJNL4NEl#VfcdW>9W8#oBi z#E1b1*3c8CLxLeOk@`l`_$6rJYXE4lSwoHV9a(RJH8q8;t2b0Uv8r2!p~zb{P$?1V zDWhiQ(p@}mqZW0qn|!TIH`1SVZ@qUKLmNBwg~S;fci(1Q-Fr^Gb6>n?Q}Mn|t6k-z zP_1#?hY+>|2t?3IA>o4oI6}d@`{a{ zcw!HeOD6!*ga6S(-c82Bjlz%8Lk!hRt=UUBt;*s>?4HEW3w*!9`FzEBO$>OegV>z)V)mL2{(Kawp-3n!R#Y5n@Ya#wB&QC-P7h zuw$rhQov+FoM@szKyP-#hE#&W5Q@^JtwPz)0`<=6a&x{lH{aW1IhoVh*#QRH`E0ep z+SVf}@I;>EGE!|cQKzsOt+qi8+SmQS>I`jjeg3~Fd&l6+qG(&RyJOq7ZQFLzv28mY z8y(xW%`diXJNe>t^m5)k_g0-#uim})$FACc_gZ7^nsbgd#yFj#-CW?RtM;#y1%veV zUzg2xAa`s>4^J<)IrAZPM=Q4L2UQn0rZzZ=9Q@KEm#K~--@!&oH zfKpw={5JFVvdK+fwYrRQENbAuXENG|5mnvJ7E-WcmA)V71`bNDVsa1{nsFi%;E>#Z zbEeG5WMQYP9<;QvYX+neDh)Hoh^@-vi_+`c6;_jbymdDLSW?X#SH#6Z%-Wt;qDrkJA<|wfmJwMRF&-=IOlI?9l{w9}3F)|WC=#?7?I&g>+bF42wL`iC#8R5j zDOwVV-6fVHQ+tw>mC4K6ajxPSO!pI+MvArTd;GtUN|cd*H{Y}5_^9>>6d-$j+>|25 z8?39+Qd^*_Xz-CLaiAkqC9$_c>ef6diG_ShuH-{9OI+*YXlxtta=2^Wmz#+XTGght%M!Chqh zI3pk2$dc!hV;!wP_>1K#Bk7UOwxU_)!YNOqqi8n_>0_*aYtw7Zp z&Q_*J&4rUXxrxPvFo7v^*PEF^^AL%tH@1O3PmY)1J@V&$FTt^moF4VPtAy9b(1FwCkspn&NwS ztGBBa=s1;(>(aH$HEQ6}coOOMTjzwOt}eCV(Z2i82-ED`OnTZM#JR3sH=^Ea^>eA) zw-6j`h;R34`6;4h1whLeSXlfTvL8S(IpDLlzcA1q3kr}};}2lQ9r1a(2~DE&X5n^3 z$d!g}2H_oTc0!=b4Yn`Sk`ugu2_@h4-x^ASVYwyERanz>O@D+>d0007HnODN4vKSb z{(E<`yVGBq#TPbsgu%%I_F{?bK^}I9n@TD`fQ6_A&M@+)3`BXJ2Pzw%^2)j=yj^xY zC5lqT1D)?OiAG0UEVFdj87X=Gx!KdY3xlzCibCYx^J3A;M?;Y(q9Pw#KSsQ;a^pQ8 z`%7$7H5jk-%bPxl>{V(0mwf!;ebMbNQ%@urAhIy06q15cUaI8>4XHPFCy-kFHp6zZ zTcjPv5je^;m;L&fZGN@X#~mnrFrd7wu>0p z2GQpr;5|}TzaLBm?*lydo&3UU`^)i?~WT{*tbR|5pDT?H^?$ghP~j{g(YQc5h)`-5Kr)Jwh!!d=6@ z^mv7OeNXDnopTi^wdu{f_m9}Pr5V4qZur-C+#U-nEDO)H1z2lmJcVZ%f^0W(=~S;8 z;+KeC3gA&_`tCq6}70@H-wAMmYrAq z>n0w7tiH&5Yj$#Apo?Ln?p7p4NQ>NN!wL$y)=N;8~~uFs7NMsAE;f2{((03g#|w=;*(fr zmmrIE?yG=*g47Y6lHWtKNbT}-f(o4E7I6Hm0NB}i_nRng+$_q+pJ;Q$InprYTiz^#22W(a2!Ri8Y*^PExxZ4q40pq?m?tUkuj%EEj9tkRQgD& z!y-(TlP#S(WC{W%y>buy$W#+GAE1mvaJY2w~Rx8A5q=Ap7B&jA%V9P6w6Dm|d$uG=svH zn;ZJSJ#Ti59gMRlUmzG-h-ohU?B&ueX^IExCb{Ez2KpvBT`?+zg=1DzsIsFJ zSRiz~Vjl8GJg0-hX7vSiJ;})N0fr+O6fzWPjpRG!V3qBBlN`N{oB6W+_M~^`=f}yL zf~)~tCUc2W&LG()>Kq+bYdx__2lZZTH1D|4986jG7G)mw6B-m7lr=2ZMr21;{winv zwwBq%9j@~Y=k1R66nZVoVXMvz909ZJX);LYWsAS$X?o}dP(!J6EQs%?i6TyqZ8LmT zADX9E8eH5KJs>EQfU{(&{?gcP-LLb?TetCuCst+(yQlHWTcQR9O#SrV_5uR?O?W^C z#$$fD8zA7;l__NLc&p}U(3ar`Ci+8yk!Uefpd)SD?yUn)y}4d(G?V1^V`1O%j1G~9 zXH|x)QuQvhDi})}Z2=h61+?rY+8DCP*}I8CK_Lk~Wm^~=xY)vh7JT!ugk08^xdB?} z)E6+t5ZJVKF$%NAPVkSDN0@pI>ED3MFMHFRdwpAB;mEUO$QJ&Gy)xYY{}Ge0U-+vB88DV0k| zEs_`s8t12qWM|5$Mc$H>u8fUHm%!O}O82MIDw6tCsF8Gsh!ddZt*OpnDo`kw%02@J z`GqQ;;)}uCRqVLLc?&8=M7qucG-5(1x@4&AMZ1g&Du5ub93N>ddGhnh7eeZ#l&3f*{xtTKzJZs}VTXP)B)=Y-ps!Oo0ynxKkF8COt?y5DsXVb0H32Uv97B z*V_NAaTp~%{Wst)@jg%gNBV};H+cUy+OO|_z<&mB{7>>HMoCTvgbC5tQbS{9Y5BK+ zl^8E&OCWs?O&}_&p^^}=B;F3dZkj2{j`pG9rOf*R@~*Uh`CHVRIev07(~~7JJ#MDe z9ko7+4FMjVSGE4sXl$q<3N3UDq`ZjFgz}s2hn3fM2NR)6lFovIz%#7zpdczjJ`bl@ z*&Ln3{}X#Pmh=<$vs`(g2_>f_Fy5?84)>M^|4|h2tY5H_Rni4l1`Q%VHqO(e*>iK# zUdfO}6?RFS0u} zFT#*7Oc@C-rYWvJB;i?=1%0b>{!pToiboaOcq{C1Fp|ZJdN` z4b+_Bw$|RZ>2U2npHFp0nRya@q_y;a|7~_+invuj{JmGA-_zIs^S%22$l7o;zv_9LzPG?>b%s!Sj-Ek7sgmMV@V zr_^xE7 z1A$bEj5?KoqOgZGtSeV)B^#ZWXJ4GFYn65_zhJr_v3thZ5}I9#=Snn_^;o|Rb6oRu zPV(e{bHP&j&aq%2=yfcUp-gQ@)+sbb{Y_Xq_aB9_CUhttACoYCS=FhP*6Zexc-g*G zu$n6IrXZ>Tk1z@ktU_(M{Ii2rqG9DKwhR-~`1JmaxSSMUa*+D~wOsW-I*#&eYfDDe zkc7?g|Cl&qIJpvA%vw_~^nrMkbrvQP4EVZo1d-jH*6x$ ze_}AqRu2Hw!N@i_j)l-RJS-+-!zA$Wy*&mLv+vr_X!wz_0ODWK3Z97ftk6%!IVZl} zz>NHFR|hZJCRnU-Gt#371B)tHSQqkza4#8q6Y59p(5-2K+s~Zy9A?y~rEtaz-}J}> zDQ46)3I1rhC}>ZgaTd!vvHHA`?<|%e|Fja~#+N|qt)B6MfZXpEJy4M>Li(OiT}hNz zNM8(scab`?Sua7ABeMv$m1hf0i~_)dWYtjxzeox*^e4Ky;^Z-)+b*#d(HKg6B*^fAmVyW#^opK5ri6S4 z%I&@_Gz;g}r8n3U3a0qN13#g?ffsv9Jz7gE@D2~yT8}_8V0ZET$pH*EMCS4~fZJ0< zyMcJz|G%;SkMisztlzll`j%Y&pZ`o)|3{G$qpEF>B7yojJl!zeVB1Hem=OS*X2kOc z4)v5Q2{{l1K2vdtQu&QtD{e((g~g0Ps`~TqXOt>6kySDO(;ou0$4PEYOGzY}t1Rc^ z%^v49-}@N?|NQWaRR=|JU*ys`#*wZdFddE&IGmgyvCSsO2_dLQWPXkZHz;@9%^?i& zmw8Pe(*tW98TRY7Z-4ByVv^uh_r$O>s$NJ19%F0}4X1YwoS(az{`omOYBXiux7no4=5IG0QG z<+aTn3Dr%LG+3v-0B!A@ac}g~2E#*KK1K)H7zlNj`$Q>NC2?aG#zR9StXR|dwp)Oz z4YtNkHujp!2TEJAn6RRnqK^6G3KgiP^Kr%~PrG?Gbb57Q+If+$$9_Qw8-~Kvo^iI} z6CW6}&RZWoOBVOKR%q0#8gU?P%GvB2eaZ$X1#0$DUqE5~;w1khZYMA6jbhPG6sbMq zly{HYBzH_rgQ~cmim5LXbtbq9`NCKewX`U6{n;|drrfv8=9So@f5l0uH5rW@TOC&jyo`T7wOG86q zR-2AL*p>1H#M66HEm6bgpOE~~kEt@*aAm-}W)4*Tnn!3;v`e~F0I{o>zMw;f2WU|J zC*V#_d4)pjNa+#vvC{X6>5uYD;O8-?izpHOu{7X?dx_Ra?{RtyZ#hf}h3y>?P&z}e z0qc*?hF_8Exs)Hb*omg*2ow_J6chA#_uenzqxk&WBd8mXYxLxIfo4!EtZ0U13EG^= z6QQvV{-II>A1F3715L$@)^LpG-Giny!^YM)bk-2o%M1q{$ERP~Luvy(;z$*T_UCs9 zR&=|+{gy}39louYZv@Z&8;g2M)l~W);aNKWSI3X-e;z+A1ymu#Pi(g|Q%tTDZ6tMc zS9dOOgf2l8j~IE@?D>e2kpbmJJhLTS*dXN0C2RWA^1m62UQ<6f?5I9wbFBU{CK>MIymrH#lY` zzIjs7OFAlLFqC7eF_r8y2X&aW`-<(DmT_oQu}$21Sn#%)$1*QY*Lv?UBj9D`n@s|< z9J2DMx>x8L`w%lxxu$0gxbb^cbaW3teXZxnq=8Swu ztw8^h&xoda?=nQA^-jlW(bPrLGiW2ub?@pjjC_wa4I9wE!6C9)@|!cZkTRFH(23GH zM%QS=`>RCtQXu$qLweZgv~5^d*-^m7LlB-#=+xOdIluXE57Ky<1{LrsDF+{YQ$Hmq z(Bett5-h=PtB2~FIm2nEdT*~IGOsJaGwj-wr#6I!r{Nf*s4kpu{40^V#6>p9!nP(} zW+*!LqD?5|o$4J(Geiz48&)JK(F|ERi4bl+qa=joQ(`vE9g`+KXztYqE-R6l&ui|S`|fP@K$}|lG=mT;askSNsM)ccLwt3i~rHFUgjm( zj&GidGwhqPija@5QnvjLq(k)k_8MYaSyJJ{q290^4>hN|g1K33v zPb^PV(P69mp>ppzkF<~xs+F`FMrJY5)3YTjMU%Nxb}-Lx)JiTPLQ}Vf3dD-sClxnV zjK^ki%Vhm>OjtW(|wdg#&CS#l&!Im#Yk6;bL;TvT$K9Ww_NsBmU$s@iK%TTaj z__EJw<5aRqi%<|;occnRN=`48Z+g;AVXe2en`3*T<%{ZEu;3pjAwBcv8!9WuTj@E3 zgUyjr=ECa&#RE=|+-CDV?aQa@yi^hmJA<95%*5A-`I)Wc&m(aCQPnN2mTo%abSrVu z+mls7lSS7_3TKMyAM&@i?9QSP*B>AjEz5uMHf?$whB+uqOnIVtK8Ux80Cn&9l`a?7 zF26u8dOHGE{C$6ZNu;lmUQjCS^ijWFO{i`HeoHM?nPi=i^4<~b8j5vm50fqnP%JyY z^eVpj1-UmK#?aqW>mgheZ}s_di*#sHFQt5e{8toM7tTGke5*lMRDS%R`QOfC{WKfZ8jC@9P{AoCCdrU}O%T0|s;9ZC*3BTyJ@DEhHL#r#^tOvWUgz&q&* z1!X2ESy4OkI8lEy_b~;t8@T?H!loU!?9_h7JlM(_Qq_v~4HBJ{u6 zJ2Z?>b-<40i#MG9(k;T~M+VH->41Rci#f;SP;c8FtIt-5{cRk_>xlNdhc=+s>LoVx z#_A;}^v3!{fc{+&&};dUA4*{P;tuzfzbC-_b=#CM*gGB7lD8jGz;Cg~DjfAa7`My(h`kq~^`Q6rV%{uq6N%oOO+Yak#@1_DPxM z$;6rR<}xq=L7vzxZKC|OI2Y&#gC41AyXX{A5%hb`5mFgxm!WNG`^YL=b#k%`gD$C* z20uc&I>I^V+#--UbV#Mylu9C;;D9sp0Ralu4H&ob|WKbH}m?Th2Esbi(kPMkd{S7QTQ@AHBgk~20a+B zk-P0Usavd!GRa#K!K3gdi|<(-dt`KWL(K<6=pB)$n%h>%=;sDgS%Ic zyH0*O`4P2N`_dfgeTf-u(kL3%`_>sR9pp80gB+|tIXRt)k_Q+$oe`TJJroX$eeJ6E z!8n}u3vxLhZ4@3h$42rV5$7SgjJD(-kvGgGJH~fCGjRmB2Vog)Q)G4$x2$X{?CdKo zEw;ba7D9Kcc}9Ad^pWnucxZm_&l$8i;}6uGOLC$=haFhI=j-7KSy$K8upFPzS60~B z+SqL@E^e->E^JEca;dM(XPBy0Ui^l#ST9=7KnHb~k-N`WKhKbBmO;8pT~?>Fx3ICS zuC~i}rK@K5AXyLkoOU2ZM*uep{(F5Rj%k{Q6|KsO6zf=+$v7o9Z5w%x*M9|>sIJ;v zWl{ZGS%PPYV`*t|g|$h&d6}JkwPjRWTcz1b#mUH3S4~DnM9mjY3FQWQxI*Mpvx4}ZpL?$XAA+CZmGo_Z-rSr#SA zL-Wq}DVVB<6z<}VGD9PrFfIRANKChAA#0TISCbnuLUoNbc zMXr=3-9@MdN(8-cT&kwo7U^btby3TEHMMidcQ3#|RB9wtS32Hvc@fHM#?{qY-@41h zqN7*O6lJfmwyK|-2efcDJ0PM&#sahg_iEPgXTA4@u!Yi+ktPlX9K;^uuj82F3cTD+ zD(y&oRLSE9RTXGM1i-LL;LH#h9>66J9k)=`VfZXw(*_ENn&=sZS*$v}g}qwwW8c!1 z$8bUO6BA_XZYm+X3j1GTL1ipHr21uLm~v*F+y6c=HcV|Q#hlk~D`v3~VPEq1oTBWz z3>Fks*dbd9qeMZ+jeHh4ws{+KcTxmOT(ZGBg@rHxExK%Hj<+3nSF1Om(3BbqyMo=d z<~Qc@B_5y5wpf8@5&27dn)V6`q}VGP)&qYec?^vplB{{j>-s&*-o$#%M-5UR|wgrnnLsG!lA8z6h$&B!{pG zr;x&04QC=4!jY2%@EjKO1OvX?On8{Bc@YWj4Qk5qs-JRg?QSxjVq%eM``6@(-< zTqG!lgl03S4tVWX*O;E+i3=PRPb12xd=yEV=G;dKSd-biF-&~R4HiY69jOBvt)>Bm z^MaZmp6r}SMK(b9bM$SB1SQssdP#fMJPoqHqeNFmqmpc+)|Eug&T+O7#8zV%;Mx6& zm7=8GWTYr8AS@?c+Chh&4^I^VT<(}xxh@A|4misPXqA~4%5GAQ&#~K-?Ol82jZ)O1 zakAJ04TPnxA~>H+XVPm-ofO#7jCjnV2oxN%CO4xkuJKk%j(KDFSk0&dDcIFPbxeU3 z%ZX`~W3w$8XM6AUrV_L@?wQ&Px0}W|b4<>E>9~J7G+p2o^=F|m$fb}5t9IfSrX8P# z4bLz2usCbB-xH7GCqZ=Y$rmV@a{Z)(_g!voV0>VGE$m!gc-1Qm0G*>p^a?7 zw=WMgcB;Y}hpXghCcO>s9Nfb1g0H2(US474;9$3ZAVaEos8@a=#_I`idFr8u$*Kh? zRN1*lI9vOP@}WPEm;2z8LE>9v^UbZU*5vkdSqzv%&67KDx%l zRH}2VDFKU@xqHg|>=>}xwy!V3MSWFe-nawjG&e_zgD#;>1p7$bqWJLN`9J$eo+5Qk zbR)8P+feMuB@CI{CyksG2Bf@hg}cI&Vx`mNfP#dFNN)}$GMg7j&k=rct8pvdj5vId z8@8tsEr^H_afX)sDBJKm|4@|EV|qLWCGca%y`_p`i0Zee{<7x?aVDpxCJJREDyB4t z7_ptOMi&ZP!el&h!)&%B)XV%NqXpylpTxMB1L^-x?eM#g6;k!QwRs>}B-`Z>{j?s3 zYuJdWssU}I&PB8L?JyS^7D^48tVSQ`|$Qu>g^OZAB zi&AcCUR$%}y@)jYh=6E4x1`2Q#3kTy*^6{=L%zBFg6e@(;eh`eA&`9#eC&$lI47jr1>NB}$=LNRSB9{soR z^iuW2`0?KjbddM9Tuu*Sa|qt&pK+Xm@bbK)_ym{YF8k`DR1bunQp<^+$ryFQpdCIN z7T`mxn3mW=iCLGxkxt|e0}u9b&dceTmo>ZA4%IO`n0YU%jli$JXJAE4s9)nMzar#T zYO#w9gT3axu`TN6k<lm-7)VmWbv3F>ni z%^URn9nSoTnXhDzA<4}U9?>5kUHq5TiDgSxm%NF)Y}2SArvbRSL!S)}3|!s6fg6fL zqKz9eP8TChbuv@l-N1g3nPpnW^ahP_e?8!T-M&5m9QI@DVJ=39@KWW2u@^Uk&=5zC z_hOpi^TKd=4$chD@a+?V={SbpXKy};4g@XK_XRaNv_Mc4#1SM?`LIo>Fr2;1CYcr( zC?Gmu!ap0pAA31K$3BIJ4Fbef`97ikNbn9M!Z6?LBZOS?3{gT^8ByaGe|sIvr3drN4Ed-N zb@`5w!}#{YU%=EPuxOwWVg+l*UY+o1Ir!Z@ZrSDQyDuP2f%w2S&U576hj5C)+R=zA z0v*P-!9;vuk_R`L*o;p`7~@4R7X$HdHT=cKm>rk%By5K62MZ*b{%+MQNe=<@E{Ar%YoEOiz%H&PX}F+We%QGEi6g= z6WLU`LGa{$ZYlK;<j<@Y6pnVXZ3jP30)dPzozumr?$+Ele@?brd*Ed?-LNXg}ds{KcB5 zG&laEx{Ui%)7%9gmbr7rj%@7J+e=tTq}ha1`WP9xZD$-U3?8DsM$*14o1@f2%GQ1} zmC&s!ThnP>+jTlD?GNiIB_##(hBlo4wvfeOdl}3N94Ak-s+bwQ_~(M0BbB5#0yGNg z7GbG$(R9)}sFpa@c0!9*{UE{WJeKJbqcoHraG=JctF&Q&8Js4dE?v_NX$3_0NEdjG z7-Us(b$b>5fZW+~UZT!~5Cois$9ZFRjQ!5*kc}8ubcn)VH)KqYo`kmYgI-q>K|E|G zF(g@uL{zXGDYKX1Y=~K`QZeUESqhbFejIsF*29NsIDZ(XZIq=En0UYX%;97?#PwgC zoYPvMwk;2fUPy9mc`LkfTP)VqVzf+ygp{F$&O96;AgHIU8`9G)s(@0^Z5Kv(EOPP( zLfz&BizAbK14QffQCkYhe7*PWA)OJVuZ}q zMMYT~=x9aYb4N%cH%uZst7TH@=Tzk*6d4D(V2!gL$jhM(J+q|s$LA703V#h0Vj_Tb-?QB!A&l2sIZhUACzyNKX;pfxYZcn+4g8BNqIBQM#5mww`U?QpU#Cy2faK zN?q(-0TrRc&2UL;)d~m&kbwx{Y%h}}dLx$jK=9lj$u+f>nvHWB03;&vpx~}NLi>zY zXj4Pcxxnyf^m>`wRc}P<=q0rl6Z!RG-cu%ZoKnb5qMwIFuEexV2Acl>P~p761+X@i zj&7(Y+<6N>6r9**z)*1Nud`CXgRQ2fgvRyg+|#Ju-9||VjjO8IQT_p+JXkn0Qqw?H zR%3vBQ2wfz<4Z)8S&l@UzA)%@$K7MamsJP*7NrbGYvQZp>dfT2wTF|Pb;Z@i%_TkI zHg9y`Iaf3^+Naj#<#sieN4~EZ`Tf)bE52SLL~$P4?`S_UzFjQ#bz4H8d0*J*ckBl< zc{RhuiY$10i<{dk#x%A@pRu*AQ%bTFFq3PjWVKsnQvVPiYl!!lswtpRiP3)2rU6;7 z{o~~HYGtyM+6?l}PoDmX)Hg-hEw8k@mP1hdbxqn&97bg3vk}Q>^uNB|vm(D=8SXD5 z<43jvhCe1S@w}q>KtzzVn@Eugx1-qZ|9vjQ=3(AxZoDy~-#HPYTI zAoj(6auBD>yM38dJ2nRv8GVO926-k?Zx6YNv(xzy*ui^{z^WLDD)N1I#FOM5yU5-7 z(X0j@b3r=U<7%5wGiQ(vL@v@=KWvA&sfzlA)fJ;>7gDTtugX>5X|}moEUaQga{ui& zkzsUnhNYRd7fwx*KQ!`*nA;UVbrX)P0geY1BPGG>XuKd4ndT-l@NLAHFkvKN@h@%D zpo$tD#%+8){N^_P42d{&V~hqD`9zi@dpWyLjpE|X_Y9*gZ-4`BzILoiD&bREOZP4r z&7ZYIyO&w-nCyh7#^H=f)=l4J3B&wH!IhLjw)5bW0K03t{E?X zTQk*htkm?j8MdW_+<&pH%yMhmL?b0)uRX~)^uVOz{bQ$%IEM**kr~1&>@a{CioLJ& zM?B2yEx`*cB7dp=FZ^I1(2X0a2OQhpsMFTEkDAgQ_>CJWMtro5n>E(!rVqbgn^_-o zotX>tR`*O3>^1$GE*pO43tcu3vz@&CskZRP%9TJNHo_cGLjRbi4{FemTKm}>riTmF zKylGOjUEDRPmG$A#-NbAeNlORwM@A`aS|P9gESCF_B__)`w~rgkbFTMkf)R^%ygb) zI1|_oZt-!MJADU^Lo^Ej+PvLG@SnvtRCt3OOof+cp(igxS0AB zbb<)0y#%3h-n~Zl5%0`$h#+_^Kl&7l`QtL^vU~S2_B1RpGEVU6C+2bsfp`t|wp<;U z2@;tdrd&ZLJV5TZMquibo=oqu5V{>Pc}u9Sie?-_LDsSkJrJL`fbM##vE`8#RUY4 z1=$ML2=-6{k;t7#G^S0uRONp{Y|~MJ!_s1(Ic7qX97LQDd4WQck^!ed zSZ9gmC35^rEjvQ<%@~v2W=!T?!56q77ZdP(mbMTEcG^Are7*vz*I}N?)BKn!<>U}g zfM09bHGYYBRkP902=pv8D9=TF5mXv7Q71OmYj zKoSqh!-2vVd(LPA0Llwgmqf1s;xB$w?00RGJE~mjH4l zP~Qm7k9rxJXd0EiUg)csF`#_RCxN$pn{o&OknD`_D-S6?z}dJ#x)I%`;VA^VT&Lq~NAyz=o6P@XeEHk}Y$G-V>WX;@1+ z?jjMY__Q2VNtL?khZ_cD(vbD3d7EKv@HY0XtLqS!jdOhRT0Sp^CcIE&uO|Sv@>L5z zXF51k)MJ=|2;Z6p{Mrh4q4mM~Ux|N$)|8$_m%I4ume@^7Rv-_pQZ3-q&XnSRJR&(i zSY=UX23Uc(s1Yam!d*WahoTtuyFe#~ChDZSe#Pv!)~RD!QJK%LK$ z67W%^4*4Z7Ur$<6c!r0Y*msr&TVV|Cfvb|gsg%|y8e)_#c-w&$`Wu;?Zq2n0ZuBi>}4uTeZ6DoUu z$W0!hs~{vN0|6%+ouV z(Jw{=w~9t_#S+!EHU7E~hO&PrqvaCXdVq&=;gPO<9dW!^xfd)tyu z-&XlRw;Ox3BA=1)cHvm=hgd%s**=m}#GGItKXGWmW$R( zvw@%T|G;QQOT7$>l8V6|#}9mwr=;Tnb!6d7H%K2MQ};5OX+2C5wjNKlD4TTf*=ATM zT)YN_Iu0c*Em8RkH|~O2lGHC*j5|z?wyR<$Sj-HAPU-v|T?f~Z|;A4!e)Wf6?_8QYjMTSrmj6~kKb$H902&madmZp$i@adhmILM``kD1sU4&qir zMvgx)2hBop!n=@xKY$=Bg3~XbDZ4j8O}!3QA+4lu)^x>cEG3{fSJ&w}T+Z@yWq$p_w%uo;u7m5up@c@;{ApU=b8``TgLBhOj#t_c z8OM?o1@$xIf@|Zwn!4y*ub!&fp(a&;s&V6Ba+esn;Ui-IWi4IEZNXI19W7 zRQy^}e3L@I7HCrP(f*#Rn0zOGu@|0PWpxmQ?h~OMnLV8!O8cvd`?+xR&QIWVj{Q=V zK7C4ja(aWOB539Wu_IKw!dRe;*k&jhUsgQnR+K)+{gHo~N3Gpg7@Vhg7V1O>Yn>Ej z5ah(PS*`>9OsOC8F|Nj#rvnF(TnjKBXS)q`!s{yLMYy5d33N}!*&{jHxUFwMkx9BS zOuyruR`d%mrEJ~PIg-~YyBxk6@Ak-dBG@kE4UeM|7|}=)dC9m#?5W|6B%qAjlNiT& zslO{3RoO;ENqQb+J@RGJeHBci_D3nDB#2;1@{fcYFP(qjrfh&$j8F{B7QN`KOY3sM z(7G2cV3#cJ4BXiJwK*U~UVIOqfl<)(fa(!^9MVTr0wDI%o9Q8-=lMtwKwKR4>M#7OsoI6 zVRq4|IEk|r=cQT*?zBOvxTwZ*#}U73f|FN({dBoCQ15=m_rh{-RSjp%lCe2>!hVUc zxiR9PK>L=){BnQ?*~5jeV8Lj}CLtU-3t^g0VC?6DF5wyxj9@{a&U&3a#6>VX`Ng?% z7F6cUts{4{!Wp%TGmTXjwSD811MSXp(vN>aOjU(;__I`qPCUFAel+yQlOzRha(}Xd zTo2LCba88^l!3EU*`?OA6p;hsCP~P+5Mw0Mf|zr4U!xO3pB+~E(dY7_5>x!9JpMdaFSyq9%?2dapOXWy ztU#5sejGwmSd4u!aLs<9OqS#ugOA%}&9lRbcfap;$A?OvP1*4Qm^nE_Q08*f3BD^> zoKlM?PGRZl+fzB8Ub~d)MR53=&-!Kjy$TSYoXhq@Ksz0K+J72pXDFSKfYbwFzOak{ zUW>a;K)=x->)MRa4(iawJ*V@uRNeHaw*_MzFNe@Q|0KvXksDLOlymK6&=CdX zZG!R7%eTu`-+1lvjL#RP^S`w;5o}Q2zLS#gBi;xbvh@8aB+I7^RWVK!&wzvBE@l35 zmxLTz;-7H?uMKRVpCXi`e|8j7UVc!bI_ga4_B)&iagKRHigOYCiI|ocLq-6|o*;2f zy~3oO_~4>*#SI{zFc=o!LzOvyLQgyJLs@6f^ogG|I;7vPm}K|D*?*^*cK3^p%KGY~J`r$izt`71c}LC6{_1C& zo?)zk-YmCLMw%f((}r>e8qGh@-ch>mk=yp!jJu%Vvf!PmCK;;D8RpQ)22TGWteFf#G=dZu_DOoo6 zCXp{@9YZY6QTgpsvzSi|ReJV{VvnM~u~mNOLPZmwLf@eH)QnM7jOXw>EMHMQeO3@S zxj9XL9P)cqAW%nkD`@lSt%*K{XAN(fAFq|r|~hoS+W(g``EaFHgqv4r{_6> z%T`xzzg=+4`1={3&B%nhT?M5+OPVx{rMDTjRE6sYh&yR#LmjQjCexOI?JC0%Y193R z`~8#43hJ=ph*6jiY`B8OFw%=UsiM)Kg&PCCyc8hq`g5@|c{Xo4OxzW%smyh@WEnu7 zY*0xzTb}b%*NVkPNqf)9)s^_OWAxcaw;>@CywOWnRJa?wSI8c&;U@E;<-$sc(B|UiqBf$ZQ|C8%@8Zw!kYoveQ~X#4AtwJsy8;aLs_GD=Tuv z?nv7-%x{_$ZIZe}uIj}io|}TcIboLQ{iG<@Sl^9h-RpQA2r2iLJB}EqcjDzpT-Wjf z@+?Q}AoNv2l^Vyq|B=vZj8gt5~@$0_{PNm46*-Hty89=uK319agynk*B~H|8m?APMnr+<%4cXslpPS;A?~1 zISEMoy&AleLXBFaI+J#C>>te5{-)blGs!NEdMCYy&qv% ztk0)v`m?=wY2oB%Fl7cva$UfS583OEHOf1G*tmfr0f}E?dRbf|38ystZYraMFEsVi ziQ%7rtiVq!3$@&!rE?gQO9o+`4>+B&9#DOC{ZN{v9RS-o!QSsjwKlyEkj<(d@YV|d z(2b?uKKe)3HVI#lrwV(($2pHO{nX)#vmv)%*vl1RG_FU~;qySSP7TF&>S*I3=i+Yl zpl`|&$P3>BjDP|8cBK%om+C*?HcH z?GZ(BQ6T~)ca63N(O+^6Gk0@4v^6uKsDE;}ODFM)T%xK8*uL$jmDyYV@jo4y~se=-~7(- zOfk&jEzjV`C)?m@FT}3#H~q|+n}&(C-ISIDRc58?z<#9%g0F#(+IE+}%7&iOHrx-r z;$h(+7l6JZTCgWCH5Lbkvn^Xt0_el0XWrN{>zuK9n|dmn`(*g9*S`=2Tdz*O9Yz@8 zfBc~S-(LSxv3GScHvP{VfEsV^Dr)GT@`+E|C8-iBkdVdI#PKC$NSB<`eR<(|Ax)g< z{eiL0nR@dZC@;)mYQM#$<%0u-x&KKj}Qdb??b&N%h5AO2sK*w z*@+vZNmak9vV3Rwch=rE-Yy$&t~9>hc8x*n@W0c8FyDlIdb9y`u^&!;g3~va!@oBx z_uQ4YF$UCLy1`in$HKFUNX=B`>EIwlB#mW*@PsiBoIHi$vEbIw4|gp_t=se~o}{d6 zaG|+*@x<`LTx3HXFv;*Fin<|Oc*_nDuw$V?S>!ti>sYz$@R?D>W~NE)IADx(xc=6& zL(v{P?RfYbUJxxzOJ)bGDm=cN4u35Do7MzgFyvn+=MS>LVeEvJaZl0u$PIi^Wra2j_d7aa+t)QY zRV&AB$8C5#wV0MH$+bqyM>G^3a$FAB=bqyGiqWF#C17DL#k~42Fp2HN+5q~+-`cZVwN6A zkGSr~X?jG~8yISgjX!@|7kYq=Z-_Z3(if;je-3dMGDPfqo@+HR>qeO@r?I0mEW*(?(jCrYHlpYC7la0p}JsO_s!FL zdYCGY*4HQ)HA$f{2&0QKGb|?FccRd+Z@#NZX6CbA6vTxBiQ5OYc97Tk#dwQ}&v6Ty z!#W%Fc&K^e0#_xn*hk=~oM-$gi;3B13;r2g3dWd#64Hr47i1dk33=uF4$w ze>%GgsHnE@Js=1Yf^;h(2-4jlCEY0)XlwdGctyb^ATGBwPe8*Pn9ia`8Wn=(mexuQ!+% zPmF)DCVh2!lO6ubYyjc9h^zr&TzjwVVjfTX2B-DM0j3zfB|Ed39CfYD+{xw9VgstP zFY=QL;9=7JR9cRDllh%5U8llP!TR%lcFbkkkw?@D%R|uo!Yop#+8O?7KME%SiG8)W@KsAyw{mH+(YIW#j$3d6v~3$$qkUycq>EJb@TuG^R4E5(kN3DLgFzdkL=irZRG%|g#@+PLR395I-Y_Ff zZwjmSgX44I#`|AF9OpI6g)^G$sH1go)K{Jq#K*9ls1C#zR8}|+^Y#}MEoEQJXQwi@ zPWqG&y`@&hgRhCPsW!<}GM}!d{5i3(=wUQMx?=-S$nf>!G}YY#tD3g=3%stqpQP=T z^>Q#haEn%vI*dRfuf8Hp(oZtH-1>O?0d-dB)k#xwTT-)em9p}5md|AET7v8bSGq87 z@ACIIJPWZbhrjV&a+2PL{l-W$+bKVaVo_+4q$sNkoTuk|VQ!b}A)*2eYeVc;tWn&Q z6_eE@)D5B*Z_Vno(abv%35v#?FH629)_Ix`z%}eyRsA*8?j~h~1bqh+gwncOp-O*Y zykc17jM_@wNn?p?;-2e^^yz?vQrs#7;zw!@RVNOIkp*ZB6mit<9@(YItfQ9jtrch< zVtn`|pqrzIG=Wl?^$%Dgu34zb`s@)cCTkV<@98eCxr`l7a)hi-@c0Od%P1ctO59Ze zQ)NX$@wYy><}3=_>uokOZ&?fegz9!4S^72KPXzyw#&D%=;Z42`DYuyw_nGySJb7o- zL1U%2b}vSr>e|2JjiiePAF&=BHZ9(?EDoosjin(Xj`!=_w~AWM8DhVsirndsj8X0| z6=}k>PT6WRH#r)@LfPEJbArKs8l%&+aC|7{(W~lEhqo|J#;2Zg3HYr3G*)Mm52Dsu}p`CN67R=$JzG zRl%QTF(7Yu-8EUfSDVq)_r@86)O|EcOY}wW++V?|c!qV%NGo0~TKM|8tr_B&-qqJ9 zsZOPLXB^2ib^;$iU)|*Zl6Wm9Td4F)AB&S)qfNeOf5Ie*ZS}J#;P;K*CCYj(o1r_v z`&RZ4f|^G>z^Z>^)kJ`Ds=HuTiU*CDu;)Z+`6e;1Gf=G>-1W0Zz>ZwmYK8Q-^dJ%Ag;`+!3g4dj{ zmq^M@!WUvGgX$L{u?n|wx=kak0=eb;ukk;7;S!!AB?&?4+NFD)bHwqPS7Trvvd1r# ztzhIQC-Kfdm=QYXGPJ*?65V8n&s}s-29_=eFME`o&ZjbrZu~SW{qw7jjGbLi&yl`w zmw*r#P?rhVA^&U`z<+Y$sv-F+fxSR-S2<|ogOAEh$>7$${yJSaf+AT z;2{|Z+Q*7&5%Mx{UJLG`e?dAIv%Bh_v< z3I%Ir9VH4YJ_tKV7S}_Ej^IAJ;Jq9~WBX`YLM&DzS<0sBdO-nRa_r>RO&^(Z6i$6x zjw{HFZ|>o6nZO5_N7~LrXZx5jGmndHL2<04gM7?XCOGx+Ja|>a=vmdf2?q^}ADS2) zbl#vq*Dk-kTaA-tn$}W?+bVd3kt(9D?}N0A3Yq}QeAv6|f0Zdt8om9-ha$^_1KbKV zioSabCm?=}Hb)EXA%h0)lT@f#(7j+>MDx73TSg-Yl&ag%Ehr|d3f)s&@t_Wh%=jTi zoY8I;yd+P0CJ7oi%%EK%Fzt&-oUNEh{9B_RBZo-LpKlP{k=^i0RaIipo-8>@2VFB8 zJ)0%n7@Jj24>Jf&W!0jSW`^>FX&?&jw1;_-or z7-Mnw@Jh$WjZYgZi2M!Y{`f*W&{JDCNWB69^0i7sXIIbVDZloiO2#PBY=2}Tlse;5PWTea_UbOSj%;7>{o)zQ zqhPNA1M_TgdA1BbZ;N6$>_}(Y(GpXM&#P608Ajk8vRdDCF7G89V+_Q1K?L2`~lj=L_HA;oUf-WM%OZm;J3oLy4MrY!_aVe z>=oTq2l)b1{CD;JJ$>F(&+}|%2EmhF5qCzuPM%f+|9F%g7j-GC{8~!1@4No$`R96| zO&oLl6A4Syt8@)-n!_}x@5CsV)}SFN*&EAAX=RGK-=gngIA5Q*$GmhOX&yt`NuF&N zXWtJ!R>03jq4+vd#N!f`5P^fmcX&5lL^2@cc#+~}F>{Zd-99EwU+0*e>&J{R3U&OY{v9 z$_$1ADLgWFyR9S^vNbbz0GcpjG2Bt{vDkIFF%q%D7L*E1O#eJdK4uS8B%Jk9D>MoL z&BEvLL5fS(P8z%V^ycw>?Iw#6%~AJRE2(zwBHuj6_3}gDTz(%z?s~h7g$p8b5}aUP zisIgMVI(K3h5Br8xCb5o?O08k8vh(;wAsVbbtej4?XZeGu5aC1L$ z>y>jUrUV#}8|3N74XV5Fd$;-q_tlR}984+rzF?7abrlsTsAuRXR*f9L-rNmg*0nq`2o3_$-2}*m< zcdUX-i6Vu~N)Jl*YM;T!2NT^w*Q<4BPtKA_HPKmtgg*f*z9SD|K9+tDF1FG}YT~r7 z^;)?np(?K!U-7Z0RtFJ@^gv7 zMQ!_BioIMSyuA-BA<6<+JZ9I{5h_NZEC~8L-dBwR(YsRM@6_8j6Y7i_^{DQC{(|(X z;ba}px0!+%#R@NyQgJZWGkR?i&%KZ>TBJp;hlIaI-PWwlrj@MKAPP;sWPiHU9etkC z@yn!*gl}maN>p;~O)%y&EWfTaf`PK zphoB|_igS(Ppy_-VZ)slI<{*y4B;KB{$v~jnH%A7%FltDM!eduC(|wGz@=q>ogzh( zC_^sAG|avYU5)Z>&%ib0Zg&Yhc9khSR6lhdxbxNeVD_$i&ePU0pK;&ph6vXxSiOi` ztq)PI;*e#M_M1ACbAgkWWAK6M-@{GH%$5n&>q%`R)o(d(Cjd`KxFRQ8%~dFnb4W4A zNouy;T;7xgr`#3x2x=VUwhxau^6h3F6i=HXFr2hL>omMMOB(=plGto%G1sH{e7>>P z)Ne=6B%xyU<4oidy956`AMtjpQ~A(<&`PW)wb0^A0b4qpD6TWnfND7hJ*+HUFs$vf5mTM?O$g!poG*E5w3`yh?+fxLwe>ys99opCfd4_(8rNZH9SBxWkv7 zx{mInj{X;3NUTHwq{K9AUr!g)m<^3j^!d~^$xlg{`@l~Jo|Q7}93>AY?BpiWdc3U_ zM>_YjgiC5&noB-9Ux}N}NGZ~f)+sHqJig@mx zvRXsi3GSMDOrk6g##(6O&06T5mUj&=4<8Rn5(0kAI8BD)Z0_5423&iCZna{oUF>`F z!6+1F>9W{c+pzMS7yjpqzL-U82It z*oDVxn(vLrrSwHxWlS*5Wd)ez^0g_8-Bt5T9Htf3)$g+7&EBSnCf@At20cze-nTl& zDO@P+i+SqDR^R9>N;Y24ql(h%Ux^SC77LXbC>5fZO=6oif*hf8n(j8z=vEHHtw++k z#HR~T(JOR}a4-mHR&Z#gqPtT}yh|Qd_Ot&e!as&KEOn3Bn06WLxe;tNg~cd5ltDzw6n>~@}{@I%pL0)=DXJ&YnV5jSGN5usxCiM6g*LPn<90Z|%tqCz_=*quYIt(ax3?+R>LE4N^HJFCCDW5P92fzWi2 z{6%ju5qxa-bvmPbFstFTfaxnff1{_)i;nW(b7j_kf7cJsXJiS-hE+>5L(ogcirK~1 zwqHHL7Zx$jqGBM+M1N^7!8R%JYLvF!=uSe6@&;92UpE^sT-QOLPJgA6p~AHIB2-6z zP?I4i$(9qQN9t z_gNVhvx(O@gIK9*t#y6eQ;a{YWAS0Uha_0ZgP}_vi{X(hopUl}ykdBcO`XPQIL(dv z*sx6TJpbgA)*T+a1E@S z6@swNaK88bqsKwy!wk~ujK2P_WfHKHx>2vXQJ2x`ed5K(w9D3d&XDqXzEXLNA?sdv zkx-@PQLPQNK;-PRh@o3~Hr|_N+@ZWjuaPUrb*CQ)9P=8Ub|+Zsk2|e|kGC(xAl5&w zt3GzYtzLhOo=t9J59V5kP^IwWv6C%_(kBc!eRNG zj*~$sUQt(mhIBhc1Mp13gYDWAALD`_RwOwC2IJ)mhP-*jfgCd|3{lO+YsT@}GLbJ` zT;nFr+>?)<3ra9~7~LOvs4>Q|&7&WFI8O0)OcB0{c3+)JLMtEBYD8H#3e5prE8rS- zRm0rTy_VU`?26o2^7&nTaH=8MohgSp|1Pfin*N8$c=a^{v~jIfc35piT__R0s-zl7 zjSw`Ulm1;Scwqq#4E{pRp*Ab{o+uvpoJlb>H6^dWlclgXC?~_bo5^$F`&+z$o4Loq zh!ZASt!32%3Li@S`0S{6;dY>h4CxlAc?#J^L% zMtItb@gcY3)`wRfWR1uvO}pW=cKaHl1=9IlG}!L@;j*NCZgv>;_huPit2(E6ZT5zK zayU#IhwQR?K<$wqtb*#eoYUA3LE|rMJZ06ILhXtv%U97Pq0uz-76}JSla^?^#~=-7 zj6B)Y4;VyenR5)&{-z&8RC^C7wNt=7XC0A-AtS1CNgI-CeBkDNsUZ{*{>?M%#Jxob z($OV{2;XU}0(x=yLbVFi6u&$tm5>Q{9!Lm+4 zj@oW7lL|AdGBa}tYgq{^yOOOFx)L)hGxORCoeTrrK;K|F$Jmv7dPN86rz7x#IMd6g zGc%|4Mze{h2M2xi4thoH>5+XE?aNqJc7T=-96Zhy6u{ZPCuM-T4Br|iXj|ms-#@b~ zmuAQyf1u~j>~8by0T*0|Na1`lx_HU-qyhf^X(lHkFC`(aqRJ>Iaftu|U6l0xixCLW zKK#}$h-Cmw8@r1H(50)e7O3A1Ko=I)-z*Gm4UNrB|J4wX=Kc#+^pA$$vPS<3K>XeQ zf6QI~3~-sl`gs>_{Qv|4R3|Pv{jCe%J^2oFQP2D@Zv7F2ii$-)YaorKM-Kv=xonx9 zf&ki|L6DUa6_-~LzZ}j#tOG^3eUSk7Nr7uGa(CZbrsw31zgWvFr~>wXHNJ>N_G@Tf z5)d|TbP$O3yCv|N>4^@E@UJ=|;$&!L^GFs*^qJcIae4-eUs~$VCk;rc-vTBV-w*i5 z_`k-NHhIXxChKTzWNK$=X#DWsh{pQL$DadII;6l5TvV3--ZDLtZv7q+`@g}Worym!^g!*qW$XGc1TeKm?{v>=r zq`QC#1%&s{mg#By;Me%_HVU=?`aioKow6Vh2rL5y!1W*3+lKD1p;b)noUBY80Cidi zQx^wOD^tTqKU>&=I(&uKfIb8Pk^XVL^%?&L(Z%PWDbS0bP1XiiXSNOCvoINuZ~

    {@^= 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�K6;V=*aS?57vP%s-9y`tWD7Yhw-3N^9ART3Y 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

    *t-4P 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%0Ds8CoZey{zGxZO|* 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%0D+z!q4W|gWYmzFcbH4R+r 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

    mobibot was written by Erik C. Thauvin as a replacement for the channel's @@ -122,7 +123,10 @@

    mobibot: paper
    mobibot: rock
    -
  1. Posting to Twitter and Mastodon
  2. +
  3. Posting to Twitter and Mastodon +
    mobibot: tweet hello twitter
    +
    mobibot: toot hello mastodon
    +
  4. Some of the internal features include RSS feed backlogs, rolling logs, debugging toggle and much more.

    From b20783a18e68d5605795c7053a8cc1b9da7db44b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 4 Jan 2023 02:55:12 -0800 Subject: [PATCH 709/858] Added UrlEncoder library --- .idea/kotlinc.xml | 2 +- build.gradle | 11 ++++++----- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 7 ++----- version.properties | 6 +++--- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 4251b72..2b8a50f 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/build.gradle b/build.gradle index e3d2267..d05bf6e 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ plugins { id 'io.gitlab.arturbosch.detekt' version '1.22.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.7.22' - id 'org.jetbrains.kotlin.kapt' version '1.7.22' + id 'org.jetbrains.kotlin.jvm' version '1.8.0' + id 'org.jetbrains.kotlin.kapt' version '1.8.0' id 'org.jetbrains.kotlinx.kover' version '0.6.1' id 'org.sonarqube' version '3.5.0.2730' id 'pmd' @@ -45,7 +45,7 @@ dependencies { compileOnly(semverProcessor) // PircBotX - implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT' + implementation 'com.github.pircbotx:pircbotx:master-SNAPSHOT' // implementation fileTree(dir: 'lib', include: '*.jar') // Commons (mostly for PircBotX) @@ -65,7 +65,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' // Logging - implementation 'org.slf4j:slf4j-api:2.0.5' + implementation 'org.slf4j:slf4j-api:2.0.6' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" @@ -82,11 +82,12 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' + implementation 'net.thauvin.erik:urlencoder:1.0.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.6.1' + testImplementation 'org.testng:testng:7.7.1' } test { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 113a0ab..bc9860d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -33,6 +33,7 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR +import net.thauvin.erik.urlencoder.UrlEncoder import org.jsoup.Jsoup import org.pircbotx.Colors import org.pircbotx.PircBotX @@ -46,7 +47,6 @@ import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.net.HttpURLConnection import java.net.URL -import java.net.URLEncoder import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths @@ -152,10 +152,7 @@ object Utils { * URL encodes the given string. */ @JvmStatic - fun String.encodeUrl(): String = URLEncoder.encode(this, StandardCharsets.UTF_8) - .replace("+", "%20") - .replace("*", "%2A") - .replace("%7E", "~") + fun String.encodeUrl(): String = UrlEncoder.encode(this) /** * Returns a property as an int. diff --git a/version.properties b/version.properties index 2090357..61222a3 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue Dec 13 01:22:17 PST 2022 -version.buildmeta=876 +#Fri Dec 30 11:11:04 PST 2022 +version.buildmeta=936 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+876 +version.semver=0.8.0-rc+936 From d9e50166899696b31a2336dc90c0dd2070d07a86 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 4 Jan 2023 03:00:07 -0800 Subject: [PATCH 710/858] Fixed nix3 IP --- .../kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt | 4 ++-- version.properties | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 734bcb3..5ab97d7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -48,8 +48,8 @@ class LookupTest { var result = nslookup("apple.com") assertThat(result, "lookup(apple.com)").contains("17.253.144.10") - result = nslookup("204.122.17.9") - assertThat(result, "lookup(204.122.17.9)").contains("nix3.thauvin.us") + result = nslookup("204.122.16.136") + assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") } @Test(groups = ["modules"]) diff --git a/version.properties b/version.properties index 61222a3..593b23f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Dec 30 11:11:04 PST 2022 -version.buildmeta=936 +#Wed Jan 04 02:59:39 PST 2023 +version.buildmeta=939 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+936 +version.semver=0.8.0-rc+939 From 8c1327655661967752286238eb1389c367b8c3c0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 5 Jan 2023 17:07:01 -0800 Subject: [PATCH 711/858] Allowed longer response from ChatGPT --- build.gradle | 2 +- .../kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt | 7 +++---- version.properties | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index d05bf6e..178326f 100644 --- a/build.gradle +++ b/build.gradle @@ -82,7 +82,7 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'net.thauvin.erik:urlencoder:1.0.0' + implementation 'net.thauvin.erik:urlencoder:1.0.1' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 8e8423a..3a03d03 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -98,11 +98,10 @@ class ChatGpt : AbstractModule() { "model": "text-davinci-003", "prompt": $prompt, "temperature": 0, - "max_tokens": 100, + "max_tokens": 1024, "top_p": 1, "frequency_penalty": 0, - "presence_penalty": 0, - "stop": ["\n"] + "presence_penalty": 0 }""".trimIndent() ) ) @@ -127,7 +126,7 @@ class ChatGpt : AbstractModule() { } catch (e: IOException) { throw ModuleException( "chatgpt($query): IO", - "An IO error has occurred while conversing with GhatGPT.", + "An IO error has occurred while conversing with ChatGPT.", e ) } diff --git a/version.properties b/version.properties index 593b23f..c234d1f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Jan 04 02:59:39 PST 2023 -version.buildmeta=939 +#Thu Jan 05 16:20:43 PST 2023 +version.buildmeta=948 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+939 +version.semver=0.8.0-rc+948 From 6894e05610612b50a3445aba8c87c24af75e9dca Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 00:57:12 -0800 Subject: [PATCH 712/858] Added max-token property --- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 19 ++++++++++++++----- .../erik/mobibot/modules/ChatGptTest.kt | 13 +++++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 3a03d03..e2bccb0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage +import org.apache.commons.text.WordUtils import org.json.JSONException import org.json.JSONObject import org.json.JSONWriter @@ -54,9 +55,9 @@ class ChatGpt : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val answer = chat(args.trim(), properties[CHATGPT_API_KEY]) + val answer = chat(args.trim(), properties[CHATGPT_API_KEY], properties[CHATGPT_MAX_TOKENS]!!.toInt()) if (answer.isNotBlank()) { - event.sendMessage(answer) + event.sendMessage(WordUtils.wrap(answer, 400)) } else { event.respond("ChatGPT is stumped.") } @@ -65,6 +66,9 @@ class ChatGpt : AbstractModule() { e.message?.let { event.respond(it) } + } catch (e: NumberFormatException) { + if (logger.isErrorEnabled) logger.error("Invalid $CHATGPT_MAX_TOKENS property.", e) + event.respond("The $name module is misconfigured.") } } else { helpResponse(event) @@ -77,6 +81,11 @@ class ChatGpt : AbstractModule() { */ const val CHATGPT_API_KEY = "chatgpt-api-key" + /** + * The ChatGPT max tokens property. + */ + const val CHATGPT_MAX_TOKENS = "chatgpt-max-tokens" + // ChatGPT command private const val CHATGPT_CMD = "chatgpt" @@ -85,7 +94,7 @@ class ChatGpt : AbstractModule() { @JvmStatic @Throws(ModuleException::class) - fun chat(query: String, apiKey: String?): String { + fun chat(query: String, apiKey: String?, maxTokens: Int): String { if (!apiKey.isNullOrEmpty()) { val prompt = JSONWriter.valueToString("Q:$query\nA:") val request = HttpRequest.newBuilder() @@ -98,7 +107,7 @@ class ChatGpt : AbstractModule() { "model": "text-davinci-003", "prompt": $prompt, "temperature": 0, - "max_tokens": 1024, + "max_tokens": $maxTokens, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 0 @@ -145,6 +154,6 @@ class ChatGpt : AbstractModule() { add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) } - initProperties(CHATGPT_API_KEY) + initProperties(CHATGPT_API_KEY, CHATGPT_MAX_TOKENS) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index f230831..b861c55 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -34,15 +34,17 @@ package net.thauvin.erik.mobibot.modules 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.message import net.thauvin.erik.mobibot.LocalProperties import org.testng.annotations.Test class ChatGptTest : LocalProperties() { @Test(groups = ["modules"]) fun testApiKey() { - assertThat { ChatGpt.chat("1 gallon to liter", "") } + assertThat { ChatGpt.chat("1 gallon to liter", "", 0) } .isFailure() .isInstanceOf(ModuleException::class.java) .hasNoCause() @@ -52,7 +54,14 @@ class ChatGptTest : LocalProperties() { fun testChat() { val apiKey = getProperty(ChatGpt.CHATGPT_API_KEY) assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey) + ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") + assertThat( + ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) + ).contains("URLEncoder") + + assertThat { ChatGpt.chat("1 liter to gallon", apiKey, 0) } + .isFailure() + .isInstanceOf(ModuleException::class.java) } } From 66ca972b9b64ea309cd957f33a053369ab82cb61 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 00:57:20 -0800 Subject: [PATCH 713/858] Minor cleanup --- .github/workflows/gradle.yml | 2 +- README.md | 8 +++++--- build.gradle | 8 ++++---- config/detekt/baseline.xml | 9 +++++---- properties/mobibot.properties | 11 ++++++----- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 13 ++++++------- .../net/thauvin/erik/mobibot/commands/seen/Seen.kt | 8 ++++---- .../erik/mobibot/commands/tell/TellManager.kt | 8 ++++---- .../erik/mobibot/commands/tell/TellMessage.kt | 4 ++-- .../net/thauvin/erik/mobibot/entries/EntryLink.kt | 4 ++-- version.properties | 6 +++--- website/index.html | 5 ++--- 12 files changed, 44 insertions(+), 42 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 4a41aff..94687dc 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -68,7 +68,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew sonarqube + run: ./gradlew sonar - name: Cleanup Gradle Cache run: | diff --git a/README.md b/README.md index 2459b1b..4dee7b5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # mobibot -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) - -[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.0-blue)](https://kotlinlang.org/) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) +[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) +[![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: diff --git a/build.gradle b/build.gradle index 178326f..3515d31 100644 --- a/build.gradle +++ b/build.gradle @@ -55,7 +55,7 @@ dependencies { implementation 'commons-net:commons-net:3.9.0' // Google - implementation 'com.google.code.gson:gson:2.10' + implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.google.guava:guava:31.1-jre' // Kotlin @@ -82,7 +82,7 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'net.thauvin.erik:urlencoder:1.0.1' + implementation 'net.thauvin.erik:urlencoder:1.3.0' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' @@ -188,7 +188,7 @@ sonarqube { } } -tasks.sonarqube { +tasks.sonar { dependsOn 'koverReport' } @@ -219,6 +219,6 @@ task deploy { task release { group = 'Publishing' description = 'Releases new version.' - dependsOn(clean, wrapper, check, deploy) + dependsOn(clean, check, deploy) mustRunAfter clean } diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 19b4dbd..57c71a4 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -11,6 +11,7 @@ LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent ) LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String ) LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean ) + MagicNumber:ChatGpt.kt$ChatGpt$400 MagicNumber:ChatGpt.kt$ChatGpt.Companion$200 MagicNumber:Comment.kt$Comment$3 MagicNumber:CryptoPrices.kt$CryptoPrices$10 @@ -48,7 +49,7 @@ MaxLineLength:TwitterOAuth.kt$TwitterOAuth$* NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean - NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?): String + NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(query: String): Message NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>) @@ -64,8 +65,8 @@ NestedBlockDepth:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> NestedBlockDepth:Tell.kt$Tell$fun send(event: GenericUserEvent) NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$@JvmStatic fun main(args: Array<String>) - NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadData(file: String, default: Any, logger: Logger, description: String): Any - NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveData(file: String, data: Any, logger: Logger, description: String) + NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any + NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveSerialData(file: String, data: Any, logger: Logger, description: String) NestedBlockDepth:Weather2.kt$Weather2$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> PrintStackTrace:TwitterOAuth.kt$TwitterOAuth$ioe @@ -76,7 +77,7 @@ SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException - ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?): String + ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message> ThrowsCount:Mastodon.kt$Mastodon.Companion$@JvmStatic @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 5551173..e82910c 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -28,12 +28,12 @@ tell-max-size=50 #disabled-modules=dice, joke # -# Credentials for: http://pinboard.in/ +# API Token for: https://pinboard.in/settings/password # #pinboard-api-token=user\:TOKEN # -# Configure app at: https://developer.twitter.com/en/apps +# Configure app at: https://developer.twitter.com/ # and then: java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth # #twitter-consumerKey= @@ -61,14 +61,14 @@ tell-max-size=50 #mastodon-auto-post=true # -# Create custom search engine at: https://cse.google.com/ -# and get API key from: https://console.developers.google.com/ +# Create custom search engine at: https://programmablesearchengine.google.com/ +# and get API key from: https://console.cloud.google.com/apis # #google-api= #google-cse-cx= # -# Get OpenWeatherMap API key from: https://openweathermap.org/ +# Get OpenWeatherMap API key from: https://openweathermap.org/api # #owm-api-key= @@ -87,3 +87,4 @@ tell-max-size=50 # ChatGPT/OpenAI API key from: https://beta.openai.com/account/api-keys # #chatgpt-api-key= +#chatgpt-max-tokens=1024 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index bc9860d..359de74 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -47,7 +47,6 @@ import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.net.HttpURLConnection import java.net.URL -import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths import java.time.LocalDateTime @@ -189,7 +188,7 @@ object Utils { } /** - * Returns {@code true} if the specified user is an operator on the [channel]. + * Returns `true` if the specified user is an operator on the [channel]. */ @JvmStatic fun GenericMessageEvent.isChannelOp(channel: String): Boolean { @@ -197,7 +196,7 @@ object Utils { } /** - * Returns {@code true} if a HTTP status code indicates a successful response. + * Returns `true` if a HTTP status code indicates a successful response. */ @JvmStatic fun Int.isHttpSuccess() = this in 200..399 @@ -214,10 +213,10 @@ object Utils { } /** - * Load data. + * Load serial data from file. */ @JvmStatic - fun loadData(file: String, default: Any, logger: Logger, description: String): Any { + fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any { val serialFile = Paths.get(file) if (serialFile.exists() && serialFile.fileSize() > 0) { try { @@ -237,7 +236,7 @@ object Utils { } /** - * Returns {@code true} if the list does not contain the given string. + * Returns `true` if the list does not contain the given string. */ @JvmStatic fun List.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) } @@ -290,7 +289,7 @@ object Utils { * Save data */ @JvmStatic - fun saveData(file: String, data: Any, logger: Logger, description: String) { + fun saveSerialData(file: String, data: Any, logger: Logger, description: String) { try { BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> ObjectOutputStream(bos).use { output -> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 66a3259..2b97f5a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -35,8 +35,8 @@ package net.thauvin.erik.mobibot.commands.seen import com.google.common.collect.ImmutableSortedSet import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.loadData -import net.thauvin.erik.mobibot.Utils.saveData +import net.thauvin.erik.mobibot.Utils.loadSerialData +import net.thauvin.erik.mobibot.Utils.saveSerialData import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime @@ -107,7 +107,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") seenNicks.putAll( - loadData( + loadSerialData( serialObject, TreeMap(), logger, @@ -118,7 +118,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { } fun save() { - saveData(serialObject, seenNicks, logger, "seen nicknames") + saveSerialData(serialObject, seenNicks, logger, "seen nicknames") } init { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index 3c1f6b5..74ed301 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -31,8 +31,8 @@ */ package net.thauvin.erik.mobibot.commands.tell -import net.thauvin.erik.mobibot.Utils.loadData -import net.thauvin.erik.mobibot.Utils.saveData +import net.thauvin.erik.mobibot.Utils.loadSerialData +import net.thauvin.erik.mobibot.Utils.saveSerialData import org.slf4j.Logger import org.slf4j.LoggerFactory import java.time.Clock @@ -60,7 +60,7 @@ object TellManager { @JvmStatic fun load(file: String): List { @Suppress("UNCHECKED_CAST") - return loadData(file, emptyList(), logger, "message queue") as List + return loadSerialData(file, emptyList(), logger, "message queue") as List } /** @@ -69,7 +69,7 @@ object TellManager { @JvmStatic fun save(file: String, messages: List?) { if (messages != null) { - saveData(file, messages, logger, "messages") + saveSerialData(file, messages, logger, "messages") } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 7d8aaed..c9333c9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -66,12 +66,12 @@ class TellMessage( var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) /** - * Returns {@code true} if a notification was sent. + * Returns `true` if a notification was sent. */ var isNotified = false /** - * Returns {@code true} if the message was received. + * Returns `true` if the message was received. */ var isReceived = false set(value) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 59f4e73..ab0fee4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -130,8 +130,8 @@ class EntryLink( /** * Formats the tags. */ - fun formatTags(sep: String, prefix: String = "") : String { - return tags.joinToString(separator = sep, prefix = prefix){it.name} + fun formatTags(sep: String, prefix: String = ""): String { + return tags.joinToString(separator = sep, prefix = prefix) { it.name } } /** diff --git a/version.properties b/version.properties index c234d1f..c049177 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jan 05 16:20:43 PST 2023 -version.buildmeta=948 +#Thu Jan 12 00:57:56 PST 2023 +version.buildmeta=975 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+948 +version.semver=0.8.0-rc+975 diff --git a/website/index.html b/website/index.html index 2d134f2..ea78552 100644 --- a/website/index.html +++ b/website/index.html @@ -1,6 +1,5 @@ - - + + mobibot From 4fcdce522938dff23090504b733a7c5ebe2b4bbe Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 10:27:01 -0800 Subject: [PATCH 714/858] Added to no-ci group to avoid rate limits --- src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index b861c55..98f0052 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -50,7 +50,7 @@ class ChatGptTest : LocalProperties() { .hasNoCause() } - @Test(groups = ["modules"]) + @Test(groups = ["modules", "no-ci"]) fun testChat() { val apiKey = getProperty(ChatGpt.CHATGPT_API_KEY) assertThat( From c3d1930592a0b6289e0ecccdd6b19c777fb55601 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 23:06:07 -0800 Subject: [PATCH 715/858] Added rate limit error --- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index e2bccb0..d62d364 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -50,16 +50,16 @@ import java.net.http.HttpResponse class ChatGpt : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) - override val name = "ChatGPT" + override val name = CHATGPT_NAME override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val answer = chat(args.trim(), properties[CHATGPT_API_KEY], properties[CHATGPT_MAX_TOKENS]!!.toInt()) + val answer = chat(args.trim(), properties[API_KEY_PROP], properties[MAX_TOKENS_PROP]!!.toInt()) if (answer.isNotBlank()) { event.sendMessage(WordUtils.wrap(answer, 400)) } else { - event.respond("ChatGPT is stumped.") + event.respond("$name is stumped.") } } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) @@ -67,7 +67,7 @@ class ChatGpt : AbstractModule() { event.respond(it) } } catch (e: NumberFormatException) { - if (logger.isErrorEnabled) logger.error("Invalid $CHATGPT_MAX_TOKENS property.", e) + if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e) event.respond("The $name module is misconfigured.") } } else { @@ -77,20 +77,26 @@ class ChatGpt : AbstractModule() { companion object { /** - * The ChatGPT API Key property. + * The service name. */ - const val CHATGPT_API_KEY = "chatgpt-api-key" + const val CHATGPT_NAME = "ChatGPT" /** - * The ChatGPT max tokens property. + * The API Key property. */ - const val CHATGPT_MAX_TOKENS = "chatgpt-max-tokens" + const val API_KEY_PROP = "chatgpt-api-key" + + /** + * The max tokens property. + */ + const val MAX_TOKENS_PROP = "chatgpt-max-tokens" + + // ChatGPT API URL + private const val API_URL = "https://api.openai.com/v1/completions" // ChatGPT command private const val CHATGPT_CMD = "chatgpt" - // ChatGPT API URL - private const val API_URL = "https://api.openai.com/v1/completions" @JvmStatic @Throws(ModuleException::class) @@ -124,23 +130,28 @@ class ChatGpt : AbstractModule() { return choices.getJSONObject(0).getString("text").trim() } catch (e: JSONException) { throw ModuleException( - "chatgpt($query): JSON", - "A JSON error has occurred while conversing with ChatGPT.", + "$CHATGPT_CMD($query): JSON", + "A JSON error has occurred while conversing with $CHATGPT_NAME.", e ) } } else { - throw IOException("Status Code: " + response.statusCode()) + if (response.statusCode() == 429) { + throw ModuleException("$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later.") + } else { + throw IOException("HTTP Status Code: " + response.statusCode()) + } } } catch (e: IOException) { throw ModuleException( - "chatgpt($query): IO", - "An IO error has occurred while conversing with ChatGPT.", + "$CHATGPT_CMD($query): IO", + "An IO error has occurred while conversing with $CHATGPT_NAME.", e ) } } else { - throw ModuleException("chatgpt($query)", "No ChatGPT API key specified.") + throw ModuleException("$CHATGPT_CMD($query)", "No $CHATGPT_NAME API key specified.") } } } @@ -148,12 +159,12 @@ class ChatGpt : AbstractModule() { init { commands.add(CHATGPT_CMD) with(help) { - add("To get answers from ChatGPT:") + add("To get answers from $name:") add(Utils.helpFormat("%c $CHATGPT_CMD ")) add("For example:") add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) } - initProperties(CHATGPT_API_KEY, CHATGPT_MAX_TOKENS) + initProperties(API_KEY_PROP, MAX_TOKENS_PROP) } } From a1ea25ae74ea2981f23b0fc6699464eb21c11b6f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 12 Jan 2023 23:06:21 -0800 Subject: [PATCH 716/858] Minor cleanup --- config/detekt/baseline.xml | 1 + .../thauvin/erik/mobibot/modules/CryptoPrices.kt | 6 +++--- .../thauvin/erik/mobibot/modules/GoogleSearch.kt | 10 +++++----- .../thauvin/erik/mobibot/modules/StockQuote.kt | 16 ++++++++-------- .../net/thauvin/erik/mobibot/modules/Twitter.kt | 6 +++--- .../net/thauvin/erik/mobibot/modules/Weather2.kt | 6 +++--- .../thauvin/erik/mobibot/modules/WolframAlpha.kt | 11 ++++++----- .../thauvin/erik/mobibot/modules/ChatGptTest.kt | 4 +--- .../erik/mobibot/modules/GoogleSearchTest.kt | 4 ++-- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2Test.kt | 10 +++++----- .../erik/mobibot/modules/WolframAlphaTest.kt | 2 +- version.properties | 6 +++--- 13 files changed, 42 insertions(+), 42 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 57c71a4..da082db 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -13,6 +13,7 @@ LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean ) MagicNumber:ChatGpt.kt$ChatGpt$400 MagicNumber:ChatGpt.kt$ChatGpt.Companion$200 + MagicNumber:ChatGpt.kt$ChatGpt.Companion$429 MagicNumber:Comment.kt$Comment$3 MagicNumber:CryptoPrices.kt$CryptoPrices$10 MagicNumber:CurrencyConverter.kt$CurrencyConverter$11 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 66a33ae..05647bd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -65,7 +65,7 @@ class CryptoPrices : AbstractModule() { } val debugMessage = "crypto($cmd $args)" - if (args == CURRENCY_CODES_KEYWORD) { + if (args == CODES_KEYWORD) { event.sendMessage("The supported currencies are:") event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true) } else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) { @@ -100,7 +100,7 @@ class CryptoPrices : AbstractModule() { private val CURRENCIES: MutableMap = mutableMapOf() // Currency codes keyword - private const val CURRENCY_CODES_KEYWORD = "codes" + private const val CODES_KEYWORD = "codes" /** * Get current market price. @@ -153,7 +153,7 @@ class CryptoPrices : AbstractModule() { add(helpFormat("%c $CRYPTO_CMD ETH EUR")) add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) add("To list the supported currencies:") - add(helpFormat("%c $CRYPTO_CMD $CURRENCY_CODES_KEYWORD")) + add(helpFormat("%c $CRYPTO_CMD $CODES_KEYWORD")) } loadCurrencies() } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 76451db..02af2b6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -67,8 +67,8 @@ class GoogleSearch : AbstractModule() { try { val results = searchGoogle( args, - properties[GOOGLE_API_KEY_PROP], - properties[GOOGLE_CSE_KEY_PROP], + properties[API_KEY_PROP], + properties[CSE_KEY_PROP], event.user.nick ) for (msg in results) { @@ -91,10 +91,10 @@ class GoogleSearch : AbstractModule() { companion object { // Google API Key property - const val GOOGLE_API_KEY_PROP = "google-api-key" + const val API_KEY_PROP = "google-api-key" // Google Custom Search Engine ID property - const val GOOGLE_CSE_KEY_PROP = "google-cse-cx" + const val CSE_KEY_PROP = "google-cse-cx" // Google command private const val GOOGLE_CMD = "google" @@ -158,6 +158,6 @@ class GoogleSearch : AbstractModule() { commands.add(GOOGLE_CMD) help.add("To search Google:") help.add(helpFormat("%c $GOOGLE_CMD ")) - initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP) + initProperties(API_KEY_PROP, CSE_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index ffca08b..6df2317 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -63,7 +63,7 @@ class StockQuote : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP]) + val messages = getQuote(args, properties[API_KEY_PROP]) for (msg in messages) { event.sendMessage(channel, msg) } @@ -80,17 +80,17 @@ class StockQuote : AbstractModule() { companion object { /** - * The Alpha Advantage property key. + * The API property key. */ - const val ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key" + const val API_KEY_PROP = "alphavantage-api-key" /** * The Invalid Symbol error string. */ const val INVALID_SYMBOL = "Invalid symbol." - // Alpha Advantage URL - private const val ALPHAVANTAGE_URL = "https://www.alphavantage.co/query?function=" + // API URL + private const val API_URL = "https://www.alphavantage.co/query?function=" // Quote command private const val STOCK_CMD = "stock" @@ -145,7 +145,7 @@ class StockQuote : AbstractModule() { with(messages) { // Search for symbol/keywords response = URL( - "${ALPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + apiKey.encodeUrl() ).reader().body var json = getJsonResponse(response, debugMessage) @@ -157,7 +157,7 @@ class StockQuote : AbstractModule() { // Get quote for symbol response = URL( - "${ALPHAVANTAGE_URL}GLOBAL_QUOTE&symbol=" + "${API_URL}GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + apiKey.encodeUrl() ).reader().body @@ -232,6 +232,6 @@ class StockQuote : AbstractModule() { commands.add(STOCK_CMD) help.add("To retrieve a stock quote:") help.add(helpFormat("%c $STOCK_CMD ")) - initProperties(ALPHAVANTAGE_API_KEY_PROP) + initProperties(API_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index 4fc3770..ddb82b2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -84,7 +84,7 @@ class Twitter : SocialModule() { const val TOKEN_PROP = "twitter-token" const val TOKEN_SECRET_PROP = "twitter-tokenSecret" - // Twitter command + // Twitter commands private const val TWITTER_CMD = "twitter" private const val TWEET_CMD = "tweet" @@ -118,7 +118,7 @@ class Twitter : SocialModule() { dm.text } } catch (e: TwitterException) { - throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e) + throw ModuleException("tweet($message)", "An error has occurred: ${e.message}", e) } } } @@ -126,7 +126,7 @@ class Twitter : SocialModule() { init { commands.add(TWITTER_CMD) commands.add(TWEET_CMD) - help.add("To tweet on Twitter:") + help.add("To $TWEET_CMD on $name:") help.add(helpFormat("%c $TWEET_CMD ")) properties[AUTO_POST_PROP] = "false" initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 5c5ae67..ec963fe 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -65,7 +65,7 @@ class Weather2 : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val messages = getWeather(args, properties[OWM_API_KEY_PROP]) + val messages = getWeather(args, properties[API_KEY_PROP]) if (messages[0].isError) { helpResponse(event) } else { @@ -88,7 +88,7 @@ class Weather2 : AbstractModule() { /** * The OpenWeatherMap API Key property. */ - const val OWM_API_KEY_PROP = "owm-api-key" + const val API_KEY_PROP = "owm-api-key" // Weather command private const val WEATHER_CMD = "weather" @@ -246,6 +246,6 @@ class Weather2 : AbstractModule() { add(helpFormat("%c $WEATHER_CMD paris, fr")) add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.") } - initProperties(OWM_API_KEY_PROP) + initProperties(API_KEY_PROP) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 56d3a3d..204ad17 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -66,9 +66,9 @@ class WolframAlpha : AbstractModule() { units = if (query.size == 2) { getUnits(query[1].trim()) } else { - getUnits(properties[WOLFRAM_UNITS_PROP]) + getUnits(properties[UNITS_PROP]) }, - appId = properties[WOLFRAM_APPID_KEY] + appId = properties[APPID_KEY_PROP] ) ) } catch (e: ModuleException) { @@ -86,12 +86,13 @@ class WolframAlpha : AbstractModule() { /** * The Wolfram Alpha API Key property. */ - const val WOLFRAM_APPID_KEY = "wolfram-appid" + const val APPID_KEY_PROP = "wolfram-appid" /** * The Wolfram units properties */ - const val WOLFRAM_UNITS_PROP = "wolfram-units" + const val UNITS_PROP = "wolfram-units" + const val METRIC = "metric" const val IMPERIAL = "imperial" @@ -137,6 +138,6 @@ class WolframAlpha : AbstractModule() { add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas")) add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric")) } - initProperties(WOLFRAM_APPID_KEY, WOLFRAM_UNITS_PROP) + initProperties(APPID_KEY_PROP, UNITS_PROP) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 98f0052..6c30297 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -34,10 +34,8 @@ package net.thauvin.erik.mobibot.modules 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.message import net.thauvin.erik.mobibot.LocalProperties import org.testng.annotations.Test @@ -52,7 +50,7 @@ class ChatGptTest : LocalProperties() { @Test(groups = ["modules", "no-ci"]) fun testChat() { - val apiKey = getProperty(ChatGpt.CHATGPT_API_KEY) + val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 8d12ab4..9d052cf 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -75,8 +75,8 @@ class GoogleSearchTest : LocalProperties() { @Test(groups = ["no-ci", "modules"]) @Throws(ModuleException::class) fun testSearchGoogle() { - val apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP) - val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP) + val apiKey = getProperty(GoogleSearch.API_KEY_PROP) + val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP) try { var query = "mobibot" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 162550f..2721469 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -59,7 +59,7 @@ class StockQuoteTest : LocalProperties() { @Test(groups = ["modules"]) @Throws(ModuleException::class) fun testGetQuote() { - val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP) + val apiKey = getProperty(StockQuote.API_KEY_PROP) try { var symbol = "apple inc" val messages = getQuote(symbol, apiKey) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 6c67536..f1949d6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -46,7 +46,7 @@ import assertk.assertions.prop import net.aksingh.owmjapis.api.APIException import net.aksingh.owmjapis.core.OWM import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.Weather2.Companion.OWM_API_KEY_PROP +import net.thauvin.erik.mobibot.modules.Weather2.Companion.API_KEY_PROP 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 @@ -86,7 +86,7 @@ class Weather2Test : LocalProperties() { @Throws(ModuleException::class) fun testWeather() { var query = "98204" - var messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) + var messages = getWeather(query, getProperty(API_KEY_PROP)) assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { contains("Everett, United States") contains("US") @@ -94,7 +94,7 @@ class Weather2Test : LocalProperties() { assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS") query = "San Francisco" - messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) + messages = getWeather(query, getProperty(API_KEY_PROP)) assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { contains("San Francisco") contains("US") @@ -102,7 +102,7 @@ class Weather2Test : LocalProperties() { assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959") query = "London, GB" - messages = getWeather(query, getProperty(OWM_API_KEY_PROP)) + messages = getWeather(query, getProperty(API_KEY_PROP)) assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { contains("London, United Kingdom") contains("GB") @@ -111,7 +111,7 @@ class Weather2Test : LocalProperties() { try { query = "Foo, US" - getWeather(query, getProperty(OWM_API_KEY_PROP)) + getWeather(query, getProperty(API_KEY_PROP)) } catch (e: ModuleException) { assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index f0fb424..0d9ed9a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -58,7 +58,7 @@ class WolframAlphaTest : LocalProperties() { @Test(groups = ["modules", "no-ci"]) @Throws(ModuleException::class) fun queryWolframTest() { - val apiKey = getProperty(WolframAlpha.WOLFRAM_APPID_KEY) + val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) try { var query = "SFO to SEA" assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles") diff --git a/version.properties b/version.properties index c049177..8d6a471 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jan 12 00:57:56 PST 2023 -version.buildmeta=975 +#Thu Jan 12 23:03:48 PST 2023 +version.buildmeta=982 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+975 +version.semver=0.8.0-rc+982 From 6ec39c6d6df7367760f64de1c3f79286268ef3e6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 28 Jan 2023 23:19:12 -0800 Subject: [PATCH 717/858] Updated copyright --- .idea/copyright/Erik_s_Copyright_Notice.xml | 2 +- LICENSE.txt | 3 +- build.gradle | 2 +- .../net/thauvin/erik/mobibot/modules/War.java | 3 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 3 +- .../net/thauvin/erik/mobibot/Constants.kt | 3 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 3 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 3 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 3 +- .../net/thauvin/erik/mobibot/TwitterOAuth.kt | 3 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 3 +- .../erik/mobibot/commands/AbstractCommand.kt | 3 +- .../erik/mobibot/commands/ChannelFeed.kt | 3 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 3 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 3 +- .../thauvin/erik/mobibot/commands/Modules.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 3 +- .../thauvin/erik/mobibot/commands/Recap.kt | 3 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 3 +- .../thauvin/erik/mobibot/commands/Users.kt | 3 +- .../thauvin/erik/mobibot/commands/Versions.kt | 3 +- .../erik/mobibot/commands/links/Comment.kt | 3 +- .../mobibot/commands/links/LinksManager.kt | 3 +- .../erik/mobibot/commands/links/Posting.kt | 3 +- .../erik/mobibot/commands/links/Tags.kt | 3 +- .../erik/mobibot/commands/links/View.kt | 3 +- .../mobibot/commands/seen/NickComparator.kt | 5 ++- .../erik/mobibot/commands/seen/Seen.kt | 3 +- .../erik/mobibot/commands/seen/SeenNick.kt | 3 +- .../erik/mobibot/commands/tell/Tell.kt | 3 +- .../erik/mobibot/commands/tell/TellManager.kt | 3 +- .../erik/mobibot/commands/tell/TellMessage.kt | 3 +- .../thauvin/erik/mobibot/entries/Entries.kt | 3 +- .../erik/mobibot/entries/EntriesUtils.kt | 3 +- .../erik/mobibot/entries/EntryComment.kt | 3 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 3 +- .../erik/mobibot/entries/FeedsManager.kt | 3 +- .../erik/mobibot/modules/AbstractModule.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 3 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 3 +- .../erik/mobibot/modules/CryptoPrices.kt | 3 +- .../erik/mobibot/modules/CurrencyConverter.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 3 +- .../erik/mobibot/modules/GoogleSearch.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 3 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 3 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 3 +- .../erik/mobibot/modules/ModuleException.kt | 3 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 3 +- .../erik/mobibot/modules/RockPaperScissors.kt | 3 +- .../erik/mobibot/modules/StockQuote.kt | 3 +- .../thauvin/erik/mobibot/modules/Twitter.kt | 3 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 3 +- .../erik/mobibot/modules/WolframAlpha.kt | 3 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 3 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 3 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 3 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 3 +- .../erik/mobibot/msg/PrivateMessage.kt | 3 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 3 +- .../erik/mobibot/social/SocialManager.kt | 3 +- .../erik/mobibot/social/SocialModule.kt | 3 +- .../erik/mobibot/social/SocialTimer.kt | 3 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 3 +- .../erik/mobibot/ExceptionSanitizer.kt | 3 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 3 +- .../thauvin/erik/mobibot/LocalProperties.kt | 3 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 3 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 3 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 3 +- .../erik/mobibot/commands/RecapTest.kt | 3 +- .../commands/links/LinksManagerTest.kt | 5 ++- .../erik/mobibot/commands/links/ViewTest.kt | 3 +- .../erik/mobibot/commands/seen/SeenTest.kt | 3 +- .../mobibot/commands/tell/TellMessageTest.kt | 3 +- .../commands/tell/TellMessagesMgrTest.kt | 3 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 3 +- .../erik/mobibot/entries/EntryLinkTest.kt | 3 +- .../erik/mobibot/entries/FeedMgrTest.kt | 3 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 3 +- .../erik/mobibot/modules/ChatGptTest.kt | 3 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 3 +- .../mobibot/modules/CurrencyConverterTest.kt | 3 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 3 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 3 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 3 +- .../erik/mobibot/modules/LookupTest.kt | 3 +- .../erik/mobibot/modules/MastodonTest.kt | 3 +- .../mobibot/modules/ModuleExceptionTest.kt | 3 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 3 +- .../mobibot/modules/RockPaperScissorsTest.kt | 3 +- .../erik/mobibot/modules/StockQuoteTest.kt | 3 +- .../erik/mobibot/modules/TwitterTest.kt | 3 +- .../erik/mobibot/modules/Weather2Test.kt | 3 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 3 +- .../erik/mobibot/modules/WordTimeTest.kt | 3 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 5 ++- src/test/resources/current.xml | 31 +++++++++++++++++++ version.properties | 6 ++-- 103 files changed, 138 insertions(+), 206 deletions(-) diff --git a/.idea/copyright/Erik_s_Copyright_Notice.xml b/.idea/copyright/Erik_s_Copyright_Notice.xml index b9f5293..055999a 100644 --- a/.idea/copyright/Erik_s_Copyright_Notice.xml +++ b/.idea/copyright/Erik_s_Copyright_Notice.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index 085f7c7..f5fe66b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,4 @@ -Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) -All rights reserved. +Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/build.gradle b/build.gradle index 3515d31..70e3d46 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ mainClassName = packageName + '.Mobibot' ext.versions = [ log4j: '2.19.0', - pmd : '6.52.0', + pmd : '6.54.0', ] repositories { diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index 9193072..d1d7882 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -1,8 +1,7 @@ /* * War.java * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 8720e95..020edf4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,8 +1,7 @@ /* * Addons.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 543511f..6766f62 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,8 +1,7 @@ /* * Constants.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index fc43dbc..f56af2e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,8 +1,7 @@ /* * FeedReader.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index fcd2d08..f811764 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,8 +1,7 @@ /* * Mobibot.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 1bc719b..b829bab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,8 +1,7 @@ /* * Pinboard.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt index 2b5ecfb..ab078db 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt @@ -1,8 +1,7 @@ /* * TwitterOAuth.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 359de74..165647a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,8 +1,7 @@ /* * Utils.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index c404b89..5f79472 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,8 +1,7 @@ /* * AbstractCommand.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 8412af0..038e378 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,8 +1,7 @@ /* * ChannelFeed.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 31a9c65..9608ca8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,8 +1,7 @@ /* * Cycle.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index 97ca12e..f271bfa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,8 +1,7 @@ /* * Die.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index f47f057..a696fa8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,8 +1,7 @@ /* * Ignore.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 9f7e855..ed0b6ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,8 +1,7 @@ /* * Info.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index bbd5479..ec7823b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,8 +1,7 @@ /* * Me.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 456fa7f..f64178d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,8 +1,7 @@ /* * Modules.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index a758996..20a6635 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,8 +1,7 @@ /* * Msg.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index e4cdf34..85a03ab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,8 +1,7 @@ /* * Nick.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 3647bcc..77154c7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,8 +1,7 @@ /* * Recap.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 0141206..7f76d35 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,8 +1,7 @@ /* * Say.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 66c4ebf..33d6fef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,8 +1,7 @@ /* * Users.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 9513aef..896c569 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,8 +1,7 @@ /* * Versions.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 5a09836..1443d44 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,8 +1,7 @@ /* * Comment.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index af6adf4..fba6b99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,8 +1,7 @@ /* * LinksManager.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 9368d37..ff4278d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,8 +1,7 @@ /* * Posting.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 785e3a0..1662857 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,8 +1,7 @@ /* * Tags.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 008ccad..825e374 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,8 +1,7 @@ /* * View.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index 8d068c2..d29b30d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,8 +1,7 @@ /* - * SeenComparator.kt + * NickComparator.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 2b97f5a..4a45598 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,8 +1,7 @@ /* * Seen.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index b53414d..b09cbf4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,8 +1,7 @@ /* * SeenNick.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index f591283..e073184 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,8 +1,7 @@ /* * Tell.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index 74ed301..b65a4da 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,8 +1,7 @@ /* * TellManager.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index c9333c9..6d2f313 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,8 +1,7 @@ /* * TellMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index a81a737..e8676ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,8 +1,7 @@ /* * Entries.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index ac2c259..9c09626 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,8 +1,7 @@ /* * EntriesUtils.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index b0b138a..bc64191 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,8 +1,7 @@ /* * EntryComment.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index ab0fee4..7bf003d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,8 +1,7 @@ /* * EntryLink.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index 1ae2690..bb3838a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,8 +1,7 @@ /* * FeedsManager.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 61e2eaf..8c8e736 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,8 +1,7 @@ /* * AbstractModule.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 76f3786..b7aae28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,8 +1,7 @@ /* * Calc.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index d62d364..8847bef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -1,8 +1,7 @@ /* * ChatGpt.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 05647bd..d14056e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,8 +1,7 @@ /* * CryptoPrices.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 3d0bf0a..d41e7a1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,8 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index c32f781..8420fb1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,8 +1,7 @@ /* * Dice.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 02af2b6..f426d1e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,8 +1,7 @@ /* * GoogleSearch.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 202dc68..a0e8fd4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,8 +1,7 @@ /* * Joke.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 90a0316..9ab2ead 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,8 +1,7 @@ /* * Lookup.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 9a4e914..3be3a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,8 +1,7 @@ /* * Mastodon.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 2f39854..a569d21 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,8 +1,7 @@ /* * ModuleException.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 0818120..944dbc1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,8 +1,7 @@ /* * Ping.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index bc02a4d..d698888 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,8 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 6df2317..dcae5e7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,8 +1,7 @@ /* * StockQuote.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt index ddb82b2..d4c02e1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt @@ -1,8 +1,7 @@ /* * Twitter.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index ec963fe..567728e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,8 +1,7 @@ /* * Weather2.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 204ad17..a72efab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,8 +1,7 @@ /* * WolframAlpha.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index fc0edf6..18072bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,8 +1,7 @@ /* * WorldTime.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index ae5651c..0607936 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,8 +1,7 @@ /* * ErrorMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 20f8725..23a33b9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,8 +1,7 @@ /* * Message.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index f88b8dd..037d504 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,8 +1,7 @@ /* * NoticeMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 827b682..842fee5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,8 +1,7 @@ /* * PrivateMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 71b2a5b..9c5e088 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,8 +1,7 @@ /* * PublicMessage.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index f3a86fe..cbc1936 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,8 +1,7 @@ /* * SocialManager.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index e31ccdc..b594670 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,8 +1,7 @@ /* * SocialModule.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index 3edb06b..267a59d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,8 +1,7 @@ /* * SocialTimer.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 826e784..8662392 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,8 +1,7 @@ /* * AddonsTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index b1c6dba..a3994ec 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,8 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 2fa58b6..0d66a0d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,8 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index 7dba11a..e4af75a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,8 +1,7 @@ /* * LocalProperties.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 3ee0f26..87617e8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,8 +1,7 @@ /* * PinboardTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index d6fe27d..22e37cb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,8 +1,7 @@ /* * UtilsTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index ba5af62..265009b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,8 +1,7 @@ /* * InfoTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 6894b3d..f1fbe11 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,8 +1,7 @@ /* * RecapTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index fa4a099..8e49b5e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,8 +1,7 @@ /* - * LinksMgrTest.kt + * LinksManagerTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index e7c07f0..c28090d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,8 +1,7 @@ /* * ViewTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 46090e5..4298a16 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,8 +1,7 @@ /* * SeenTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 2e4ef6e..f7239e0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,8 +1,7 @@ /* * TellMessageTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index a07eb68..cff11f2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,8 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 23ba01e..6eef16e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,8 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 8c1a862..ab8c71c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,8 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index 68be3f7..cd2ebb8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,8 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 0873e7b..fb0402e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,8 +1,7 @@ /* * CalcTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 6c30297..5e0e1d2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -1,8 +1,7 @@ /* * ChatGptTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 3bef92b..e3475f1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,8 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 66ae5c7..8c1d745 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,8 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index e391f2a..cdc04f0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,8 +1,7 @@ /* * DiceTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 9d052cf..c2bb833 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,8 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 618cd36..fa063f4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,8 +1,7 @@ /* * JokeTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 5ab97d7..9c21f7c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,8 +1,7 @@ /* * LookupTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 74b5a9a..d464f03 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,8 +1,7 @@ /* * MastodonTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index eef610a..c7dbfc0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,8 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index 50f7fda..e1e79f3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,8 +1,7 @@ /* * PingTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 4bd8025..8dc90ba 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,8 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 2721469..d35a3d3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,8 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 126fb4c..9651ba0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -1,8 +1,7 @@ /* * TwitterTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index f1949d6..4c7f7e6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,8 +1,7 @@ /* * Weather2Test.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 0d9ed9a..4aaf620 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,8 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 049d315..f17ed1d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,8 +1,7 @@ /* * WordTimeTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index 7219a1b..e856112 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,8 +1,7 @@ /* - * TestMessage.kt + * MessageTest.kt * - * Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net) - * All rights reserved. + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/resources/current.xml b/src/test/resources/current.xml index 8552a9a..535a400 100644 --- a/src/test/resources/current.xml +++ b/src/test/resources/current.xml @@ -1,4 +1,35 @@ + + #mobibot IRC Links diff --git a/version.properties b/version.properties index 8d6a471..fb36533 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jan 12 23:03:48 PST 2023 -version.buildmeta=982 +#Sat Jan 28 23:17:39 PST 2023 +version.buildmeta=983 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+982 +version.semver=0.8.0-rc+983 From 6f3b84a7c0c4634b2b2a5f2abfb54268e0ff6c62 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 31 Jan 2023 22:03:33 -0800 Subject: [PATCH 718/858] Fixed potential resource leak --- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 165647a..f61c56c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -420,12 +420,12 @@ object Utils { val connection = this.openConnection() as HttpURLConnection connection.setRequestProperty( "User-Agent", - "Mozilla/5.0 (Linux x86_64; rv:104.0) Gecko/20100101 Firefox/104.0" + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" ) return if (connection.responseCode.isHttpSuccess()) { - UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().readText()) + UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) } else { - UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().readText()) + UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() }) } } From a43d4e0b5dbd75c1e998810dbe6f46e8d27c112d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 31 Jan 2023 23:24:34 -0800 Subject: [PATCH 719/858] Updated workflow --- .github/workflows/gradle.yml | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 94687dc..fa46754 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -15,12 +15,12 @@ jobs: java-version: [ 11, 18 ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: java-version: ${{ matrix.java-version }} @@ -29,23 +29,14 @@ jobs: - name: Cache SonarCloud packages if: matrix.java-version == env.SONAR_JDK - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ matrix.java-version }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle-${{ matrix.java-version }}- - - name: Test with Gradle + uses: gradle/gradle-build-action@v2 env: CI_NAME: "GitHub CI" ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -60,8 +51,8 @@ jobs: MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} - - run: ./gradlew build check --stacktrace + with: + arguments: build check --stacktrace - name: SonarCloud if: success() && matrix.java-version == env.SONAR_JDK @@ -69,8 +60,3 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: ./gradlew sonar - - - name: Cleanup Gradle Cache - run: | - rm -f ~/.gradle/caches/modules-2/modules-2.lock - rm -f ~/.gradle/caches/modules-2/gc.properties From 1b55736ee189ddd3f1663f38be4af6651372d6b2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 31 Jan 2023 23:31:12 -0800 Subject: [PATCH 720/858] Added Java distribution --- .github/workflows/gradle.yml | 1 + build.gradle | 2 +- version.properties | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index fa46754..2e4df52 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -22,6 +22,7 @@ jobs: - name: Set up JDK ${{ matrix.java-version }} uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: ${{ matrix.java-version }} - name: Grant execute permission for gradlew diff --git a/build.gradle b/build.gradle index 70e3d46..8b7827f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.44.0' + id 'com.github.ben-manes.versions' version '0.45.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.22.0' id 'java' diff --git a/version.properties b/version.properties index fb36533..693db4f 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Jan 28 23:17:39 PST 2023 -version.buildmeta=983 +#Mon Jan 30 22:08:48 PST 2023 +version.buildmeta=986 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+983 +version.semver=0.8.0-rc+986 From f06ba4a6f55d769d1a5dd872ab394f86f047b6d9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 1 Feb 2023 02:37:04 -0800 Subject: [PATCH 721/858] Added disabled modules and commands listing --- src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt | 10 ++++++++++ src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt | 6 +++++- .../net/thauvin/erik/mobibot/commands/Modules.kt | 8 ++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 020edf4..1127f02 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -74,7 +74,10 @@ class Addons(private val props: Properties) { if (logger.isDebugEnabled) { logger.debug("Module $name is disabled.") } + names.disabledModules.add(name) } + } else { + names.disabledModules.add(name) } } return enabled @@ -106,7 +109,10 @@ class Addons(private val props: Properties) { if (logger.isDebugEnabled) { logger.debug("Command $name is disabled.") } + names.disabledCommands.add(name) } + } else { + names.disabledCommands.add(name) } } return enabled @@ -168,12 +174,16 @@ class Addons(private val props: Properties) { */ object Names { val modules: MutableList = mutableListOf() + val disabledModules: MutableList = mutableListOf() val commands: MutableList = mutableListOf() + val disabledCommands: MutableList = mutableListOf() val ops: MutableList = mutableListOf() fun sort() { modules.sort() + disabledModules.sort() commands.sort() + disabledCommands.sort() ops.sort() } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index f811764..2dff959 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -148,6 +148,10 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro event.sendMessage("The commands are:") event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) if (event.isChannelOp(channel)) { + if (addons.names.disabledCommands.isNotEmpty()) { + event.sendMessage("The disabled commands are:") + event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true) + } event.sendMessage("The op commands are:") event.sendList(addons.names.ops, 8, isBold = true, isIndent = true) } @@ -412,7 +416,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(Ignore()) addons.add(LinksManager()) addons.add(Me()) - addons.add(Modules(addons.names.modules)) + addons.add(Modules(addons.names.modules, addons.names.disabledModules)) addons.add(Msg()) addons.add(Nick()) addons.add(Posting()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index f64178d..b2293b0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -36,9 +36,9 @@ import net.thauvin.erik.mobibot.Utils.isChannelOp import net.thauvin.erik.mobibot.Utils.sendList import org.pircbotx.hooks.types.GenericMessageEvent -class Modules(private val modules: List) : AbstractCommand() { +class Modules(private val modules: List, private val disabledModules: List) : AbstractCommand() { override val name = "modules" - override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name")) + override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name")) override val isOpOnly = true override val isPublic = false override val isVisible = true @@ -51,6 +51,10 @@ class Modules(private val modules: List) : AbstractCommand() { event.respondPrivateMessage("The enabled modules are: ") event.sendList(modules, 7, isIndent = true) } + if (disabledModules.isNotEmpty()) { + event.respondPrivateMessage("The disabled modules are: ") + event.sendList(disabledModules, 7, isIndent = true) + } } else { helpResponse(channel, args, event) } From d9d5dd2e47f9d198a3cc55961247ba4e45958222 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 1 Feb 2023 02:37:39 -0800 Subject: [PATCH 722/858] Updated to latest workflow actions --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 2e4df52..6ad0035 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -60,4 +60,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew sonar + run: ./gradlew sonar --info From 1d85a1c51683073aa1453be757716751064a0a61 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 3 Feb 2023 00:58:16 -0800 Subject: [PATCH 723/858] Disabled Twitter module by default --- .idea/kotlinc.xml | 2 +- README.md | 7 ++----- build.gradle | 9 +++++---- properties/mobibot.properties | 2 +- .../net/thauvin/erik/mobibot/modules/TwitterTest.kt | 2 +- website/index.html | 3 +-- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 2b8a50f..0fc3113 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/README.md b/README.md index 4dee7b5..53bcfe4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.0-blue)](https://kotlinlang.org/) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.10-blue)](https://kotlinlang.org/) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) @@ -24,10 +24,7 @@ Some very basic instructions: # help java -jar mobibot.jar -h - - # twitter oauth token request - java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth - + # launch /usr/bin/nohup java -jar mobibot.jar & ``` diff --git a/build.gradle b/build.gradle index 8b7827f..f09cdb1 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ plugins { id 'io.gitlab.arturbosch.detekt' version '1.22.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.8.0' - id 'org.jetbrains.kotlin.kapt' version '1.8.0' + id 'org.jetbrains.kotlin.jvm' version '1.8.10' + id 'org.jetbrains.kotlin.kapt' version '1.8.10' id 'org.jetbrains.kotlinx.kover' version '0.6.1' id 'org.sonarqube' version '3.5.0.2730' id 'pmd' @@ -92,10 +92,11 @@ dependencies { test { useTestNG() { + excludeGroups.add('twitter') if (isCI) { - excludeGroups('no-ci') - println "Excluded test groups: ${excludeGroups}" + excludeGroups.add('no-ci') } + println "Excluded test groups: ${excludeGroups}" } } diff --git a/properties/mobibot.properties b/properties/mobibot.properties index e82910c..0f97f3c 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -25,7 +25,7 @@ tell-max-days=5 tell-max-size=50 #disabled-commands=die, ignore -#disabled-modules=dice, joke +disabled-modules=twitter # # API Token for: https://pinboard.in/settings/password diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt index 9651ba0..6e4ab27 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt @@ -41,7 +41,7 @@ import org.testng.annotations.Test * The `TwitterTest` class. */ class TwitterTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test(groups = ["modules", "twitter"]) @Throws(ModuleException::class) fun testTweet() { val msg = "Testing Twitter API from ${getHostName()}" diff --git a/website/index.html b/website/index.html index ea78552..be6c33e 100644 --- a/website/index.html +++ b/website/index.html @@ -122,8 +122,7 @@
    mobibot: paper
    mobibot: rock
    -
  5. Posting to Twitter and Mastodon -
    mobibot: tweet hello twitter
    +
  6. Automatic and manual posting to Mastodon
    mobibot: toot hello mastodon
  7. From d67cf4ace765ca2cb842f0edc6afd9d7acd947ca Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 15 Feb 2023 22:15:50 -0800 Subject: [PATCH 724/858] Minor cleanup --- README.md | 3 +-- src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt | 5 +++-- website/index.html | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 53bcfe4..8105ed3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.10-blue)](https://kotlinlang.org/) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.10-7f52ff.svg)](https://kotlinlang.org)[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 22e37cb..ef0eaaf 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -266,8 +266,9 @@ class UtilsTest { @Test @Throws(IOException::class) fun testUrlReader() { - assertThat(URL("https://postman-echo.com/status/200").reader().body, "urlReader()") - .isEqualTo("{\"status\":200}") + val reader = URL("https://postman-echo.com/status/200").reader() + assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}") + assertThat(reader.responseCode).isEqualTo(200) } @Test diff --git a/website/index.html b/website/index.html index be6c33e..97e337d 100644 --- a/website/index.html +++ b/website/index.html @@ -69,7 +69,7 @@
    mobibot: view
  8. Performing calculations -
    mobibot: calc (floor(sqrt(3)) + 3.14) * 3^2
    +
    mobibot: calc (floor(sqrt(3)) + π) * 3^2
  9. Crypto currencies prices
    mobibot: cryto btc
    From db8469169144beb7a5b7f0758f62956a08ed6db9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 15 Feb 2023 22:27:08 -0800 Subject: [PATCH 725/858] Upgraded to Gradle 8.0 --- build.gradle | 17 ++++++++++++++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index f09cdb1..f7da86e 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ dependencies { implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" - implementation 'com.rometools:rome:1.18.0' + implementation 'com.rometools:rome:1.19.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' @@ -92,11 +92,13 @@ dependencies { test { useTestNG() { - excludeGroups.add('twitter') + // excludeGroups.add('twitter') if (isCI) { excludeGroups.add('no-ci') } - println "Excluded test groups: ${excludeGroups}" + if (!excludeGroups.isEmpty()) { + println "Excluded test groups: ${excludeGroups}" + } } } @@ -153,6 +155,15 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } +tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach { + jvmTarget = java.targetCompatibility.toString() +} + +tasks.withType(io.gitlab.arturbosch.detekt.DetektCreateBaselineTask).configureEach { + jvmTarget = java.targetCompatibility.toString() +} + + jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f398c33..42defcc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From c2c5f8beaeb5ce48483209d29dcef9eee3f7da0a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 17 Feb 2023 22:09:57 -0800 Subject: [PATCH 726/858] Upgraded sonar plugin --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index f7da86e..55c1a38 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' version '1.8.10' id 'org.jetbrains.kotlin.kapt' version '1.8.10' id 'org.jetbrains.kotlinx.kover' version '0.6.1' - id 'org.sonarqube' version '3.5.0.2730' + id 'org.sonarqube' version '4.0.0.2929' id 'pmd' } @@ -75,7 +75,7 @@ dependencies { implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' implementation 'org.json:json:20220924' - implementation 'org.jsoup:jsoup:1.15.3' + implementation 'org.jsoup:jsoup:1.15.4' implementation 'org.twitter4j:twitter4j-core:4.1.2' // Thauvin From 207c1b7e022b5a7ebc4458d4e9c9aa12912a6049 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 00:01:01 -0700 Subject: [PATCH 727/858] Removed twitter module --- .github/workflows/gradle.yml | 5 - .idea/codeStyles/Project.xml | 275 ++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 1 + .idea/kotlinc.xml | 2 +- build.gradle | 58 ++-- config/detekt/baseline.xml | 7 +- gradle/wrapper/gradle-wrapper.jar | Bin 61574 -> 62076 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 11 +- properties/mobibot.properties | 17 +- .../net/thauvin/erik/mobibot/Constants.kt | 6 + .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 3 +- .../net/thauvin/erik/mobibot/TwitterOAuth.kt | 118 -------- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 8 +- .../thauvin/erik/mobibot/modules/Twitter.kt | 133 --------- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 - .../thauvin/erik/mobibot/FeedReaderTest.kt | 17 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 3 +- .../erik/mobibot/modules/ChatGptTest.kt | 7 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 8 +- .../erik/mobibot/modules/MastodonTest.kt | 5 +- .../erik/mobibot/modules/StockQuoteTest.kt | 3 +- .../erik/mobibot/modules/TwitterTest.kt | 60 ---- .../erik/mobibot/modules/Weather2Test.kt | 5 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 8 +- .../erik/mobibot/modules/WordTimeTest.kt | 3 +- version.properties | 6 +- website/index.html | 1 - 29 files changed, 360 insertions(+), 416 deletions(-) delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt delete mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt delete mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 6ad0035..0034945 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -44,11 +44,6 @@ jobs: CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} OWM_API_KEY: ${{ secrets.OWM_API_KEY }} PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} - TWITTER_CONSUMERKEY: ${{ secrets.TWITTER_CONSUMERKEY }} - TWITTER_CONSUMERSECRET: ${{ secrets.TWITTER_CONSUMERSECRET }} - TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }} - TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }} - TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }} MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 1bec35e..76b5425 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,6 +3,281 @@ + + diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index a55e7a1..6e6eec1 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 0fc3113..217e5c5 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/build.gradle b/build.gradle index 55c1a38..6a131ab 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,16 @@ +import io.gitlab.arturbosch.detekt.Detekt +import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask + plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.45.0' + id 'com.github.ben-manes.versions' version '0.46.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.22.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.8.10' - id 'org.jetbrains.kotlin.kapt' version '1.8.10' - id 'org.jetbrains.kotlinx.kover' version '0.6.1' + id 'org.jetbrains.kotlin.jvm' version '1.8.21' + id 'org.jetbrains.kotlin.kapt' version '1.8.21' + id 'org.jetbrains.kotlinx.kover' version '0.7.0' id 'org.sonarqube' version '4.0.0.2929' id 'pmd' } @@ -29,8 +32,8 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.19.0', - pmd : '6.54.0', + log4j: '2.20.0', + pmd : '6.55.0', ] repositories { @@ -61,22 +64,21 @@ dependencies { // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' // Logging - implementation 'org.slf4j:slf4j-api:2.0.6' + implementation 'org.slf4j:slf4j-api:2.0.7' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" - implementation 'com.rometools:rome:1.19.0' - implementation 'com.squareup.okhttp3:okhttp:4.10.0' + implementation 'com.rometools:rome:2.1.0' + implementation 'com.squareup.okhttp3:okhttp:4.11.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20220924' - implementation 'org.jsoup:jsoup:1.15.4' - implementation 'org.twitter4j:twitter4j-core:4.1.2' + implementation 'org.json:json:20230227' + implementation 'org.jsoup:jsoup:1.16.1' // Thauvin implementation 'net.thauvin.erik:cryptoprice:1.0.0' @@ -84,10 +86,10 @@ dependencies { implementation 'net.thauvin.erik:pinboard-poster:1.0.3' implementation 'net.thauvin.erik:urlencoder:1.3.0' - testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' + testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.26.1' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation "org.mockito:mockito-core:4.0.0" - testImplementation 'org.testng:testng:7.7.1' + testImplementation 'org.testng:testng:7.8.0' } test { @@ -114,6 +116,12 @@ java { targetCompatibility = JavaVersion.VERSION_11 } +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + kapt { includeCompileClasspath = false arguments { @@ -121,15 +129,10 @@ kapt { } } -tasks.withType(JavaCompile) { +tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { - kotlinOptions { - jvmTarget = java.targetCompatibility.toString() - } -} compileJava { dependsOn 'incrementBuildMeta' @@ -155,15 +158,14 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } -tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach { +tasks.withType(Detekt).configureEach { jvmTarget = java.targetCompatibility.toString() } -tasks.withType(io.gitlab.arturbosch.detekt.DetektCreateBaselineTask).configureEach { +tasks.withType(DetektCreateBaselineTask).configureEach { jvmTarget = java.targetCompatibility.toString() } - jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) @@ -204,19 +206,19 @@ tasks.sonar { dependsOn 'koverReport' } -task copyToDeploy(type: Copy) { +tasks.register('copyToDeploy', Copy) { from('properties', jar) into deployDir } -task copyToDeployLib(type: Copy) { +tasks.register('copyToDeployLib', Copy) { from(configurations.runtimeClasspath) { exclude 'annotations-*.jar' } into(deployDir + '/lib') } -task deploy { +tasks.register('deploy') { description = "Copies all needed files to the ${deployDir} directory." group = 'Publishing' dependsOn(assemble, jar) @@ -228,7 +230,7 @@ task deploy { mustRunAfter(clean) } -task release { +tasks.register('release') { group = 'Publishing' description = 'Releases new version.' dependsOn(clean, check, deploy) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index da082db..db7e772 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -10,7 +10,6 @@ LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent ) LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String ) - LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean ) MagicNumber:ChatGpt.kt$ChatGpt$400 MagicNumber:ChatGpt.kt$ChatGpt.Companion$200 MagicNumber:ChatGpt.kt$ChatGpt.Companion$429 @@ -33,7 +32,6 @@ MagicNumber:StockQuote.kt$StockQuote.Companion$10 MagicNumber:Tell.kt$Tell$50 MagicNumber:Tell.kt$Tell$7 - MagicNumber:TwitterOAuth.kt$TwitterOAuth$401 MagicNumber:Users.kt$Users$8 MagicNumber:Utils.kt$Utils$200 MagicNumber:Utils.kt$Utils$399 @@ -47,7 +45,6 @@ MagicNumber:WorldTime.kt$WorldTime.Companion$3600 MagicNumber:WorldTime.kt$WorldTime.Companion$60 MagicNumber:WorldTime.kt$WorldTime.Companion$86.4 - MaxLineLength:TwitterOAuth.kt$TwitterOAuth$* NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String @@ -65,13 +62,10 @@ NestedBlockDepth:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> NestedBlockDepth:Tell.kt$Tell$fun send(event: GenericUserEvent) - NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$@JvmStatic fun main(args: Array<String>) NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveSerialData(file: String, data: Any, logger: Logger, description: String) NestedBlockDepth:Weather2.kt$Weather2$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> - PrintStackTrace:TwitterOAuth.kt$TwitterOAuth$ioe - PrintStackTrace:TwitterOAuth.kt$TwitterOAuth$te ReturnCount:Addons.kt$Addons$fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException @@ -91,5 +85,6 @@ TooManyFunctions:EntryLink.kt$EntryLink : Serializable TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter TooManyFunctions:Tell.kt$Tell : AbstractCommand + WildcardImport:FeedReaderTest.kt$import assertk.assertions.* diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 943f0cbfa754578e88a3dae77fce6e3dea56edbf..c1962a79e29d3e0ab67b14947c167a862655af9b 100644 GIT binary patch delta 13895 zcmZ8|Wmp``)-~=Hdu)0n3Y-8OvyK$p9^s9MM|Aj$miotNhy-{udLczZyd9uWtD)X_{|!LhIEF9y8(e*Z zW>^w$u&x|i9OjL=#6Nl~*ERulzX>8C-}o;iSMRYdfCU5d`~U{V4>HCg0HG4Xg2uP;fn!>S9+>LbuWbc0bETMQfo9~h}yI*TSv;Oikl~t-+xqI-`P$Rj@yi{mr2zC~s1snMT3!OPBdJ%IDnPXq+pl*Z>=+?qo${lkCSKmwTlVjfb3thU6B8yFjr!tphOs*G6 zwL`RyVAUXj4p=9&@PpWK)m+REuvHaq838TEhY^7W+JAp$ zZ^y;8`Z*@VqJ{sFFj?<|7SKS@G`$Yi)gx%nOi@Lr zCv0IJlFz0bP(eDIW(uWNq?;8zEAb+uGgnkLk;y!4XhA6=Eoa<`+|;6mOq>z`%ir@z$4)Mkd3 zF=hFo zyd{*bRQ4YUe^bU*Y`__)Uhu5NIjVJ~a}{lHp-_7wI?#EB11XcqmdY>pk`JJ) zW9Rt!tK=N>fZ!UDomwMnb`0EOvTjcNl=yW@$c!OAg*=l()GjZwSyJ+o^;Zi#I5*uP z$6qeih8&g8E(pNSneK>93A(8*%gvwv!0V|SqGcj55Y7`=N*@pJx_ig3uVuf-G~LJbm`7nxNcZ>Jgqy(LTHu_C2e>STp^Pm{}f&^)XU}vzuU`UV&>e& zqsXNXSs;Wri|?NhCq0vXC5$>9Cag$adyWz^x@NCiy2${9Dc)Y;J8k1Z933W$3$H}g zCQFU1XwzGm_WUheXvnDisH_%BdzMgNwk2^mHcQu*x>U%iN*B^8U(eVz1~(%`kV1Vb z=9T0xmN?bQMyrrd?u}jer}zV&sCK6zSm!zV8A8dP6THF=4*V{_K*E*K<)I(Q^(eV!m!vu##-2g|G z{RB;{gJB_X-w{ANq?ft_n!@=O8_gj6FxW&zO$7L3@NjWt@R{NxMbpHLk6;=2$0P5P=kKc1_85inX z#s$&s0zhV1cz>nRb#|D#N8Z-=Tphm)sGH>9cz3K3I)6XpimJW0(6$GtLzN(YPu9%R zdFXG9|30AZME4r@joC0IdvBBe08mF@+5Dd97p$h=n|pi80Cn2n{ev!S$llPGLqHva zZ3*OmW%!Qj>C$F!Ffafl7#I_1(gz!aa)b{ebU*=yH%^kr=~N?|2&2Df2o9X=2B?U!#R#+Cj45=f@=EcQx+9J z=X3~A=zbX29Fqn23m3dm}0Voj^Q9BjI=MiG+NZ)YCYn@r^qv(xE3=)&i z=(ML301=rNTptvUt2tnsPb1~G*DWFWoZfv)wV|uNW%?!)jju`jN(K-0$JYi!ofNup z9K%_ucHwutbZsl~vDQ!Jtj8uI6WA6K--@?8+_=t>g|kgUeC=w`IP9m&*fuoO3#A;t z&3@=3;J0>yjM89?h5MG$S`wW+=vyYOWQGhIP`^vScM8^JL{mGan5uTJPvAg$0z}8; z zhMi+S${H#^wF;eU-0UHJDo$QwXDjm{ns>^ltubKXtd>6Bq-=ByF%bHu>2&e&uZj2X zgWIq(l^;Ab7#I@h%#j1AtBIkB`GO*y!i;1K+_SZ-p}4jmP7#%E-=>{ zK(3*ObyAgDLnbBLObTWJWNO7<60SK6*!dD~_7JOWTB*}(*X)ox0{lq5ac$ABkcL~0 z9qCHT8^`QIe_4-BW&mIe*&0VT6w|oJ9hnOO&oZUe!rP+gStQ)h5ZPhBprHZI;So+g5}&;adp<|7#r@DG!wXmtwdwy=7i>a`x1D4 z_N$0`Q)>zTVUU%@RzlG=4Nk1hE=_klWj|6aj`KJ@S`y^%bifkdX`s!A#|mpM-x;SF zg;bju5cA0?a}%hk=3AL^#2B>5X(TSne6PDWY5gRVvn6nKl;vg?SIbv^Uz=+4aPUft z-$}QR)+_U?eX*p)V0%#0@S46_6c($OJL^bPj0Ij}up8}In#GQa&Cp<#%ZPjx(^97{ z8AfEgrNRTg-l9WJrNJzHx1EkI<|n(P3VIwFlTvMxfe=V&NL)4MubdHqZF)&Eq4`+% z7z;>s(sjUsebUfFF;~)_%@3BDl8i085o$H!*yBv%Z27d~)|jfg4DhJ&nMb((B#4hOfeBhL)g+r)f%2be?s2ox zT3j0k+Va^9`gqO)FoUV@F|((*vGxN>?5IlvC!BzW-8cyCy_)Fl8W+eg<&Lz^s>dJx zkly@2Xzzi9Uf%|1pF_Nz-3SgOx*+ShK(x=XUlP?;EfoDqAkkwyR*yjIcD#7-@=|Um z{T+V}q`6)wnSO#*N#Hp8QT7^>6R+H^_o4LBc}$aD^@(1!+Y54YF3@A|Cupsfz@Wt8 z!KwmSb9}3l)u^Y+V6W6(bL3hk;XTY4FNy3hKhID#Ep#xLM88?`xT=lw3xsgN;gKK@ zqpElV*j#e;{w`OPYcb1_szKUtRLygjq2ldhGJ$8ksyH(hF%^w`&FH|zlDK`DfuZ_g zs}!{hMk^~48&b=jWqG2*^m8?ERreHIw8dgR`Ugj*t4Uo`^U*56MmU<^ zNxcuRh+Kc2>W~lzD8S6}Xho3s9f}{o4@tIc)G;lKXi(HJhZV{qSH1-xj>P2$NHEK2 z)TjOy%>(9Ot_zPO)^tp@AsSNd+`R?}_2Vd>=eT{G&TfITkeW@p{F+FTJf(n87##z& z!%w+6-!NJ*?9Z(hbZv^BG$Y1`BOo~*k7jaZ)9%@;H6F+W!Q%IV4qSM85; z0%xWZi_wc=CCc>2rd3Rk3C79_rJH1uG?yFIm4f6Fdmts<41T*;3ek&p z3(NaDK3iIDa)MaUD{_;~fMV6obrT6_K$c+eeRBJ7jd)c%0jldoJX`EWz8M$b1s|DS z)cr6)em!+P%GjM6uQb6CQ!FvUb%_>qbKn=gHl=@K-Z*6_VaD=;!?P9pr$Z?6NrB%a zb_G4M-UkkhI>H@+kP;eS4p->q_f+&(R^7hyRsS9Xl94vA^AYlM%tdNdHQz zFQu?Rau!C@&&Dn;i5iEhn3`y>{O-m^_*h+Jp6C?D+5yn9Vq5XVQoUe#BP3}lqvHa} z@x~UctaNE9PwnRg6+15NJ5k(PC0dETm#QxXY6&uTqupm)GVrsvKC9o)&*mLo9?$Ot z!SFjh+!mr{kYE5A#urFIBv?<(6-HtqfprK#3H4dylz5j`Uc)Hz@1}A9OXe=4gf3_- z$P|^SpeQ89xlL`pftC^4tO3N)JXTqmkbruGAsraU5Y$fyMd~L3r3t8-SfkX{n4<`@ zhBKAeBP_1Rd8q`<3^dio2W9^9iYW?#m-!IKDO7ge{vC%1Y>dWLslyLNrm-!*YU3Dy ze|qm9gwdCJKZlwcvaoV%S_%X-k_?QIf2zuAG&32WtJ6NDr0i+<{w;CG_St&I_7HtR zTiR;!)_1iw&#FKwAGFuBze6(_%DLu?>|K(H5bf{br_f5|#qa zNOuJQhSU1PGQ+dltC{ik3sA?PcKcDJg;_^-LCcLGo+|3VsWx0vMNOpKz3*U1wGG0{Z@O=3gt1Ay|67ZJC zGe%Q2bP}rYtE^Lc+ybPES@Snxwlh7Ydq$c{H?d&8e>!Dvt=dFxeS0fvt=u3$KHuU; zKHr9fCbGGQBeJ~@{wdgJi6Ah40fcT>yGRWEe)%=j!AaG~XDaHNdzsU6*ZJ2XC5>lv z=IT$K4yEi0xt7i<^=rn-$1nOKKRQZ$7df4uU#`?ddlH+Oo~+H_Zq!-}6VK;|?PGiI zhbt$ffNJ|--Bn6(L{pZ#!&ykjgBXEs%hmxg3vB~;GMKcAfeq~#2~f9vw7{>?pTu{T zcxLiHNCP}pJ_fYl3^gBy_}h~U`lx1^?)q|U1cti6s?Nt*RvSgF6WD8U%3uk zwC7lEPg``Bjt5YXNFE!^nq zJC-z}n^zNvd{jVhiv9aKNd}lH0$n97EBjb`Fh+7~amqAtrK{@Sn3QZO3BBiUIo^n$ zsiS{+L+8B0e&`mFnEqM!LCLnzlclx?UwZ(L6!FZ$b53#xA2caP^zn&!GVtipn{W`U zvN9yG-?@6)3`HYt>E;wO*N_UGd``TDMJ+e<*WUe$SGeaBU)dJHbvUp$J?}caKfP>U znZQtJY@$~+#6FOn9R6m86Sq3iiaaWa3kiz1k>ntIk2*6R+6gchFxKLcBi9EMRVQrl zP~vO=WAFX7o6BB76*mwH?R^-5HX?KAu`a^Eplkmc zSXpmBvQ4t(kVfyQIR#|Wi7PYcy+x;(5j|LOp3()IiR>2j9**}<*nO2NiED?Z;)iGh&PH4nB*kN{VVt!lYX*(jAlnZkabB{Fa7)iF?pBFk(T+)xyg(Y5TUd;DX&MX&_}`_=Z_KcQ9;Ok=&YEqPyVul9sRG%P!*byO8nRS# zGwOm?IyLaeqMf=7AGF{L7v%GKmeM+;#U;vPs0=0R1WAo2JIq8N`PGDe}Q zt6VP!Fqln^U#5ZJFp?b?d*Q}Ynd3Q)jTU;{RwiqDncXA=DXTWhkWhiR{XF9aobJH{ zEYYt-`Hdwp@ZQ5$_i&f`=DA1D>lgJ>_PkLE6#)L#3R1Giq@XA zCLtGAgOI35<3Y-&55pCx#&@_R?w|x@%3$Q-X|@=Zhuo`C@cOG0@M*&sW@uXQJz-M; z=ZcUIw+bXwCV+k?WF;Ugyrm6gy8KjZmaobl;Omt^`!m*(!@&}j)uCT=+}RbLo7WiC zM*7VJG5hnkugII&>R-Jyx<}$pNBtEizA`Gn{GbTy^WPi*o!^5_gH8ME&+{<}nBbSA*p<6A z{c--0SNgk{iH@g2s&K3L#wl5fR-H5$YrMAEA$gwfPC&GdtAb=bUk$?Md6^mdF&^vj z+iAp=tz8ZK>*?)QgEVBG?CnAb`($wf9*1w->8@)hg(hpH^%IFjGqTs7<*jz0J-*C! zs)=j2cA@=KgS0+*LX^Qe*))69yFm;(i`r6`?_p2Dfi!AQh43;ix#Kv8_*W|IsGg;f zJ=0%L||IPz~u^1P?ZkuO7VD7>GEfT=K*2JP!?hLF1f0rSkXpoIojW`}iLv zt$qt5Kd$Ty5UwS~N|w!IW4-TDG6g9!ecEoE+JUM(=T{d4yASY8>tlDG_XdEUinvXN zl>XB_*;iM^53IG90-1uxg#z{ov9M-y`(|4~g#J?dVQ&7tJ+a=N9npjr(_lb@G$v24 zPeA4UfgSFXLSe$Ghn!^hh)2|+YuV|~a}U+Y9iy?b*TKn*`y{ADmlq%d|HzJn0mW<0 z5McIquX})(09`s?@%4OLy)I^TdiKP=%}XfT`s{oX5eauP0FS#ZH3$bT&E#E)1%_v48Kc&JbnK@KR+fCJ+WWg`;cXecj9ij8zP$MV%S9InmL z#D$p6%KIKx&U;|#5fPg~KlH~fC7Sh-(Ut}5+tSSriumK>DDF&sl2pa_A|~tu_*8aY z(*Ud4=(+k5;ke&7V(y`$@j|FGqk0(WA5Wc(N${j@=7U}Xs^XNgK(<|>qug3-b1T3( z0=#Hgj}+TLlDhVm<>&!j$jvWXm6SLkMW&2k+;_u9Tq#<8uKtToJ3Q^==VQ0eV{+r6 zQn5p9xfHk@%P_FbqYM3DFnxUSXF^sk#Ms{)T4quYP`fK;T+Tj&gRl6sm|74UbHHrF z7h!QzEST^cpRO6L8_~zXNp!niGl&79$k_8RSj0W{xMrR)D4`>~tNrK~*s0gkO-PC^ zu^*~aOBQF>qG>`%KGd+7W{nGqd5lc0%E_*&rn?MObfYvgPvJ%vawv{il#Km=$-hF* z1V^<{OA_t~X|u>{5ljynGhf844dJ#q31&xuibhPgP;6z{C2qw67U617_1*$=(_{mu z@T$|cK0GIz9sS4`1VcT=#Rqfsfiwbly-A61ih$VWK@T{K(t%VCA4=VJ4(eT` zLP`DnbAKO!X02C>qoh6kk2SEE|nQ8^J~0S)XyHMI1`BA+8Q-{{y-|Sc=j6N9xVnV z3^giq-U}tR!`_$ty{geQQ}xVo!CwzlXx}-}k2&VU3u7n@(1G0xP$36j1GKVJtLydS zm|^pz&9wE!Q>OWGMLY+Y?=$lIM$IKdF`8Pw)uhzhmFGtIyWl(qh0C@9BbzwDR>rEa z2gc62w3u1cW+De8tCw(3SQ8EK+t9l|ef|)GLRlRJz>SleVh^o zSq>XS(iJr>IQL-5^9LMn-MBxnO*FN{K2{7JVUpW5nZ{sz&_Z(dXDW?G7lmn%1nU|B zqC_R`=83Y=g^uel37AnfplTx)W_%O1pY@^^#~MgJg`0^G07b7RHOA>7K6Vzom_M3= zbD)3(BXXoqR6UFGHM9a3uK)SxX-0%jvKG23)#s6{vbq>#o$1tZMI5hU1c`fGME7#Ij+u%*rdsnO7yaltUc zz)OZMW*a=_Q|k2CFQ+lR%Md1Kd~``A8LX7vMtOupY7HV^E*;7o5$|Yq;EZjl%s-BLWa)nM| zOY1bfH5&%ed5t0h_`z*>GNiXhoMBw9+W7 z4U!O;)Tz3n;x64wHcYoivoslIkj9IN05|H7X~GWEx-k619Z-KjWv%8@$1wbIvAFfI z0=AQoH{3yl1z|`pSg$(!>x0)nU|wT@4i`lCchm_nrU@Y;XR$D^5wA!Ftl}*9OwXFZ zai&Zh_YNnlz=LEccY_eUXOEY1;q&Pd;dLtf$RffP4%P#4ZyIjV&0;_13^ zIVGMUzx+5jLyq55_Qz0jPBx~-{DfuUW)hKduk1gv0et-e(ZN8;IIdhtV$3N9Bg((Q zw5eHG)FFs=ewUwfdHfvHb$&&i=h{#epIdWr+=YE9)%453DlIOHLFX;%dv2LDNMrMZ zEWU|CvEYY*(2SE$Y{jAd$QU-wd*Hbe5yO+Lu6Ux|(Y>L}E_jNPR+TX@Ch(#orbP8g zv+Z(oKz1gylHHGKB*FbdpSh7VBM2KVmx2oj>?q8|s72`}5s)jT=s4;lbRw$cKh+N{ zVTxW`s~QW~rRB;e|7pxFoJ_Vm^eVjcddUh0Xp(NhCBZ@Uya;(x_wkvyH*^ds{2_H? zs*PV?33(>MyJC_<)JC=|9II5@I`QnNGgZr z5AfQVuy5}nzXlGQGV~eESn9UcL_U$gw(QjDVEW4b-o=BQGBT*a$1Fk+4bm2n^6m6w z_hn7X46IDL7iQZ8s+_(8yX!fXqM9htq_Ts}08b%snTZMmP}{6(anfizqhpR1cR61k z=sfzRN*!0HP{Z76PDg%PUY)rjwhuy71^5D3f^bR;(fQe>3U#zrWwe0OSYjHZ-eSJV zuKnE7`~*u%-HShx%*b9ZPU~(Rg=`lQI$;iBY#2k^6{Ef6e9D&EK^irorXEpE!h=>^ zVxH#pyrndMgk)Ff-ke*RFsPY@B3AM_;Kj`PIJU@EH^QsIUo1wdl_wfqd48O^9?06@ zt*>img{+gG%WiGU+&V)`jeJUPSDDLhd#nVrUr~dURh(&O#gMnA0dEg-#?fg0Wnp#P z;4QjL{Fv?Unq!!)POdN%ZI&vU*Ww};bqd3@5fb_<7mIa_w@U?X&ed5f1FCQ@57aR@ z)TUphLPht{?j%;+T}Sfla?uiG26R^?7=x!#CUXw+$_TQx_%vLhgg8LVJz@{QVxH;M zGcV^6&Z%`yWalhb>$VS`{^Ex`w@cldtZ8t!!exC zu+Msuk)M-ylAjAz8{yA&TjgR`O%H1H0T&$<*+K{2-<~=1E0~C+w@CzUg>GyIegmx$ z$vp-I6CygcS8Jm9rR{Wt@W?<)IdIk##3DUE741Dg@lQ~Lskm-7=|2%)&XCF_8|780 z9d-AgO*4e1uf}M3*FGo&%&eG;OB^Vm_x8i73V3P?d^qdJMvO&{H(jgc?n6UYZ>-FU zeO%|qJ%xvB;o+$e+CHm+Ot1UgzOrX7_G!pZrt%?TaOs9ZPg>i>-gg^Vuu6p>LEd99 zGlCZbE5(oNfEP{~x>KfOZv6XWA8zfk0@R+{;r7WV?(wWFRaGkg&mR3j$wJa7CBWz= znwfnWiE^@dC=n6jrAY4vvH*;b5{E#wK8AoUW`vT3W+8gyt9<*hPl1ID>F3bkLniI?`*u@J2zcd_cAH2?L5O|qzu1jQs$J^g9=beD zYoEgyA^AIv!P%D3;3T_C#zm7j6=+ACjtf5->)lXATb2p>g%qD7L1EbTMh(z$4oMY) zSZft;+pfN?a7x#%4}(P3Q)Gvt1F^8eu9}_PDW&}_2hhqjF#&SGUnz^`=V(U{;B;`G zt7FmRinElmq%KVXaBZL$+hD> zLe`*wO^B_i5W9q8#>l8J4;5{XbZg#@Z9|D|{gN8}jF1XBNzpi*9R3+-F)w8EbJ~In zEdim4jC?)`IzcZ1_`5oBWd#yPJNc%ajkte>^q1KY$#LzK)`jz_7$%1`N1_tdhr^wG zp92GvW>iDG)!1`I3*Y3;C)Jz7**nV;DaO_d19A_8qX%OCf-KY-GEZ#Nv;2CZQ*ht5 zY`vXc7yAb|?h#Z_dEKDC)Wp}g7hJDlI>P+ctKoq`U4!4az+ECGUSGmfHRpW&m_%7? z(o7gajY+w(Le-L(_Al|yQIvl1gk&lX-5BMZn=+~n-N}$`J#2x5x&B1EG{drVp+i;- zucW)%=6bqw%wNB|=k!-_k($v{gQB1ZX`dn0tu@(Z7b0$g5k88nHYIEE zT{wBh?|8X1yS1ITl!hS_>>{cobd%i3<#)=amBnHn>p;m6f%!T!BSP{_9DL_Wmv{PtyL9hoTep$i_uAr>^@7u^a($-HJh2k0xNsYVmt|v+kCWusAE%8~f zgZeq1{C!DL z7|_)gsX-J$DBwOYs|TpK6>I&l2*#dm_B%7y(JCJ?jaOVZJg!;eleEd~bT^pJkrk>q zB4)r!XRL!mow*tX6z6JA){(LgKapsISwxE@P|Hy&;*5I17ktf2EQSu$>0G&bDc^|D zoB?VpoqIQzg72DO!zOL#jXEsFWVZoyX*Q+>cyNC5+bi$(-R z2PXnAH)~j-X7q#KV*r7K0Tj#Pt=_Ix!xQizqfxG}vfg*swPul)E%ElLW)2B0BOb4U z$5{w|1BT44k;f7uS&T@0UH_mBvgr?Q_m;tun8!5sqbDu3_a@H76e`xzggnje$~Vo7 za$jN9vO%&+?c(NFBWd(HH(c*Tf3txzhrnp4X1859WXnbk!aVPy#xl`hJYOb;9$6q{ zkbx6NHJ;r$;+CoL5@BT|)P$#Nd4mLhJ?! z#V8L2#1$FDnc_k5#=YeMy9&SHkG_wJOT1g%-w$u1eta|QD44f{Y&WqiWW218tS?qy z$ZDkAwNCgrzLY?-u2WO8%SB`AO_vLdwg{s)2>YT(Vp}$u)h6yDPl(o)wFGQ6GTv9!92`>rC_Xgn9)BKfMk>B0lFK$_ux zk^my^G@g^?|Ds?LnEwzyJ7qzahke+uzE$SE-IhBwTL zCnKg33>Lk_tsV;Q?3Nd07IG)>PA43Q@@bD_XViZuJnF+-SR9eSm-b^YbLCU7PG6GQ zJKkO|*b;^O^%Ehg6e-0+bze&Un{k(1?Aom@b7Sm z?b{}WJ!Zfj23oRMKPiLEh^qy6lZ(sff1?M#aP;~C;P0@AuUam$iHH$i(Zc-_8++)) zGiB*fRHaTE_*K_lAl+<$IklN{WiruTjZ?Ir>rocinb-6%~rZb)Z@l>WsZ%cVnF`u(k z3MC-R0(^u8vlUE{9TX~VYef_B+y~v-T`n!_ zJXHL4N_pJy{bQGCGEJ2vO`^5M=(MU>=QoaiN4n$ZmlEhRRC09~b|CV#QExkR{!cxv z-Ih(Yq);JB({7Iv5SqD14A&CD>{9d#mQfp_-1nX*824hiHi&jI!rbzk3^mafyBi2I zXwJzh@J~^n^Qq+Rev`}V%T)Pds`2QDUxGv4pkJOaJP+l=87o}7L-RV1V*p70%Q?kQJ!b+v(*=vXQsHF z#w&NkJNb4_Kvu6hrx0e1Q_pLru87EM%Rez`mTlk~vCAr;IKZqQ$#>gK{ZQNJ$F@r9 z17m<_yD6oKG?O@e`O;WsIhdWwE)Z7*SyABxHvKJ!x|y(wVq*Eg`D2Q%Q#&zSm8c_X zY`zJhB88q%6!2%9%}+RQMhWH=sbw#8{a(embAwu zeRHhkOtBY=U&ubKu7vS#2DPzJ+WbaUn%Eu`p1cjDEU*&qFGKE(o%RZ13w1x?o_-#{ zj3y3uOaJI8nlJ`Rt11>dUer4~gzlg1qwk_n+`w_Q&I230F}#e<84l6$Ub}ga5BLCy z$uT-aXsHnb5x(Q2(qiSxMHMrLS5E#p#t6L)COeA@Vy#t82W3I7zxNN*jGG$^^A3V~ zTr=^dD(liTi!S&uFU(~grGKHPJ3#7Wm91!jh!*X-6-6}Q?cA`2ld(6Q{A_nw+16`p zBq**{Pk_!LEyI8)FurdbBN-IqyhFR52Y9f)rE-#p}V=M?A%c$M#J3kjR;+GEA#vBv7ig$61YKjN2FsuXxl6YE;g-oLfc3d7ixb z(~0wjUXzRlz7@}MhgnS+FRey=b`F|l<3w;qodOa{(-yU^k{7Owq0>0sq7~my3O9?# z;MqUiGm}Q%_f`tMUWXlWG>uF0_?>-d_6ru!DNoiMD&X~fg!7a0H9Z%=3kwQs-Q1{g zxIsDbEXG9ly4o5M4LODy_vvf8k1Dey9QW4T^up55&l zkpg05cG;FhOyo7R#xy!3{&xPzXTpzSZpRkB&$uR(?99to5LDHD?ak+~^R*OGg2wFv zUjX`1J0_eHXV^8UJXLSFxSNPlDSRKCJ@A^Jrtp08!98KQXBT1L%avWTv-8l?va+Jq zHqd)|JwByFcmK%afGyJ=rb@ELtB7tehaH#)iRz5v6?C;mDxZj)`upc|y>)S)VveGb zj?RG?$-D;ms{Mi9UTajprUthRTIksl=OfjZ8iD{zhh{YOLQV$~PKQE~HHn!A-`+on zR*Vi4Qpbff5whUZ9dr@0UMy^6)_zH48Tiz-RM+T2vk9}rr*_Wy-CfoxGjcedo-{zF zI=^!G@*UT_@;VTiU+I>Ht{NTo^Dj&T`?{QK>&9s}PXt=TxQbmKUDW->h6Eh)@|}uY zfxqy8(^9cw%+k#m9NNz`x+UB*DrrBVuFm%-eo5kp!74OI^qtOcOgmD z8KADRYxrHr>DeRsuJG&}MumPmOimcRYf)HcNZ@n+9Z>VwI;H|{kuzD-~H{S8;hQ?c2 zjtv0GZ}PmMOMCz*ca!f8t!=)0eIWsWjJ71-P|23{TZz8yg7Kf_uYY%rfKs-#-mI6~ zWDtv=K%3NLAnu*Falh$e$sp$0L0w!lpwgZ9QTM+QD_m~`Hwd`>zEy>8mki>B7c|Ao z1M1j$C*t3TL;k-)g!W*N|5no|$$~>*LSlkyga9DKJp_ntp?@6S+sqXOyh(8W{uKnw zfCBb--`KW2G6-skzsABWLHJMO%+dg)|G1h+znMw@zb^du$snNhKu5aNu>aTVhA9Aa zypI5ZZuUl#f&d5a@?81@G6)V!kn(}ZTjkqZ1;HA0Zp8~i*?9jK@7DzF5Cwb{M0EJJ zdFQYCg$>j{ouh%B3M1Qs3=ZGV(U(Iq2#NQ~M^NV>2IYUw?*FKE|8LZ9$ASPj2hfxc z)|-fz^uOHyRf8gcfie7#JF3$^?wBCp5zhlK2f^T{`>T=fi_P#-dNmI zGKjp)zxq`<#rm&d{*P?xe});I^_TmbiV9SEit=9}|1ST-{Qv(9yx`vu!D0;he=gX+ z0@?prp8cP``iuSvME>_G8=t*R-p;@1^t1OXT=hnT^!!D1c2WH6hj~s0Vcqu+jSSK~ ze?K{$!~Z?8YDWJup9~X#I?msx!{h`2w0@2N(KYpMNVp(=<47*ZAV}x_uET;%E(l>n J*WbtZ{{Z#P!zlm& delta 13442 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 42defcc..37aef8d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 65dcd68..aeb74cb 100755 --- a/gradlew +++ b/gradlew @@ -85,9 +85,6 @@ done APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +141,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +149,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/properties/mobibot.properties b/properties/mobibot.properties index 0f97f3c..fa8ce50 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -25,28 +25,13 @@ tell-max-days=5 tell-max-size=50 #disabled-commands=die, ignore -disabled-modules=twitter +disabled-modules=mastodon # # API Token for: https://pinboard.in/settings/password # #pinboard-api-token=user\:TOKEN -# -# Configure app at: https://developer.twitter.com/ -# and then: java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth -# -#twitter-consumerKey= -#twitter-consumerSecret= -#twitter-token= -#twitter-tokenSecret= - -# Twitter handle to receive channel join/leave notifications -#twitter-handle= - -# Automatically post links to Mastodon -#twitter-auto-post=true - # # Create a Mastodon application access token at: https//SERVER_INSTANCE/settings/applications # Make sure the 'write:statuses' scope is enabled. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 6766f62..98ef74a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -59,6 +59,12 @@ object Constants { */ const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar" + /** + * User-Agent + */ + const val USER_AGENT = + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + /** * The help command. */ diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index f56af2e..d82f011 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -74,7 +74,7 @@ class FeedReader(private val url: String, val event: GenericMessageEvent) : Runn fun readFeed(url: String, maxItems: Int = 5): List { val messages = mutableListOf() val input = SyndFeedInput() - XmlReader(URL(url)).use { reader -> + XmlReader(URL(url).openStream()).use { reader -> val feed = input.build(reader) val items = feed.entries if (items.isEmpty()) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 2dff959..dabb7c8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -78,7 +78,6 @@ import net.thauvin.erik.mobibot.modules.Mastodon 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.WolframAlpha @@ -438,7 +437,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro addons.add(View()) // Load social modules - LinksManager.socialManager.add(addons, Twitter(), Mastodon()) + LinksManager.socialManager.add(addons, Mastodon()) // Load the modules addons.add(Calc()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt b/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt deleted file mode 100644 index ab078db..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * TwitterOAuth.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import twitter4j.AccessToken -import twitter4j.OAuthAuthorization -import twitter4j.TwitterException -import java.io.BufferedReader -import java.io.IOException -import java.io.InputStreamReader -import kotlin.system.exitProcess - - -/** - * The `TwitterOAuth` class. - * - * Go to [https://developer.twitter.com/en/apps](https://developer.twitter.com/en/apps) to register your bot. - * - * Then execute: - * - * `java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth ` - * - * and follow the prompts/instructions. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net) - * @author [Yusuke Yamamoto](https://github.com/Twitter4J/Twitter4J/blob/main/twitter4j-examples/src/main/java/examples/oauth/GetAccessToken.java) - */ -object TwitterOAuth { - /** - * Twitter OAuth Client Registration. - * - * @param args The consumerKey and consumerSecret should be passed as arguments. - */ - @JvmStatic - fun main(args: Array) { - if (args.size == 2) { - try { - val oAuthAuthorization = OAuthAuthorization.getInstance(args[0], args[1]) - val requestToken = oAuthAuthorization.oAuthRequestToken - var accessToken: AccessToken? = null - val br = BufferedReader(InputStreamReader(System.`in`)) - while (null == accessToken) { - print( - """ - Open the following URL and grant access to your account: - - ${requestToken.authorizationURL} - - Enter the PIN (if available) or just hit enter. [PIN]: """.trimIndent() - ) - val pin = br.readLine() - try { - accessToken = if (!pin.isNullOrEmpty()) { - oAuthAuthorization.getOAuthAccessToken(requestToken, pin) - } else { - oAuthAuthorization.getOAuthAccessToken(requestToken) - } - } catch (te: TwitterException) { - if (401 == te.statusCode) { - println("Unable to get the access token.") - } else { - te.printStackTrace() - } - } - } - println( - """ - Please add the following to the bot's property file: - - twitter-consumerKey=${args[0]} - twitter-consumerSecret=${args[1]} - twitter-token=${accessToken.token} - twitter-tokenSecret=${accessToken.tokenSecret} - """.trimIndent() - ) - } catch (te: TwitterException) { - te.printStackTrace() - println("Failed to get accessToken: " + te.message) - exitProcess(-1) - } catch (ioe: IOException) { - ioe.printStackTrace() - println("Failed to read the system input.") - exitProcess(-1) - } - } else { - println("Usage: ${TwitterOAuth::class.java.name} ") - } - exitProcess(0) - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 8847bef..3647b31 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -31,6 +31,7 @@ package net.thauvin.erik.mobibot.modules +import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage import org.apache.commons.text.WordUtils @@ -106,6 +107,7 @@ class ChatGpt : AbstractModule() { .uri(URI.create(API_URL)) .header("Content-Type", "application/json") .header("Authorization", "Bearer $apiKey") + .header("User-Agent", Constants.USER_AGENT) .POST( HttpRequest.BodyPublishers.ofString( """{ @@ -136,8 +138,10 @@ class ChatGpt : AbstractModule() { } } else { if (response.statusCode() == 429) { - throw ModuleException("$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later.") + throw ModuleException( + "$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later." + ) } else { throw IOException("HTTP Status Code: " + response.statusCode()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt deleted file mode 100644 index d4c02e1..0000000 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Twitter.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Twitter.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.social.SocialModule -import twitter4j.TwitterException - -/** - * The Twitter module. - */ -class Twitter : SocialModule() { - override val name = "Twitter" - - override val handle: String? - get() = properties[HANDLE_PROP] - - override val isAutoPost: Boolean - get() = isEnabled && properties[AUTO_POST_PROP].toBoolean() - - override val isValidProperties: Boolean - get() = !(properties[CONSUMER_KEY_PROP].isNullOrBlank() || properties[CONSUMER_SECRET_PROP].isNullOrBlank() - || properties[TOKEN_PROP].isNullOrBlank() || properties[TOKEN_SECRET_PROP].isNullOrBlank()) - - /** - * Formats the entry for posting. - */ - override fun formatEntry(entry: EntryLink): String { - return "${entry.title} ${entry.link} via ${entry.nick} on ${entry.channel}" - } - - /** - * Posts on Twitter. - */ - @Throws(ModuleException::class) - override fun post(message: String, isDm: Boolean): String { - return tweet( - consumerKey = properties[CONSUMER_KEY_PROP], - consumerSecret = properties[CONSUMER_SECRET_PROP], - token = properties[TOKEN_PROP], - tokenSecret = properties[TOKEN_SECRET_PROP], - handle = handle, - message = message, - isDm = isDm - ) - } - - companion object { - // Property keys - const val AUTO_POST_PROP = "twitter-auto-post" - const val CONSUMER_KEY_PROP = "twitter-consumerKey" - const val CONSUMER_SECRET_PROP = "twitter-consumerSecret" - const val HANDLE_PROP = "twitter-handle" - const val TOKEN_PROP = "twitter-token" - const val TOKEN_SECRET_PROP = "twitter-tokenSecret" - - // Twitter commands - private const val TWITTER_CMD = "twitter" - private const val TWEET_CMD = "tweet" - - /** - * Post on Twitter. - */ - @JvmStatic - @Throws(ModuleException::class) - fun tweet( - consumerKey: String?, - consumerSecret: String?, - token: String?, - tokenSecret: String?, - handle: String?, - message: String, - isDm: Boolean - ): String { - return try { - val twitter = twitter4j.Twitter.newBuilder() - .prettyDebugEnabled(true) - .oAuthConsumer(consumerKey, consumerSecret) - .oAuthAccessToken(token, tokenSecret) - .build() - if (!isDm) { - val status = twitter.v1().tweets().updateStatus(message) - "Your message was posted to https://twitter.com/${ - twitter.v1().users().accountSettings.screenName - }/statuses/${status.id}" - } else { - val dm = twitter.v1().directMessages().sendDirectMessage(handle, message) - dm.text - } - } catch (e: TwitterException) { - throw ModuleException("tweet($message)", "An error has occurred: ${e.message}", e) - } - } - } - - init { - commands.add(TWITTER_CMD) - commands.add(TWEET_CMD) - help.add("To $TWEET_CMD on $name:") - help.add(helpFormat("%c $TWEET_CMD ")) - properties[AUTO_POST_PROP] = "false" - initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP) - } -} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 8662392..ebc2aa0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -45,7 +45,6 @@ import net.thauvin.erik.mobibot.modules.Dice import net.thauvin.erik.mobibot.modules.Joke import net.thauvin.erik.mobibot.modules.Lookup import net.thauvin.erik.mobibot.modules.RockPaperScissors -import net.thauvin.erik.mobibot.modules.Twitter import net.thauvin.erik.mobibot.modules.War import org.testng.annotations.Test import java.util.Properties @@ -62,7 +61,6 @@ class AddonsTest { // Modules addons.add(Joke()) addons.add(RockPaperScissors()) - addons.add(Twitter()) // no properties, disabled. addons.add(War()) addons.add(Dice()) addons.add(Lookup()) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 0d66a0d..d30977e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -31,14 +31,9 @@ package net.thauvin.erik.mobibot import assertk.all +import assertk.assertFailure import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFailure -import assertk.assertions.isInstanceOf -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import com.rometools.rome.io.FeedException import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed import net.thauvin.erik.mobibot.msg.Message @@ -66,13 +61,13 @@ class FeedReaderTest { assertThat(messages, "messages").size().isEqualTo(84) assertThat(messages.last(), "messages.last").prop(Message::msg).contains("techdigest.tv") - assertThat { readFeed("blah") }.isFailure().isInstanceOf(MalformedURLException::class.java) + assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) - assertThat { readFeed("https://www.example.com") }.isFailure().isInstanceOf(FeedException::class.java) + assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java) - assertThat { readFeed("https://www.thauvin.net/foo") }.isFailure().isInstanceOf(IOException::class.java) + assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) - assertThat { readFeed("https://www.examplesfoo.com/") }.isFailure() + assertFailure { readFeed("https://www.examplesfoo.com/") } .isInstanceOf(UnknownHostException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index fb0402e..b3bd248 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -30,6 +30,7 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertFailure import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFailure @@ -48,6 +49,6 @@ class CalcTest { assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}") assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}") - assertThat { calculate("one + one") }.isFailure().isInstanceOf(UnknownFunctionOrVariableException::class.java) + assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 5e0e1d2..e4638b9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -30,6 +30,7 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause @@ -41,8 +42,7 @@ import org.testng.annotations.Test class ChatGptTest : LocalProperties() { @Test(groups = ["modules"]) fun testApiKey() { - assertThat { ChatGpt.chat("1 gallon to liter", "", 0) } - .isFailure() + assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } .isInstanceOf(ModuleException::class.java) .hasNoCause() } @@ -57,8 +57,7 @@ class ChatGptTest : LocalProperties() { ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ).contains("URLEncoder") - assertThat { ChatGpt.chat("1 liter to gallon", apiKey, 0) } - .isFailure() + assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } .isInstanceOf(ModuleException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index c2bb833..175af47 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -31,6 +31,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all +import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasMessage @@ -59,14 +60,13 @@ class GoogleSearchTest : LocalProperties() { "searchGoogle(empty)" ).isInstanceOf(ErrorMessage::class.java) - assertThat { searchGoogle("test", "", "apiKey") }.isFailure() + assertFailure { searchGoogle("test", "", "apiKey") } .isInstanceOf(ModuleException::class.java).hasNoCause() - assertThat { searchGoogle("test", "apiKey", "") }.isFailure() + assertFailure { searchGoogle("test", "apiKey", "") } .isInstanceOf(ModuleException::class.java).hasNoCause() - assertThat { searchGoogle("test", "apiKey", "cssKey") } - .isFailure() + assertFailure { searchGoogle("test", "apiKey", "cssKey") } .isInstanceOf(ModuleException::class.java) .hasMessage("API key not valid. Please pass a valid API key.") } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index d464f03..34f778a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -32,7 +32,6 @@ package net.thauvin.erik.mobibot.modules import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.isSuccess import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot import org.testng.annotations.Test @@ -42,7 +41,7 @@ class MastodonTest : LocalProperties() { @Throws(ModuleException::class) fun testToot() { val msg = "Testing Mastodon API from ${getHostName()}" - assertThat { + assertThat( toot( getProperty(Mastodon.ACCESS_TOKEN_PROP), getProperty(Mastodon.INSTANCE_PROP), @@ -50,6 +49,6 @@ class MastodonTest : LocalProperties() { msg, true ) - }.isSuccess().contains(msg) + ).contains(msg) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index d35a3d3..b4a277e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -31,6 +31,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all +import assertk.assertFailure import assertk.assertThat import assertk.assertions.hasNoCause import assertk.assertions.index @@ -78,7 +79,7 @@ class StockQuoteTest : LocalProperties() { isInstanceOf(ErrorMessage::class.java) prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) } - assertThat { getQuote("test", "") }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause() + assertFailure { getQuote("test", "") }.isInstanceOf(ModuleException::class.java).hasNoCause() } catch (e: ModuleException) { // Avoid displaying api keys in CI logs if ("true" == System.getenv("CI")) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt deleted file mode 100644 index 6e4ab27..0000000 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/TwitterTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * TwitterTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.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.tweet -import org.testng.annotations.Test - -/** - * The `TwitterTest` class. - */ -class TwitterTest : LocalProperties() { - @Test(groups = ["modules", "twitter"]) - @Throws(ModuleException::class) - fun testTweet() { - val msg = "Testing Twitter API from ${getHostName()}" - assertThat { - tweet( - getProperty(Twitter.CONSUMER_KEY_PROP), - getProperty(Twitter.CONSUMER_SECRET_PROP), - getProperty(Twitter.TOKEN_PROP), - getProperty(Twitter.TOKEN_SECRET_PROP), - getProperty(Twitter.HANDLE_PROP), - msg, - true - ) - }.isSuccess().isEqualTo(msg) - } -} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 4c7f7e6..ca650bb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -31,6 +31,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all +import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.endsWith @@ -116,8 +117,8 @@ class Weather2Test : LocalProperties() { } query = "test" - assertThat { getWeather(query, "") }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause() - assertThat { getWeather(query, null) }.isFailure().isInstanceOf(ModuleException::class.java).hasNoCause() + assertFailure { getWeather(query, "") }.isInstanceOf(ModuleException::class.java).hasNoCause() + assertFailure { getWeather(query, null) }.isInstanceOf(ModuleException::class.java).hasNoCause() messages = getWeather("", "apikey") assertThat(messages, "getWeather(empty)").index(0).prop(Message::isError).isTrue() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 4aaf620..ae1722d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -31,10 +31,10 @@ package net.thauvin.erik.mobibot.modules +import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasMessage -import assertk.assertions.isFailure import assertk.assertions.isInstanceOf import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties @@ -44,13 +44,11 @@ import org.testng.annotations.Test class WolframAlphaTest : LocalProperties() { @Test(groups = ["modules"]) fun testAppId() { - assertThat { queryWolfram("1 gallon to liter", appId = "DEMO") } - .isFailure() + assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } .isInstanceOf(ModuleException::class.java) .hasMessage("Error 1: Invalid appid") - assertThat { queryWolfram("1 gallon to liter", appId = "") } - .isFailure() + assertFailure { queryWolfram("1 gallon to liter", appId = "") } .isInstanceOf(ModuleException::class.java) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index f17ed1d..46888e3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -30,6 +30,7 @@ */ package net.thauvin.erik.mobibot.modules +import assertk.assertFailure import assertk.assertThat import assertk.assertions.endsWith import assertk.assertions.isSuccess @@ -65,7 +66,7 @@ class WordTimeTest { @Test(groups = ["modules"]) fun testZones() { COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { - assertThat { ZoneId.of(it.value) }.isSuccess() + assertThat(ZoneId.of(it.value)) } } } diff --git a/version.properties b/version.properties index 693db4f..27ca594 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Mon Jan 30 22:08:48 PST 2023 -version.buildmeta=986 +#Sat May 20 23:54:50 PDT 2023 +version.buildmeta=1077 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+986 +version.semver=0.8.0-rc+1077 diff --git a/website/index.html b/website/index.html index 97e337d..1ffaad2 100644 --- a/website/index.html +++ b/website/index.html @@ -46,7 +46,6 @@
  10. Pinboard Poster
  11. PircBotX
  12. Rome
  13. -
  14. Twitter4J
  15. UrlEncoder
  16. mobibot was written by From e57f80f9d9152d49853b754adb4b3f4b87e394a6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 00:16:46 -0700 Subject: [PATCH 728/858] Moved to JDK 17 --- .circleci/config.yml | 12 ++++++------ .github/workflows/gradle.yml | 4 ++-- .idea/compiler.xml | 2 +- .idea/misc.xml | 2 +- README.md | 3 ++- build.gradle | 6 +++--- version.properties | 6 +++--- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b76d7e8..67e6618 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,19 +31,19 @@ defaults_gradle: &defaults_gradle path: build/reports/ jobs: - build_gradle_jdk18: + build_gradle_jdk17: <<: *defaults docker: - - image: cimg/openjdk:18.0 + - image: cimg/openjdk:17.0 <<: *defaults_gradle - build_gradle_jdk11: + build_gradle_jdk20: <<: *defaults docker: - - image: cimg/openjdk:11.0 + - image: cimg/openjdk:20.0 <<: *defaults_gradle @@ -51,5 +51,5 @@ workflows: version: 2 gradle: jobs: - - build_gradle_jdk11 - - build_gradle_jdk18 + - build_gradle_jdk17 + - build_gradle_jdk20 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 0034945..c917819 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -8,11 +8,11 @@ jobs: env: GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m" - SONAR_JDK: "11" + SONAR_JDK: "17" strategy: matrix: - java-version: [ 11, 18 ] + java-version: [ 17, 20 ] steps: - uses: actions/checkout@v3 diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8..b589d56 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 323152a..1bcb8e7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -7,5 +7,5 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index 8105ed3..1c4de30 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.10-7f52ff.svg)](https://kotlinlang.org)[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.21-7f52ff.svg)](https://kotlinlang.org) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/build.gradle b/build.gradle index 6a131ab..e3d5453 100644 --- a/build.gradle +++ b/build.gradle @@ -112,13 +112,13 @@ tasks.withType(Test).configureEach { } java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlin { jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(11)) + languageVersion.set(JavaLanguageVersion.of(17)) } } diff --git a/version.properties b/version.properties index 27ca594..b4770f6 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat May 20 23:54:50 PDT 2023 -version.buildmeta=1077 +#Sun May 21 00:12:53 PDT 2023 +version.buildmeta=1078 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1077 +version.semver=0.8.0-rc+1078 From 37b4eb4343bcb3f6d97842df499544191c882856 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 00:56:47 -0700 Subject: [PATCH 729/858] Added default max token for ChatGPT --- .circleci/config.yml | 6 +++--- build.gradle | 17 ++++++++++++----- .../net/thauvin/erik/mobibot/modules/ChatGpt.kt | 3 ++- version.properties | 6 +++--- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 67e6618..140179a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -39,11 +39,11 @@ jobs: <<: *defaults_gradle - build_gradle_jdk20: + build_gradle_jdk19: <<: *defaults docker: - - image: cimg/openjdk:20.0 + - image: cimg/openjdk:19.0 <<: *defaults_gradle @@ -52,4 +52,4 @@ workflows: gradle: jobs: - build_gradle_jdk17 - - build_gradle_jdk20 + - build_gradle_jdk19 diff --git a/build.gradle b/build.gradle index e3d5453..31ec925 100644 --- a/build.gradle +++ b/build.gradle @@ -193,19 +193,26 @@ incrementBuildMeta { } } +koverReport { + defaults { + xml { + onCheck = true + } + html { + onCheck = true + } + } +} + sonarqube { properties { property('sonar.organization', 'ethauvin-github') property('sonar.projectKey', 'ethauvin_mobibot') property('sonar.host.url', 'https://sonarcloud.io') - property('sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/kover/xml/report.xml") + property('sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/kover/report.xml") } } -tasks.sonar { - dependsOn 'koverReport' -} - tasks.register('copyToDeploy', Copy) { from('properties', jar) into deployDir diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 3647b31..c1f660e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -55,7 +55,8 @@ class ChatGpt : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val answer = chat(args.trim(), properties[API_KEY_PROP], properties[MAX_TOKENS_PROP]!!.toInt()) + val answer = chat(args.trim(), properties[API_KEY_PROP], + properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()) if (answer.isNotBlank()) { event.sendMessage(WordUtils.wrap(answer, 400)) } else { diff --git a/version.properties b/version.properties index b4770f6..654c604 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun May 21 00:12:53 PDT 2023 -version.buildmeta=1078 +#Sun May 21 00:52:16 PDT 2023 +version.buildmeta=1085 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1078 +version.semver=0.8.0-rc+1085 From e94b1aa97d74a60a8448ab7d5aa5764af9795ccf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 01:08:05 -0700 Subject: [PATCH 730/858] Potential fix for CircleCI --- build.gradle | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build.gradle b/build.gradle index 31ec925..e6ae710 100644 --- a/build.gradle +++ b/build.gradle @@ -158,14 +158,6 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } -tasks.withType(Detekt).configureEach { - jvmTarget = java.targetCompatibility.toString() -} - -tasks.withType(DetektCreateBaselineTask).configureEach { - jvmTarget = java.targetCompatibility.toString() -} - jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) From 1825b13a23851639fe006cfc71c43d555099bb94 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 01:13:17 -0700 Subject: [PATCH 731/858] Disabled JDK 19 from CircleCI --- .circleci/config.yml | 1 - build.gradle | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 140179a..1868b37 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,4 +52,3 @@ workflows: gradle: jobs: - build_gradle_jdk17 - - build_gradle_jdk19 diff --git a/build.gradle b/build.gradle index e6ae710..31ec925 100644 --- a/build.gradle +++ b/build.gradle @@ -158,6 +158,14 @@ detekt { baseline = file("${projectDir}/config/detekt/baseline.xml") } +tasks.withType(Detekt).configureEach { + jvmTarget = java.targetCompatibility.toString() +} + +tasks.withType(DetektCreateBaselineTask).configureEach { + jvmTarget = java.targetCompatibility.toString() +} + jar { manifest.attributes('Main-Class': mainClassName, 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) From 945c763768dc1251e1d1d0ba7944e6e88264b706 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 21 May 2023 02:03:31 -0700 Subject: [PATCH 732/858] Moved to JDK 17 for GitLab and BitBucket --- .gitlab-ci.yml | 2 +- bitbucket-pipelines.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 490f7b8..48a3396 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: gradle:7-jdk18 +image: gradle:8-jdk17 variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index a9514a0..623b3c3 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,4 +1,4 @@ -image: maven:3-openjdk-18 +image: maven:3-eclipse-temurin-17 pipelines: default: From 66b8adb743e361eb5e848b63aaded63fab30211e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 23 May 2023 08:31:51 -0700 Subject: [PATCH 733/858] Upgraded to PircBotX 2.3.1 --- build.gradle | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 31ec925..d35a393 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ dependencies { compileOnly(semverProcessor) // PircBotX - implementation 'com.github.pircbotx:pircbotx:master-SNAPSHOT' + implementation 'com.github.pircbotx:pircbotx:2.3.1' // implementation fileTree(dir: 'lib', include: '*.jar') // Commons (mostly for PircBotX) diff --git a/version.properties b/version.properties index 654c604..4ce7fe9 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun May 21 00:52:16 PDT 2023 -version.buildmeta=1085 +#Tue May 23 08:26:47 PDT 2023 +version.buildmeta=1087 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1085 +version.semver=0.8.0-rc+1087 From d8291e21562c6010937adab5991dff219bd6a299 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 17 Jun 2023 21:30:55 -0700 Subject: [PATCH 734/858] Added all keyword to seen command --- .idea/kotlinc.xml | 2 +- build.gradle | 16 ++++++------ config/detekt/baseline.xml | 2 ++ .../erik/mobibot/commands/seen/Seen.kt | 26 ++++++++++++++++++- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- version.properties | 6 ++--- 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 217e5c5..9a55c2d 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/build.gradle b/build.gradle index d35a393..ff50536 100644 --- a/build.gradle +++ b/build.gradle @@ -3,15 +3,15 @@ import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.46.0' + id 'com.github.ben-manes.versions' version '0.47.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.22.0' + id 'io.gitlab.arturbosch.detekt' version '1.23.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.8.21' - id 'org.jetbrains.kotlin.kapt' version '1.8.21' - id 'org.jetbrains.kotlinx.kover' version '0.7.0' - id 'org.sonarqube' version '4.0.0.2929' + id 'org.jetbrains.kotlin.jvm' version '1.8.22' + id 'org.jetbrains.kotlin.kapt' version '1.8.22' + id 'org.jetbrains.kotlinx.kover' version '0.7.1' + id 'org.sonarqube' version '4.2.1.3168' id 'pmd' } @@ -59,11 +59,11 @@ dependencies { // Google implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:31.1-jre' + implementation 'com.google.guava:guava:32.0.1-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + implementation 'org.jetbrains.kotlin:kotlin-stdlib' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index db7e772..356067c 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -27,6 +27,7 @@ MagicNumber:Mastodon.kt$Mastodon.Companion$200 MagicNumber:Mobibot.kt$Mobibot$8 MagicNumber:Modules.kt$Modules$7 + MagicNumber:Seen.kt$Seen$8 MagicNumber:SocialManager.kt$SocialManager$1000L MagicNumber:SocialManager.kt$SocialManager$60L MagicNumber:StockQuote.kt$StockQuote.Companion$10 @@ -69,6 +70,7 @@ ReturnCount:Addons.kt$Addons$fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException + ReturnCount:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 4a45598..fca143a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -32,10 +32,14 @@ package net.thauvin.erik.mobibot.commands.seen import com.google.common.collect.ImmutableSortedSet +import net.thauvin.erik.mobibot.Utils +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.loadSerialData import net.thauvin.erik.mobibot.Utils.saveSerialData +import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime @@ -43,15 +47,19 @@ import org.pircbotx.User import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.util.TreeMap +import java.util.* class Seen(private val serialObject: String) : AbstractCommand() { private val logger: Logger = LoggerFactory.getLogger(Seen::class.java) + private val allKeyword = "all" val seenNicks = TreeMap(NickComparator()) override val name = "seen" override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name ")) + private val helpOp = help.plus( + arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) + ) override val isOpOnly = false override val isPublic = true override val isVisible = true @@ -61,6 +69,11 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { if (args.isNotBlank() && !args.contains(' ')) { val ch = event.bot().userChannelDao.getChannel(channel) + if (args.equals(allKeyword) && ch.isOp(event.user) && seenNicks.isNotEmpty()) { + event.sendMessage("The ${"seen".bold()} nicks are:") + event.sendList(seenNicks.keys.toList(), 8, separator = ", ", isIndent = true) + return + } ch.users.forEach { if (args.equals(it.nick, true)) { event.sendMessage("${it.nick} is on ${channel}.") @@ -102,6 +115,17 @@ class Seen(private val serialObject: String) : AbstractCommand() { fun count(): Int = seenNicks.size + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + return if (event.isChannelOp(channel)) { + for (h in helpOp) { + event.sendMessage(Utils.helpCmdSyntax(h, event.bot().nick, true)) + } + true + } else { + super.helpResponse(channel, topic, event) + } + } + fun load() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index bc64191..8de54e4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -46,6 +46,6 @@ data class EntryComment(var comment: String, var nick: String) : Serializable { companion object { // Serial version UID - const val serialVersionUID = 1L + private const val serialVersionUID: Long = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 7bf003d..fc61d18 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -208,6 +208,6 @@ class EntryLink( companion object { // Serial version UID - const val serialVersionUID = 1L + private const val serialVersionUID: Long = 1L } } diff --git a/version.properties b/version.properties index 4ce7fe9..9cd983c 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Tue May 23 08:26:47 PDT 2023 -version.buildmeta=1087 +#Sat Jun 17 21:28:53 PDT 2023 +version.buildmeta=1098 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1087 +version.semver=0.8.0-rc+1098 From 5834a23b7e9a7856c3c7c113a109fcc4357c0e22 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 2 Jul 2023 02:23:09 -0700 Subject: [PATCH 735/858] Updated dependencies --- .idea/misc.xml | 5 +++++ README.md | 2 +- build.gradle | 14 ++++++++------ config/detekt/baseline.xml | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 62076 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 5 ++++- .../erik/mobibot/commands/seen/Seen.kt | 4 ++-- version.properties | 6 +++--- 9 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 1bcb8e7..f648a66 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,6 +5,11 @@ + diff --git a/README.md b/README.md index 1c4de30..6739889 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.21-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.22-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/build.gradle b/build.gradle index ff50536..a3677ef 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask @@ -10,7 +12,7 @@ plugins { id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.8.22' id 'org.jetbrains.kotlin.kapt' version '1.8.22' - id 'org.jetbrains.kotlinx.kover' version '0.7.1' + id 'org.jetbrains.kotlinx.kover' version '0.7.2' id 'org.sonarqube' version '4.2.1.3168' id 'pmd' } @@ -54,17 +56,17 @@ dependencies { // Commons (mostly for PircBotX) implementation 'org.apache.commons:commons-lang3:3.12.0' implementation 'org.apache.commons:commons-text:1.10.0' - implementation 'commons-codec:commons-codec:1.15' + implementation 'commons-codec:commons-codec:1.16.0' implementation 'commons-net:commons-net:3.9.0' // Google implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.0.1-jre' + implementation 'com.google.guava:guava:32.1.1-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' // Logging @@ -77,7 +79,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.11.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20230227' + implementation 'org.json:json:20230618' implementation 'org.jsoup:jsoup:1.16.1' // Thauvin @@ -188,7 +190,7 @@ incrementBuildMeta { if (isCI) { println 'No increment with CI.' } else { - buildMeta = sprintf("%03d", (buildMeta as Integer) + 1) + buildMeta = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) } } } diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 356067c..0aa592e 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -27,7 +27,7 @@ MagicNumber:Mastodon.kt$Mastodon.Companion$200 MagicNumber:Mobibot.kt$Mobibot$8 MagicNumber:Modules.kt$Modules$7 - MagicNumber:Seen.kt$Seen$8 + MagicNumber:Seen.kt$Seen$7 MagicNumber:SocialManager.kt$SocialManager$1000L MagicNumber:SocialManager.kt$SocialManager$60L MagicNumber:StockQuote.kt$StockQuote.Companion$10 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a79e29d3e0ab67b14947c167a862655af9b..033e24c4cdf41af1ab109bc7f253b2b887023340 100644 GIT binary patch delta 16170 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 delta 14892 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37aef8d..62f495d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cb..fcb6fca 100755 --- a/gradlew +++ b/gradlew @@ -130,10 +130,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index fca143a..c9ee0f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -69,9 +69,9 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { if (args.isNotBlank() && !args.contains(' ')) { val ch = event.bot().userChannelDao.getChannel(channel) - if (args.equals(allKeyword) && ch.isOp(event.user) && seenNicks.isNotEmpty()) { + if (args == allKeyword && ch.isOp(event.user) && seenNicks.isNotEmpty()) { event.sendMessage("The ${"seen".bold()} nicks are:") - event.sendList(seenNicks.keys.toList(), 8, separator = ", ", isIndent = true) + event.sendList(seenNicks.keys.toList(), 7, separator = ", ", isIndent = true) return } ch.users.forEach { diff --git a/version.properties b/version.properties index 9cd983c..b464c4e 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sat Jun 17 21:28:53 PDT 2023 -version.buildmeta=1098 +#Sun Jul 02 02:19:45 PDT 2023 +version.buildmeta=20230702021945 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+1098 +version.semver=0.8.0-rc+20230702021945 From 2313d36584fdb363b8eeb93331cf05a60e63f12a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 6 Jul 2023 10:23:04 -0700 Subject: [PATCH 736/858] Upgraded to Koltin 1.9.0 --- .idea/codeStyles/codeStyleConfig.xml | 3 +- .idea/kotlinc.xml | 2 +- README.md | 2 +- build.gradle | 4 +- config/detekt/baseline.xml | 26 ++++ .../net/thauvin/erik/mobibot/modules/War.java | 12 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 115 ++++++------------ .../net/thauvin/erik/mobibot/Pinboard.kt | 4 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 35 +++--- .../thauvin/erik/mobibot/commands/Ignore.kt | 14 +-- .../net/thauvin/erik/mobibot/commands/Info.kt | 10 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 4 +- .../thauvin/erik/mobibot/commands/Recap.kt | 8 +- .../thauvin/erik/mobibot/commands/Versions.kt | 8 +- .../erik/mobibot/commands/links/Comment.kt | 46 +++---- .../mobibot/commands/links/LinksManager.kt | 6 +- .../erik/mobibot/commands/links/Posting.kt | 20 +-- .../erik/mobibot/commands/links/Tags.kt | 4 +- .../erik/mobibot/commands/links/View.kt | 10 +- .../erik/mobibot/commands/seen/Seen.kt | 14 +-- .../erik/mobibot/commands/tell/Tell.kt | 44 +++---- .../erik/mobibot/commands/tell/TellMessage.kt | 24 ++-- .../thauvin/erik/mobibot/entries/Entries.kt | 8 +- .../erik/mobibot/entries/EntriesUtils.kt | 6 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 59 +++++---- .../erik/mobibot/entries/FeedsManager.kt | 48 ++++---- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 40 +++--- .../erik/mobibot/modules/CryptoPrices.kt | 6 +- .../erik/mobibot/modules/CurrencyConverter.kt | 22 ++-- .../erik/mobibot/modules/GoogleSearch.kt | 30 ++--- .../thauvin/erik/mobibot/modules/Lookup.kt | 6 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 40 +++--- .../erik/mobibot/modules/ModuleException.kt | 6 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 24 ++-- .../erik/mobibot/modules/RockPaperScissors.kt | 10 +- .../erik/mobibot/modules/StockQuote.kt | 66 +++++----- .../thauvin/erik/mobibot/modules/Weather2.kt | 36 +++--- .../erik/mobibot/modules/WolframAlpha.kt | 28 ++--- .../thauvin/erik/mobibot/modules/WorldTime.kt | 4 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 10 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 3 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 4 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 18 ++- .../erik/mobibot/ExceptionSanitizer.kt | 6 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 35 +++--- .../thauvin/erik/mobibot/commands/InfoTest.kt | 8 +- .../erik/mobibot/commands/RecapTest.kt | 6 +- .../commands/links/LinksManagerTest.kt | 4 +- .../erik/mobibot/commands/links/ViewTest.kt | 16 +-- .../erik/mobibot/commands/seen/SeenTest.kt | 9 +- .../commands/tell/TellMessagesMgrTest.kt | 8 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 20 +-- .../erik/mobibot/entries/EntryLinkTest.kt | 20 ++- .../erik/mobibot/entries/FeedMgrTest.kt | 10 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 1 - .../erik/mobibot/modules/ChatGptTest.kt | 11 +- .../mobibot/modules/CurrencyConverterTest.kt | 12 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 8 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 22 ++-- .../thauvin/erik/mobibot/modules/JokeTest.kt | 7 +- .../erik/mobibot/modules/MastodonTest.kt | 14 +-- .../mobibot/modules/ModuleExceptionTest.kt | 22 ++-- .../erik/mobibot/modules/StockQuoteTest.kt | 11 +- .../erik/mobibot/modules/Weather2Test.kt | 13 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 10 +- .../erik/mobibot/modules/WordTimeTest.kt | 8 +- version.properties | 6 +- 76 files changed, 549 insertions(+), 645 deletions(-) diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 6e6eec1..d91f848 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,6 +1,5 @@ - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 9a55c2d..fdf8d99 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/README.md b/README.md index 6739889..df026ab 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.8.22-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.0-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/build.gradle b/build.gradle index a3677ef..fa00b54 100644 --- a/build.gradle +++ b/build.gradle @@ -10,8 +10,8 @@ plugins { id 'io.gitlab.arturbosch.detekt' version '1.23.0' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.8.22' - id 'org.jetbrains.kotlin.kapt' version '1.8.22' + id 'org.jetbrains.kotlin.jvm' version '1.9.0' + id 'org.jetbrains.kotlin.kapt' version '1.9.0' id 'org.jetbrains.kotlinx.kover' version '0.7.2' id 'org.sonarqube' version '4.2.1.3168' id 'pmd' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 0aa592e..f7cc151 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -46,6 +46,15 @@ MagicNumber:WorldTime.kt$WorldTime.Companion$3600 MagicNumber:WorldTime.kt$WorldTime.Companion$60 MagicNumber:WorldTime.kt$WorldTime.Companion$86.4 + MaxLineLength:DiceTest.kt$DiceTest$. + MaxLineLength:Lookup.kt$Lookup$("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + MaxLineLength:Mastodon.kt$Mastodon.Companion$mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") + MaxLineLength:Mobibot.kt$Mobibot$helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) + MaxLineLength:PinboardTest.kt$PinboardTest$URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body + MaxLineLength:StockQuote.kt$StockQuote.Companion$+ + MaxLineLength:Utils.kt$Utils$list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = "") + MaxLineLength:View.kt$View$helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) + MaxLineLength:Weather2.kt$Weather2.Companion$country.name.replace('_', ' ').capitalizeWords() NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String @@ -87,6 +96,23 @@ TooManyFunctions:EntryLink.kt$EntryLink : Serializable TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter TooManyFunctions:Tell.kt$Tell : AbstractCommand + WildcardImport:AddonsTest.kt$import net.thauvin.erik.mobibot.modules.* + WildcardImport:EntryLinkTest.kt$import assertk.assertions.* + WildcardImport:FeedMgrTest.kt$import assertk.assertions.* WildcardImport:FeedReaderTest.kt$import assertk.assertions.* + WildcardImport:FeedsManager.kt$import com.rometools.rome.feed.synd.* + WildcardImport:GoogleSearchTest.kt$import assertk.assertions.* + WildcardImport:JokeTest.kt$import assertk.assertions.* + WildcardImport:Mobibot.kt$import java.io.* + WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.* + WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.commands.links.* + WildcardImport:Mobibot.kt$import net.thauvin.erik.mobibot.modules.* + WildcardImport:Mobibot.kt$import org.pircbotx.hooks.events.* + WildcardImport:ModuleExceptionTest.kt$import assertk.assertions.* + WildcardImport:SeenTest.kt$import assertk.assertions.* + WildcardImport:StockQuoteTest.kt$import assertk.assertions.* + WildcardImport:TellMessagesMgrTest.kt$import assertk.assertions.* + WildcardImport:Utils.kt$import java.io.* + WildcardImport:Weather2Test.kt$import assertk.assertions.* diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java index d1d7882..4bbbd9b 100644 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ b/src/main/java/net/thauvin/erik/mobibot/modules/War.java @@ -72,6 +72,12 @@ public final class War extends AbstractModule { help.add(Utils.helpFormat("%c " + WAR_CMD)); } + @NotNull + @Override + public String getName() { + return "War"; + } + /** * {@inheritDoc} */ @@ -99,10 +105,4 @@ public final class War extends AbstractModule { } while (i == y); } - - @NotNull - @Override - public String getName() { - return "War"; - } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 1127f02..2c5f05d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -38,7 +38,7 @@ import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.util.Properties +import java.util.* /** * Modules and Commands addons. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 98ef74a..7cf6719 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -63,7 +63,7 @@ object Constants { * User-Agent */ const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index dabb7c8..3342077 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -45,67 +45,24 @@ import net.thauvin.erik.mobibot.Utils.lastOrEmpty 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.commands.ChannelFeed -import net.thauvin.erik.mobibot.commands.Cycle -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.Me -import net.thauvin.erik.mobibot.commands.Modules -import net.thauvin.erik.mobibot.commands.Msg -import net.thauvin.erik.mobibot.commands.Nick -import net.thauvin.erik.mobibot.commands.Recap +import net.thauvin.erik.mobibot.commands.* import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap -import net.thauvin.erik.mobibot.commands.Say -import net.thauvin.erik.mobibot.commands.Users -import net.thauvin.erik.mobibot.commands.Versions -import net.thauvin.erik.mobibot.commands.links.Comment -import net.thauvin.erik.mobibot.commands.links.LinksManager -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.links.* import net.thauvin.erik.mobibot.commands.seen.Seen import net.thauvin.erik.mobibot.commands.tell.Tell -import net.thauvin.erik.mobibot.modules.Calc -import net.thauvin.erik.mobibot.modules.ChatGpt -import net.thauvin.erik.mobibot.modules.CryptoPrices -import net.thauvin.erik.mobibot.modules.CurrencyConverter -import net.thauvin.erik.mobibot.modules.Dice -import net.thauvin.erik.mobibot.modules.GoogleSearch -import net.thauvin.erik.mobibot.modules.Joke -import net.thauvin.erik.mobibot.modules.Lookup -import net.thauvin.erik.mobibot.modules.Mastodon -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.War -import net.thauvin.erik.mobibot.modules.Weather2 -import net.thauvin.erik.mobibot.modules.WolframAlpha -import net.thauvin.erik.mobibot.modules.WorldTime +import net.thauvin.erik.mobibot.modules.* import net.thauvin.erik.semver.Version 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.events.QuitEvent +import org.pircbotx.hooks.events.* 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 -import java.io.FileOutputStream -import java.io.IOException -import java.io.PrintStream +import java.io.* import java.nio.file.Files import java.nio.file.Paths -import java.util.Properties +import java.util.* import java.util.regex.Pattern import kotlin.system.exitProcess @@ -140,9 +97,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro event.sendMessage("Type a URL on $channel to post it.") event.sendMessage("For more information on a specific command, type:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c ${Constants.HELP_CMD} ", event.bot().nick, event is PrivateMessageEvent) - ) + helpFormat( + helpCmdSyntax("%c ${Constants.HELP_CMD} ", event.bot().nick, event is PrivateMessageEvent) + ) ) event.sendMessage("The commands are:") event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) @@ -204,7 +161,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has joined ${event.channel.name} on $serverHostname" + "$nick has joined ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -252,7 +209,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has left ${event.channel.name} on $serverHostname" + "$nick has left ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -275,22 +232,22 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro // Set up the command line options val parser = ArgParser(Constants.CLI_CMD) val debug by parser.option( - ArgType.Boolean, - Constants.DEBUG_ARG, - Constants.DEBUG_ARG.substring(0, 1), - "Print debug & logging data directly to the console" + ArgType.Boolean, + Constants.DEBUG_ARG, + Constants.DEBUG_ARG.substring(0, 1), + "Print debug & logging data directly to the console" ).default(false) val property by parser.option( - ArgType.String, - Constants.PROPS_ARG, - Constants.PROPS_ARG.substring(0, 1), - "Use alternate properties file" + ArgType.String, + Constants.PROPS_ARG, + Constants.PROPS_ARG.substring(0, 1), + "Use alternate properties file" ).default("./${ReleaseInfo.PROJECT}.properties") val version by parser.option( - ArgType.Boolean, - Constants.VERSION_ARG, - Constants.VERSION_ARG.substring(0, 1), - "Print version info" + ArgType.Boolean, + Constants.VERSION_ARG, + Constants.VERSION_ARG.substring(0, 1), + "Print version info" ).default(false) // Parse the command line @@ -299,8 +256,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro if (version) { // Output the version println( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + + " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" ) println(ReleaseInfo.WEBSITE) } else { @@ -308,7 +265,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro val p = Properties() try { Files.newInputStream( - Paths.get(property) + Paths.get(property) ).use { fis -> p.load(fis) } @@ -327,11 +284,11 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro if (!debug) { try { val stdout = PrintStream( - BufferedOutputStream( - FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true - ) - ), true + BufferedOutputStream( + FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true + ) + ), true ) System.setOut(stdout) } catch (ignore: IOException) { @@ -340,9 +297,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro } try { val stderr = PrintStream( - BufferedOutputStream( - FileOutputStream("$logsDir$nickname.err", true) - ), true + BufferedOutputStream( + FileOutputStream("$logsDir$nickname.err", true) + ), true ) System.setErr(stderr) } catch (ignore: IOException) { @@ -367,8 +324,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro login = p.getProperty("login", nickname) realName = p.getProperty("realname", nickname) addServer( - ircServer, - p.getIntProperty("port", Constants.DEFAULT_PORT) + ircServer, + p.getIntProperty("port", Constants.DEFAULT_PORT) ) addAutoJoinChannel(channel) addListener(this@Mobibot) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index b829bab..1a4260d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -37,7 +37,7 @@ import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit -import java.util.Date +import java.util.* /** * Handles posts to pinboard.in. @@ -92,7 +92,7 @@ class Pinboard { */ private fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( - toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() + toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() ).format(DateTimeFormatter.ISO_INSTANT) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index f61c56c..0595220 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -39,11 +39,7 @@ import org.pircbotx.PircBotX import org.pircbotx.hooks.events.PrivateMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger -import java.io.BufferedInputStream -import java.io.BufferedOutputStream -import java.io.IOException -import java.io.ObjectInputStream -import java.io.ObjectOutputStream +import java.io.* import java.net.HttpURLConnection import java.net.URL import java.nio.file.Files @@ -51,8 +47,7 @@ import java.nio.file.Paths import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter -import java.util.Date -import java.util.Properties +import java.util.* import kotlin.io.path.exists import kotlin.io.path.fileSize @@ -220,7 +215,7 @@ object Utils { if (serialFile.exists() && serialFile.fileSize() > 0) { try { ObjectInputStream( - BufferedInputStream(Files.newInputStream(serialFile)) + BufferedInputStream(Files.newInputStream(serialFile)) ).use { input -> if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") return input.readObject() @@ -307,20 +302,20 @@ object Utils { @JvmStatic @JvmOverloads fun GenericMessageEvent.sendList( - list: List, - maxPerLine: Int, - separator: String = " ", - isBold: Boolean = false, - isIndent: Boolean = false + list: List, + 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 - ), + helpFormat( + list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), + isBold, + isIndent + ), ) i += maxPerLine } @@ -419,8 +414,8 @@ object Utils { fun URL.reader(): UrlReaderResponse { val connection = this.openConnection() as HttpURLConnection connection.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + "User-Agent", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" ) return if (connection.responseCode.isHttpSuccess()) { UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index a696fa8..88109e8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -50,15 +50,15 @@ class Ignore : AbstractCommand() { override val name = IGNORE_CMD override val help = listOf( - "To ignore a link posted to the channel:", - helpFormat("https://www.foo.bar %n"), - "To check your ignore status:", - helpFormat("%c $name"), - "To toggle your ignore status:", - helpFormat("%c $name $me") + "To ignore a link posted to the channel:", + helpFormat("https://www.foo.bar %n"), + "To check your ignore status:", + helpFormat("%c $name"), + "To toggle your ignore status:", + helpFormat("%c $name $me") ) private val helpOp = help.plus( - arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name [ ...]")) + arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name [ ...]")) ) override val isOpOnly = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index ed0b6ef..7eb3bdb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -48,8 +48,8 @@ import kotlin.time.toDuration class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { private val allVersions = listOf( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", - "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", + "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" ) override val name = "info" override val help = listOf("To view information about the bot:", helpFormat("%c $name")) @@ -104,9 +104,9 @@ class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { event.sendList(allVersions, 1) val info = StringBuilder() info.append("Uptime: ") - .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) - .append(" [Entries: ") - .append(LinksManager.entries.links.size) + .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) + .append(" [Entries: ") + .append(LinksManager.entries.links.size) if (seen.isEnabled()) { info.append(", Seen: ").append(seen.count()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 20a6635..48ff38f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -39,8 +39,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Msg : AbstractCommand() { override val name = "msg" override val help = listOf( - "To have the bot send a private message to someone:", - helpFormat("%c $name ") + "To have the bot send a private message to someone:", + helpFormat("%c $name ") ) override val isOpOnly = true override val isPublic = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 77154c7..66e721e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -41,8 +41,8 @@ import java.time.LocalDateTime class Recap : AbstractCommand() { override val name = "recap" override val help = listOf( - "To list the last 10 public channel messages:", - helpFormat("%c $name") + "To list the last 10 public channel messages:", + helpFormat("%c $name") ) override val isOpOnly = false override val isPublic = true @@ -60,8 +60,8 @@ class Recap : AbstractCommand() { @JvmStatic fun storeRecap(sender: String, message: String, isAction: Boolean) { recaps.add( - LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() - + " - $sender" + (if (isAction) " " else ": ") + message + LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + + " - $sender" + (if (isAction) " " else ": ") + message ) if (recaps.size > MAX_RECAPS) { recaps.removeFirst() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 896c569..f920891 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -40,10 +40,10 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Versions : AbstractCommand() { private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", - "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + - ", JVM ${System.getProperty("java.runtime.version")}", - "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + + ", JVM ${System.getProperty("java.runtime.version")}", + "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" ) override val name = "versions" override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 1443d44..9fe250d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -45,13 +45,13 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Comment : AbstractCommand() { override val name = COMMAND override val help = listOf( - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, use its label: ", - helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), - "To delete a comment, use its label and a minus sign: ", - helpFormat("${Constants.LINK_CMD}1.1:-") + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, use its label: ", + helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), + "To delete a comment, use its label and a minus sign: ", + helpFormat("${Constants.LINK_CMD}1.1:-") ) override val isOpOnly = false override val isPublic = true @@ -100,12 +100,12 @@ class Comment : AbstractCommand() { } private fun changeAuthor( - channel: String, - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + channel: String, + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { if (event.isChannelOp(channel) && cmd.length > 1) { val comment = entry.getComment(commentIndex) @@ -118,11 +118,11 @@ class Comment : AbstractCommand() { } private fun deleteComment( - channel: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + channel: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) @@ -134,11 +134,11 @@ class Comment : AbstractCommand() { } private fun setComment( - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { entry.setComment(commentIndex, cmd, event.user.nick) event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index fba6b99..fb1a634 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -161,8 +161,8 @@ class LinksManager : AbstractCommand() { internal fun fetchTitle(link: String): String { try { val html = Jsoup.connect(link) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") - .get() + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") + .get() val title = html.title() if (title.isNotBlank()) { return title @@ -178,7 +178,7 @@ class LinksManager : AbstractCommand() { return try { val match = entries.links.single { it.link == link } event.sendMessage( - "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) + "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) ) true } catch (ignore: NoSuchElementException) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index ff4278d..e04cd15 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -47,16 +47,16 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Posting : AbstractCommand() { override val name = "posting" override val help = listOf( - "Post a URL, by saying it on a line on its own:", - helpFormat(" [] ${Tags.COMMAND}: <+tag> [...]]"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", - "To add a title, use its label and a pipe:", - helpFormat("${Constants.LINK_CMD}1:|This is the title"), - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, see: ", - helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") + "Post a URL, by saying it on a line on its own:", + helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", + "To add a title, use its label and a pipe:", + helpFormat("${Constants.LINK_CMD}1:|This is the title"), + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, see: ", + helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) override val isOpOnly = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1662857..9071059 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -44,8 +44,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent 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> [...]") + "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 isOpOnly = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 825e374..ea1ebf8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -46,8 +46,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class View : AbstractCommand() { override val name = VIEW_CMD override val help = listOf( - "To list or search the current URL posts:", - helpFormat("%c $name [<start>] [<query>]") + "To list or search the current URL posts:", + helpFormat("%c $name [<start>] [<query>]") ) override val isOpOnly = false override val isPublic = true @@ -107,9 +107,9 @@ class View : AbstractCommand() { if (sent == MAX_ENTRIES && index < entries.links.size) { event.sendMessage("To view more, try: ") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) - ) + helpFormat( + helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index c9ee0f3..05ad330 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -58,7 +58,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { override val name = "seen" override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) private val helpOp = help.plus( - arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) + arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) ) override val isOpOnly = false override val isPublic = true @@ -130,12 +130,12 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") seenNicks.putAll( - loadSerialData( - serialObject, - TreeMap<String, SeenNick>(), - logger, - "seen nicknames" - ) as TreeMap<String, SeenNick> + loadSerialData( + serialObject, + TreeMap<String, SeenNick>(), + logger, + "seen nicknames" + ) as TreeMap<String, SeenNick> ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index e073184..96800bb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -66,11 +66,11 @@ class Tell(private val serialObject: String) : AbstractCommand() { override val name = "tell" override val help = listOf( - "To send a message to someone when they join the channel:", - helpFormat("%c $name <nick> <message>"), - "To view queued and sent messages:", - helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' + "To send a message to someone when they join the channel:", + helpFormat("%c $name <nick> <message>"), + "To view queued and sent messages:", + helpFormat("%c $name ${View.VIEW_CMD}"), + "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' ) override val isOpOnly: Boolean = false override val isPublic: Boolean = isEnabled() @@ -118,9 +118,9 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } else { if (messages.removeIf { - it.id == id && - (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) - }) { + it.id == id && + (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) + }) { save() event.sendMessage("The message was deleted from the queue.") } else { @@ -180,7 +180,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (message.sender == nickname) { if (event !is MessageEvent) { event.user.send().message( - "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" + "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" ) message.isReceived = true message.isNotified = true @@ -188,17 +188,17 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } else { event.user.send().message( - "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" + "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" ) message.isReceived = true save() } } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified + && !message.isNotified ) { event.user.send().message( - "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " - + "${message.recipient.bold()} on ${message.receptionDate}" + "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " + + "${message.recipient.bold()} on ${message.receptionDate}" ) message.isNotified = true save() @@ -219,8 +219,8 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (messages.isNotEmpty()) { for (message in messages) { event.sendMessage( - "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + - (if (message.isReceived) "DELIVERED]" else "QUEUED]") + "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + + (if (message.isReceived) "DELIVERED]" else "QUEUED]") ) } } else { @@ -238,13 +238,13 @@ class Tell(private val serialObject: String) : AbstractCommand() { } if (message.isReceived) { event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" ) } else { event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" ) } event.sendMessage(helpFormat(message.message)) @@ -254,9 +254,9 @@ class Tell(private val serialObject: String) : AbstractCommand() { } else { event.sendMessage("To delete one or all delivered messages:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) - ) + helpFormat( + helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) + ) ) event.sendMessage(help.last()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 6d2f313..33bc1e9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -39,20 +39,20 @@ import java.time.format.DateTimeFormatter * Tell Message. */ class TellMessage( - /** - * Returns the message's sender. - */ - val sender: String, + /** + * Returns the message's sender. + */ + val sender: String, - /** - * Returns the message's recipient. - */ - val recipient: String, + /** + * Returns the message's recipient. + */ + val recipient: String, - /** - * Returns the message text. - */ - val message: String + /** + * Returns the message text. + */ + val message: String ) : Serializable { /** * Returns the queued date/time. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index e8676ec..ba22746 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.entries import net.thauvin.erik.mobibot.Utils.today class Entries( - var channel: String = "", - var ircServer: String = "", - var logsDir: String = "", - var backlogs: String = "" + var channel: String = "", + var ircServer: String = "", + var logsDir: String = "", + var backlogs: String = "" ) { val links = mutableListOf<EntryLink>() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 9c09626..ff1e423 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -43,7 +43,7 @@ object EntriesUtils { */ @JvmStatic fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") + ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") /** * Prints an entry's link for display on the channel. @@ -52,7 +52,7 @@ object EntriesUtils { @JvmOverloads fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") - .append('[').append(entry.nick).append(']') + .append('[').append(entry.nick).append(']') if (isView && entry.comments.isNotEmpty()) { buff.append("[+").append(entry.comments.size).append(']') } @@ -73,7 +73,7 @@ object EntriesUtils { */ @JvmStatic fun printTags(entryIndex: Int, entry: EntryLink): String = - entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") + entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") /** * Builds link label based on its index. e.g: L1 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index fc61d18..80ca536 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -34,47 +34,46 @@ import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl import net.thauvin.erik.mobibot.commands.links.LinksManager import java.io.Serializable -import java.util.Calendar -import java.util.Date +import java.util.* /** * The class used to store link entries. */ class EntryLink( - // Link's comments - val comments: MutableList<EntryComment> = mutableListOf(), + // Link's comments + val comments: MutableList<EntryComment> = mutableListOf(), - // Tags/categories - val tags: MutableList<SyndCategory> = mutableListOf(), + // Tags/categories + val tags: MutableList<SyndCategory> = mutableListOf(), - // Channel - var channel: String, + // Channel + var channel: String, - // Creation date - var date: Date = Calendar.getInstance().time, + // Creation date + var date: Date = Calendar.getInstance().time, - // Link's URL - var link: String, + // Link's URL + var link: String, - // Author's login - var login: String = "", + // Author's login + var login: String = "", - // Author's nickname - var nick: String, + // Author's nickname + var nick: String, - // Link's title - var title: String + // Link's title + var title: String ) : Serializable { /** * Creates a new entry. */ constructor( - link: String, - title: String, - nick: String, - login: String, - channel: String, - tags: List<String?> + link: String, + title: String, + nick: String, + login: String, + channel: String, + tags: List<String?> ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { setTags(tags) } @@ -83,12 +82,12 @@ class EntryLink( * Creates a new entry. */ constructor( - link: String, - title: String, - nick: String, - channel: String, - date: Date, - tags: List<SyndCategory> + link: String, + title: String, + nick: String, + channel: String, + date: Date, + tags: List<SyndCategory> ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { this.tags.addAll(tags) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index bb3838a..a30ba24 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -30,11 +30,7 @@ */ 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.feed.synd.* import com.rometools.rome.io.FeedException import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.SyndFeedOutput @@ -48,7 +44,7 @@ import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths -import java.util.Calendar +import java.util.* import kotlin.io.path.exists /** @@ -76,7 +72,7 @@ class FeedsManager private constructor() { if (xml.exists()) { val input = SyndFeedInput() InputStreamReader( - Files.newInputStream(xml), StandardCharsets.UTF_8 + Files.newInputStream(xml), StandardCharsets.UTF_8 ).use { reader -> val feed = input.build(reader) pubDate = feed.publishedDate.toIsoLocalDate() @@ -85,12 +81,12 @@ class FeedsManager private constructor() { 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 + 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/>")) { @@ -123,7 +119,7 @@ class FeedsManager private constructor() { val items: MutableList<SyndEntry> = mutableListOf() var item: SyndEntry OutputStreamWriter( - Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 + Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 ).use { fw -> with(rss) { feedType = "rss_2.0" @@ -138,13 +134,13 @@ class FeedsManager private constructor() { 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>") + .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) { @@ -169,11 +165,11 @@ class FeedsManager private constructor() { output.output(rss, fw) } OutputStreamWriter( - Files.newOutputStream( - Paths.get( - entries.logsDir + today() + dotXml - ) - ), StandardCharsets.UTF_8 + 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) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index c1f660e..e1e86df 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -55,8 +55,10 @@ class ChatGpt : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (args.isNotBlank()) { try { - val answer = chat(args.trim(), properties[API_KEY_PROP], - properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()) + val answer = chat( + args.trim(), properties[API_KEY_PROP], + properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() + ) if (answer.isNotBlank()) { event.sendMessage(WordUtils.wrap(answer, 400)) } else { @@ -105,13 +107,13 @@ class ChatGpt : AbstractModule() { if (!apiKey.isNullOrEmpty()) { val prompt = JSONWriter.valueToString("Q:$query\nA:") val request = HttpRequest.newBuilder() - .uri(URI.create(API_URL)) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .header("User-Agent", Constants.USER_AGENT) - .POST( - HttpRequest.BodyPublishers.ofString( - """{ + .uri(URI.create(API_URL)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .header("User-Agent", Constants.USER_AGENT) + .POST( + HttpRequest.BodyPublishers.ofString( + """{ "model": "text-davinci-003", "prompt": $prompt, "temperature": 0, @@ -120,9 +122,9 @@ class ChatGpt : AbstractModule() { "frequency_penalty": 0, "presence_penalty": 0 }""".trimIndent() + ) ) - ) - .build() + .build() try { val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) if (response.statusCode() == 200) { @@ -132,16 +134,16 @@ class ChatGpt : AbstractModule() { return choices.getJSONObject(0).getString("text").trim() } catch (e: JSONException) { throw ModuleException( - "$CHATGPT_CMD($query): JSON", - "A JSON error has occurred while conversing with $CHATGPT_NAME.", - e + "$CHATGPT_CMD($query): JSON", + "A JSON error has occurred while conversing with $CHATGPT_NAME.", + e ) } } else { if (response.statusCode() == 429) { throw ModuleException( - "$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later." + "$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later." ) } else { throw IOException("HTTP Status Code: " + response.statusCode()) @@ -149,9 +151,9 @@ class ChatGpt : AbstractModule() { } } catch (e: IOException) { throw ModuleException( - "$CHATGPT_CMD($query): IO", - "An IO error has occurred while conversing with $CHATGPT_NAME.", - e + "$CHATGPT_CMD($query): IO", + "An IO error has occurred while conversing with $CHATGPT_NAME.", + e ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index d14056e..5136504 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -134,9 +134,9 @@ class CryptoPrices : AbstractModule() { } } catch (e: CryptoException) { throw ModuleException( - "loadCurrencies(): CE", - "An error has occurred while retrieving the currencies table.", - e + "loadCurrencies(): CE", + "An error has occurred while retrieving the currencies table.", + e ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index d41e7a1..0bf9d7a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -45,7 +45,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException import java.net.URL -import java.util.TreeMap +import java.util.* /** @@ -99,15 +99,15 @@ class CurrencyConverter : AbstractModule() { event.sendMessage("To convert from one currency to another:") event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) - ) + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) + ) ) event.sendMessage("To list the supported currency codes:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) - ) + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) + ) ) } return true @@ -146,7 +146,7 @@ class CurrencyConverter : AbstractModule() { if (json.getBoolean("success")) { PublicMessage( - "${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}" + "${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}" ) } else { ErrorMessage("Sorry, an error occurred while converting the currencies.") @@ -178,9 +178,9 @@ class CurrencyConverter : AbstractModule() { } } catch (e: IOException) { throw ModuleException( - "loadCodes(): IOE", - "An IO error has occurred while retrieving the currencies.", - e + "loadCodes(): IOE", + "An IO error has occurred while retrieving the currencies.", + e ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index f426d1e..b0e911c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -65,10 +65,10 @@ class GoogleSearch : AbstractModule() { if (args.isNotBlank()) { try { val results = searchGoogle( - args, - properties[API_KEY_PROP], - properties[CSE_KEY_PROP], - event.user.nick + args, + properties[API_KEY_PROP], + properties[CSE_KEY_PROP], + event.user.nick ) for (msg in results) { if (msg.isError) { @@ -104,23 +104,23 @@ class GoogleSearch : AbstractModule() { @JvmStatic @Throws(ModuleException::class) fun searchGoogle( - query: String, - apiKey: String?, - cseKey: String?, - quotaUser: String = ReleaseInfo.PROJECT + query: String, + apiKey: String?, + cseKey: String?, + quotaUser: String = ReleaseInfo.PROJECT ): List<Message> { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { throw ModuleException( - "${GoogleSearch::class.java.name} is disabled.", - "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." + "${GoogleSearch::class.java.name} is disabled.", + "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." ) } val results = mutableListOf<Message>() if (query.isNotBlank()) { try { val url = URL( - "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" + "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + + ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" ) val json = JSONObject(url.reader().body) if (json.has("items")) { @@ -141,9 +141,9 @@ class GoogleSearch : AbstractModule() { throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) } catch (e: JSONException) { throw ModuleException( - "searchGoogle($query): JSON", - "A JSON error has occurred searching Google.", - e + "searchGoogle($query): JSON", + "A JSON error has occurred searching Google.", + e ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 9ab2ead..fc85226 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -55,9 +55,9 @@ class Lookup : AbstractModule() { event.respondWith(nslookup(args).prependIndent()) } catch (ignore: UnknownHostException) { if (args.matches( - ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") - .toRegex() - ) + ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + .toRegex() + ) ) { try { val lines = whois(args) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 3be3a5f..4cf2fe9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -65,7 +65,7 @@ class Mastodon : SocialModule() { private fun formatTags(entry: EntryLink): String { return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } - .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } + .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } } /** @@ -74,11 +74,11 @@ class Mastodon : SocialModule() { @Throws(ModuleException::class) override fun post(message: String, isDm: Boolean): String { return toot( - apiKey = properties[ACCESS_TOKEN_PROP], - instance = properties[INSTANCE_PROP], - handle = handle, - message = message, - isDm = isDm + apiKey = properties[ACCESS_TOKEN_PROP], + instance = properties[INSTANCE_PROP], + handle = handle, + message = message, + isDm = isDm ) } @@ -99,21 +99,21 @@ class Mastodon : SocialModule() { @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { val request = HttpRequest.newBuilder() - .uri(URI.create("https://$instance/api/v1/statuses")) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .POST( - HttpRequest.BodyPublishers.ofString( - JSONWriter.valueToString( - if (isDm) { - mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") - } else { - mapOf("status" to message) - } - ) + .uri(URI.create("https://$instance/api/v1/statuses")) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .POST( + HttpRequest.BodyPublishers.ofString( + JSONWriter.valueToString( + if (isDm) { + mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") + } else { + mapOf("status" to message) + } + ) + ) ) - ) - .build() + .build() try { val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) if (response.statusCode() == 200) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index a569d21..017efd4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -34,9 +34,9 @@ package net.thauvin.erik.mobibot.modules * The `ModuleException` class. */ class ModuleException @JvmOverloads constructor( - val debugMessage: String, - message: String? = null, - cause: Throwable? = null + val debugMessage: String, + message: String? = null, + cause: Throwable? = null ) : Exception(message, cause) { companion object { private const val serialVersionUID = 1L diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 944dbc1..de5c1e8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -50,18 +50,18 @@ class Ping : AbstractModule() { */ @JvmField val PINGS = listOf( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!" + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!" ) @JvmStatic diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index d698888..359956a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -52,10 +52,10 @@ class RockPaperScissors : AbstractModule() { with(help) { add("To play Rock Paper Scissors:") add( - helpFormat( - "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" - + " | ${Hands.SCISSORS.name.lowercase()}" - ) + helpFormat( + "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" + + " | ${Hands.SCISSORS.name.lowercase()}" + ) ) } } @@ -96,7 +96,7 @@ class RockPaperScissors : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { val hand = Hands.valueOf(cmd.uppercase()) - val botHand = Hands.values()[(0..Hands.values().size).random()] + val botHand = Hands.entries[(0..Hands.entries.size).random()] when { hand == botHand -> { event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.") diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index dcae5e7..661a4e8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -132,8 +132,8 @@ class StockQuote : AbstractModule() { fun getQuote(symbol: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${StockQuote::class.java.name} is disabled.", - "${STOCK_CMD.capitalise()} is disabled. The API key is missing." + "${StockQuote::class.java.name} is disabled.", + "${STOCK_CMD.capitalise()} is disabled. The API key is missing." ) } val messages = mutableListOf<Message>() @@ -144,8 +144,8 @@ class StockQuote : AbstractModule() { with(messages) { // Search for symbol/keywords response = URL( - "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" - + apiKey.encodeUrl() + "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + + apiKey.encodeUrl() ).reader().body var json = getJsonResponse(response, debugMessage) val symbols = json.getJSONArray("bestMatches") @@ -156,9 +156,9 @@ class StockQuote : AbstractModule() { // Get quote for symbol response = URL( - "${API_URL}GLOBAL_QUOTE&symbol=" - + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" - + apiKey.encodeUrl() + "${API_URL}GLOBAL_QUOTE&symbol=" + + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + + apiKey.encodeUrl() ).reader().body json = getJsonResponse(response, debugMessage) val quote = json.getJSONObject("Global Quote") @@ -167,50 +167,50 @@ class StockQuote : AbstractModule() { } else { add( - PublicMessage( - "Symbol: " + quote.getString("01. symbol").unescapeXml() - + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' - ) + PublicMessage( + "Symbol: " + quote.getString("01. symbol").unescapeXml() + + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' + ) ) val pad = 10 add( - PublicMessage( - "Price:".padEnd(pad).prependIndent() - + quote.getString("05. price").unescapeXml() - ) + PublicMessage( + "Price:".padEnd(pad).prependIndent() + + quote.getString("05. price").unescapeXml() + ) ) add( - PublicMessage( - "Previous:".padEnd(pad).prependIndent() - + quote.getString("08. previous close").unescapeXml() - ) + PublicMessage( + "Previous:".padEnd(pad).prependIndent() + + quote.getString("08. previous close").unescapeXml() + ) ) 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" + "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() - + quote.getString(it.second).unescapeXml() - ) + NoticeMessage( + "${it.first}:".padEnd(pad).prependIndent() + + quote.getString(it.second).unescapeXml() + ) ) } add( - NoticeMessage( - "Change:".padEnd(pad).prependIndent() - + quote.getString("09. change").unescapeXml() - + " [" + quote.getString("10. change percent").unescapeXml() + ']' - ) + NoticeMessage( + "Change:".padEnd(pad).prependIndent() + + quote.getString("09. change").unescapeXml() + + " [" + quote.getString("10. change percent").unescapeXml() + ']' + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 567728e..533cce6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -104,7 +104,7 @@ class Weather2 : AbstractModule() { * Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found. */ fun getCountry(countryCode: String): Country { - for (c in Country.values()) { + for (c in Country.entries) { if (c.value.equals(countryCode, ignoreCase = true)) { return c } @@ -120,8 +120,8 @@ class Weather2 : AbstractModule() { fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${Weather2::class.java.name} is disabled.", - "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." + "${Weather2::class.java.name} is disabled.", + "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." ) } val owm = OWM(apiKey) @@ -145,10 +145,10 @@ class Weather2 : AbstractModule() { } if (cwd.hasCityName()) { messages.add( - PublicMessage( - "City: ${cwd.cityName}, " + - country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" - ) + PublicMessage( + "City: ${cwd.cityName}, " + + country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" + ) ) cwd.mainData?.let { with(it) { @@ -181,8 +181,8 @@ class Weather2 : AbstractModule() { for (w in it) { w?.let { condition.append(' ') - .append(w.getDescription().capitalise()) - .append('.') + .append(w.getDescription().capitalise()) + .append('.') } } messages.add(NoticeMessage(condition.toString())) @@ -192,15 +192,15 @@ class Weather2 : AbstractModule() { cwd.cityId?.let { if (it > 0) { messages.add( - NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) + NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) ) } else { messages.add( - NoticeMessage( - "https://openweathermap.org/find?q=" - + "$city,${code.uppercase()}".encodeUrl(), - Colors.GREEN - ) + NoticeMessage( + "https://openweathermap.org/find?q=" + + "$city,${code.uppercase()}".encodeUrl(), + Colors.GREEN + ) ) } } @@ -209,9 +209,9 @@ class Weather2 : AbstractModule() { } catch (e: APIException) { if (e.code == 404) { throw ModuleException( - "getWeather($query): API ${e.code}", - "The requested city was not found.", - e + "getWeather($query): API ${e.code}", + "The requested city was not found.", + e ) } else { throw ModuleException("getWeather($query): API ${e.code}", e.message, e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index a72efab..049807a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -60,15 +60,15 @@ class WolframAlpha : AbstractModule() { try { val query = args.trim().split("units=", limit = 2, ignoreCase = true) event.sendMessage( - queryWolfram( - query[0].trim(), - units = if (query.size == 2) { - getUnits(query[1].trim()) - } else { - getUnits(properties[UNITS_PROP]) - }, - appId = properties[APPID_KEY_PROP] - ) + queryWolfram( + query[0].trim(), + units = if (query.size == 2) { + getUnits(query[1].trim()) + } else { + getUnits(properties[UNITS_PROP]) + }, + appId = properties[APPID_KEY_PROP] + ) ) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) @@ -111,15 +111,15 @@ class WolframAlpha : AbstractModule() { return urlReader.body } else { throw ModuleException( - "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", - urlReader.body.ifEmpty { - "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" - } + "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", + urlReader.body.ifEmpty { + "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" + } ) } } catch (ioe: IOException) { throw ModuleException( - "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe + "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 18072bc..debbe98 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -322,7 +322,7 @@ class WorldTime : AbstractModule() { put("ZULU", "Zulu") put("ZW", "Africa/Harare") ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } - .forEach { tz -> put(tz, tz) } + .forEach { tz -> put(tz, tz) } } // The Time command @@ -336,7 +336,7 @@ class WorldTime : AbstractModule() { // Date/Time Format private var dtf = - DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") + DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") /** * Returns the current Internet (beat) Time. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 0607936..2695a3b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -34,4 +34,4 @@ package net.thauvin.erik.mobibot.msg * The `ErrorMessage` class. */ class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isError = true) + Message(msg, color, isError = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 23a33b9..3b4be49 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -36,11 +36,11 @@ import net.thauvin.erik.semver.Constants * The `Message` class. */ open class Message @JvmOverloads constructor( - var msg: String, - var color: String = DEFAULT_COLOR, - var isNotice: Boolean = false, - isError: Boolean = false, - var isPrivate: Boolean = false + var msg: String, + var color: String = DEFAULT_COLOR, + var isNotice: Boolean = false, + isError: Boolean = false, + var isPrivate: Boolean = false ) { companion object { var DEFAULT_COLOR = Constants.EMPTY diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 037d504..cd6721c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -34,5 +34,5 @@ package net.thauvin.erik.mobibot.msg * The `NoticeMessage` class. */ class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isNotice = true) + Message(msg, color, isNotice = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 842fee5..3033d1a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -33,6 +33,5 @@ package net.thauvin.erik.mobibot.msg /** * The `PrivateMessage` class. */ -@Suppress("unused") class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isPrivate = true) + Message(msg, color, isPrivate = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index cbc1936..91f2dd9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -36,7 +36,7 @@ import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.util.Timer +import java.util.* /** * Social Manager. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index b594670..32e670a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -76,8 +76,8 @@ abstract class SocialModule : AbstractModule() { post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn( - "Failed to post entry ${index.toLinkLabel()} on $name.", - e + "Failed to post entry ${index.toLinkLabel()} on $name.", + e ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index 267a59d..3fd315e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -31,7 +31,7 @@ package net.thauvin.erik.mobibot.social -import java.util.TimerTask +import java.util.* class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() { override fun run() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index ebc2aa0..5a8a638 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -41,13 +41,9 @@ import net.thauvin.erik.mobibot.commands.Die import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.View -import net.thauvin.erik.mobibot.modules.Dice -import net.thauvin.erik.mobibot.modules.Joke -import net.thauvin.erik.mobibot.modules.Lookup -import net.thauvin.erik.mobibot.modules.RockPaperScissors -import net.thauvin.erik.mobibot.modules.War +import net.thauvin.erik.mobibot.modules.* import org.testng.annotations.Test -import java.util.Properties +import java.util.* class AddonsTest { private val p = Properties().apply { @@ -80,11 +76,11 @@ class AddonsTest { assertThat(addons.names.ops, "names.ops").containsExactly("cycle") assertThat(addons.names.commands, "names.command").containsExactly( - "joke", - "rock", - "paper", - "scissors", - "ignore" + "joke", + "rock", + "paper", + "scissors", + "ignore" ) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index a3994ec..be2deb3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -46,9 +46,9 @@ object ExceptionSanitizer { with(this) { if (!cause?.message.isNullOrBlank()) { return ModuleException( - debugMessage, - cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), - this + debugMessage, + cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), + this ) } else if (!message.isNullOrBlank()) { return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index d30977e..7611ae3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -68,6 +68,6 @@ class FeedReaderTest { assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) assertFailure { readFeed("https://www.examplesfoo.com/") } - .isInstanceOf(UnknownHostException::class.java) + .isInstanceOf(UnknownHostException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index e4af75a..1384a72 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -36,7 +36,7 @@ import java.net.InetAddress import java.net.UnknownHostException import java.nio.file.Files import java.nio.file.Paths -import java.util.Properties +import java.util.* /** * Access to `local.properties`. diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 87617e8..4ebb53c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -68,7 +68,7 @@ class PinboardTest : LocalProperties() { private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { val response = - URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body + URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body matches.forEach { if (!response.contains(it)) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index ef0eaaf..8ddb013 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -66,15 +66,14 @@ import java.io.File import java.io.IOException import java.net.URL import java.time.LocalDateTime -import java.util.Calendar -import java.util.Properties +import java.util.* /** * The `Utils Test` class. */ class UtilsTest { private val ascii = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" private val cal = Calendar.getInstance() private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) private val test = "This is a test." @@ -90,7 +89,7 @@ class UtilsTest { val sep = '/' val url = "https://erik.thauvin.net" assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") - .isEqualTo(dir + File.separatorChar) + .isEqualTo(dir + File.separatorChar) assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") } @@ -116,24 +115,24 @@ class UtilsTest { fun textCapitaliseWords() { assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") assertThat("Already Capitalized".capitalizeWords(), "already capitalized") - .isEqualTo("Already Capitalized") + .isEqualTo("Already Capitalized") assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") } @Test fun testColorize() { assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( - Colors.REVERSE + ascii + Colors.REVERSE + Colors.REVERSE + ascii + Colors.REVERSE ) assertThat(ascii.colorize(Colors.RED), "red.colorize()") - .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + .isEqualTo(Colors.RED + ascii + Colors.NORMAL) assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") - .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") - .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) + .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) } @Test @@ -165,19 +164,19 @@ class UtilsTest { fun testHelpCmdSyntax() { val bot = "mobibot" assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") - .isEqualTo("$bot: $test $bot $test") + .isEqualTo("$bot: $test $bot $test") assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") - .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") } @Test fun testHelpFormat() { assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") - .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") + .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") - .isEqualTo(test.prependIndent()) + .isEqualTo(test.prependIndent()) assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") - .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) + .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) } @@ -219,15 +218,15 @@ class UtilsTest { val search = arrayOf("one", "two", "three") val replace = arrayOf("1", "2", "3") assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") - .isEqualTo(replace.joinToString(",")) + .isEqualTo(replace.joinToString(",")) assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") - .isEqualTo(test.replace("t", "").replace("e", "E")) + .isEqualTo(test.replace("t", "").replace("e", "E")) assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") - .isEqualTo(test) + .isEqualTo(test) } @Test @@ -259,7 +258,7 @@ class UtilsTest { @Test fun testUnescapeXml() { assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( - "<a name=\"test & ''\">" + "<a name=\"test & ''\">" ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 265009b..1f28049 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -40,14 +40,14 @@ class InfoTest { @Test(groups = ["commands"]) fun testToUptime() { assertThat( - 547800300076L.toUptime(), - "upTime(full)" + 547800300076L.toUptime(), + "upTime(full)" ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") assertThat( - 1320300000L.toUptime(), - "upTime(weeks days hours minutes)" + 1320300000L.toUptime(), + "upTime(weeks days hours minutes)" ).isEqualTo("2 weeks 1 day 6 hours 45 minutes") assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index f1fbe11..5f1a690 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -48,13 +48,13 @@ class RecapTest { assertThat(Recap.recaps, "Recap.recaps").all { size().isEqualTo(Recap.MAX_RECAPS) prop(MutableList<String>::first) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) prop(MutableList<String>::last) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) } Recap.storeRecap("sender", "test action", true) assertThat(Recap.recaps.last()) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index 8e49b5e..8fcbd8b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -47,8 +47,8 @@ class LinksManagerTest { fun fetchTitle() { assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") assertThat( - linksManager.fetchTitle("https://www.google.com/foo"), - "fetchTitle(Foo)" + linksManager.fetchTitle("https://www.google.com/foo"), + "fetchTitle(Foo)" ).isEqualTo(Constants.NO_TITLE) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index c28090d..0853a9d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -45,14 +45,14 @@ class ViewTest { for (i in 1..10) { LinksManager.entries.links.add( - EntryLink( - "https://www.example.com/$i", - "Example $i", - "nick$i", - "login$i", - "#channel", - emptyList() - ) + EntryLink( + "https://www.example.com/$i", + "Example $i", + "nick$i", + "login$i", + "#channel", + emptyList() + ) ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 4298a16..52a21cc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -33,14 +33,7 @@ package net.thauvin.erik.mobibot.commands.seen import assertk.all import assertk.assertThat -import assertk.assertions.isEmpty -import assertk.assertions.isEqualTo -import assertk.assertions.isGreaterThan -import assertk.assertions.isNotEqualTo -import assertk.assertions.isNotNull -import assertk.assertions.key -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import org.testng.annotations.AfterClass import org.testng.annotations.BeforeClass import org.testng.annotations.Test diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index cff11f2..115e9fb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -33,13 +33,7 @@ package net.thauvin.erik.mobibot.commands.tell import assertk.all import assertk.assertThat -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFalse -import assertk.assertions.isGreaterThan -import assertk.assertions.isTrue -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import org.testng.annotations.AfterClass import org.testng.annotations.BeforeClass import org.testng.annotations.Test diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 6eef16e..a09ebb9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -46,14 +46,14 @@ class EntriesUtilsTest { private val links = buildList { for (i in 0..5) { add( - EntryLink( - "https://www.mobitopia.org/$i", - "Mobitopia$i", - "Skynx$i", - "JimH$i", - "#mobitopia$i", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") - ) + EntryLink( + "https://www.mobitopia.org/$i", + "Mobitopia$i", + "Skynx$i", + "JimH$i", + "#mobitopia$i", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) ) } } @@ -67,7 +67,7 @@ class EntriesUtilsTest { fun printLinkTest() { for (i in links.indices) { assertThat( - printLink(i - 1, links[i]), "link $i" + printLink(i - 1, links[i]), "link $i" ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") } @@ -79,7 +79,7 @@ class EntriesUtilsTest { fun printTagsTest() { for (i in links.indices) { assertThat( - printTags(i - 1, links[i]), "tag $i" + printTags(i - 1, links[i]), "tag $i" ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index ab8c71c..4c20525 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -32,18 +32,12 @@ package net.thauvin.erik.mobibot.entries import assertk.all import assertk.assertThat -import assertk.assertions.index -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 assertk.assertions.* import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl import org.testng.annotations.Test import java.security.SecureRandom -import java.util.Date +import java.util.* /** * The `EntryUtilsTest` class. @@ -54,8 +48,8 @@ import java.util.Date */ class EntryLinkTest { private val entryLink = EntryLink( - "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") ) @Test(groups = ["entries"]) @@ -123,12 +117,12 @@ class EntryLinkTest { entryLink.setTags("+mobitopia") entryLink.setTags("-mobitopia") assertThat( - entryLink.formatTags(","), - "formatTags(',')" + entryLink.formatTags(","), + "formatTags(',')" ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") entryLink.setTags("-tag4 tag5") assertThat( - entryLink.formatTags(" ", ","), "formatTag(' ',',')" + entryLink.formatTags(" ", ","), "formatTag(' ',',')" ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") val size = entryLink.tags.size entryLink.setTags("") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index cd2ebb8..4223d9d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -33,18 +33,12 @@ package net.thauvin.erik.mobibot.entries import assertk.all import assertk.assertThat -import assertk.assertions.endsWith -import assertk.assertions.exists -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isTrue -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* 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 java.util.* import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize import kotlin.io.path.name diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index b3bd248..2b1d3f9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.modules import assertk.assertFailure 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 diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index e4638b9..fa50fcb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -34,7 +34,6 @@ import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause -import assertk.assertions.isFailure import assertk.assertions.isInstanceOf import net.thauvin.erik.mobibot.LocalProperties import org.testng.annotations.Test @@ -43,21 +42,21 @@ class ChatGptTest : LocalProperties() { @Test(groups = ["modules"]) fun testApiKey() { assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } - .isInstanceOf(ModuleException::class.java) - .hasNoCause() + .isInstanceOf(ModuleException::class.java) + .hasNoCause() } @Test(groups = ["modules", "no-ci"]) fun testChat() { val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) + ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") assertThat( - ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) + ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ).contains("URLEncoder") assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } - .isInstanceOf(ModuleException::class.java) + .isInstanceOf(ModuleException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 8c1d745..10a2470 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -58,16 +58,16 @@ class CurrencyConverterTest { @Test(groups = ["modules"]) fun testConvertCurrency() { assertThat( - convertCurrency("100 USD to EUR").msg, - "convertCurrency(100 USD to EUR)" + convertCurrency("100 USD to EUR").msg, + "convertCurrency(100 USD to EUR)" ).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex()) assertThat( - convertCurrency("1 USD to BTC").msg, - "convertCurrency(1 USD to BTC)" + convertCurrency("1 USD to BTC").msg, + "convertCurrency(1 USD to BTC)" ).matches("1 United States Dollar = 0\\.\\d+ Bitcoin".toRegex()) assertThat( - convertCurrency("100,000.00 GBP to BTC").msg, - "convertCurrency(100,000.00 GBP to BTC)" + convertCurrency("100,000.00 GBP to BTC").msg, + "convertCurrency(100,000.00 GBP to BTC)" ).matches("100,000.00 British Pound Sterling = \\d{1,2}\\.\\d+ Bitcoin".toRegex()) assertThat(convertCurrency("100 USD to USD"), "convertCurrency(100 USD to USD)").all { prop(Message::msg).contains("You're kidding, right?") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index cdc04f0..4225e3b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -42,12 +42,12 @@ class DiceTest { fun testRoll() { assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") assertThat(Dice.roll(2, 1), "roll(2d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") + .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") assertThat(Dice.roll(5, 1), "roll(5d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") + .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") assertThat(Dice.roll(2, 6), "roll(2d6)") - .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) + .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) assertThat(Dice.roll(3, 7), "roll(3d7)") - .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) + .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 175af47..640a721 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -33,15 +33,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertFailure import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.hasMessage -import assertk.assertions.hasNoCause -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFailure -import assertk.assertions.isInstanceOf -import assertk.assertions.isNotEmpty -import assertk.assertions.prop +import assertk.assertions.* import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle @@ -56,19 +48,19 @@ class GoogleSearchTest : LocalProperties() { @Test(groups = ["modules"]) fun testAPIKeys() { assertThat( - searchGoogle("", "apikey", "cssKey").first(), - "searchGoogle(empty)" + searchGoogle("", "apikey", "cssKey").first(), + "searchGoogle(empty)" ).isInstanceOf(ErrorMessage::class.java) assertFailure { searchGoogle("test", "", "apiKey") } - .isInstanceOf(ModuleException::class.java).hasNoCause() + .isInstanceOf(ModuleException::class.java).hasNoCause() assertFailure { searchGoogle("test", "apiKey", "") } - .isInstanceOf(ModuleException::class.java).hasNoCause() + .isInstanceOf(ModuleException::class.java).hasNoCause() assertFailure { searchGoogle("test", "apiKey", "cssKey") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("API key not valid. Please pass a valid API key.") + .isInstanceOf(ModuleException::class.java) + .hasMessage("API key not valid. Please pass a valid API key.") } @Test(groups = ["no-ci", "modules"]) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index fa063f4..55a7b8f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -32,12 +32,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertThat -import assertk.assertions.doesNotContain -import assertk.assertions.each -import assertk.assertions.isGreaterThan -import assertk.assertions.isInstanceOf -import assertk.assertions.prop -import assertk.assertions.size +import assertk.assertions.* import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 34f778a..84f9375 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -42,13 +42,13 @@ class MastodonTest : LocalProperties() { fun testToot() { val msg = "Testing Mastodon API from ${getHostName()}" assertThat( - toot( - getProperty(Mastodon.ACCESS_TOKEN_PROP), - getProperty(Mastodon.INSTANCE_PROP), - getProperty(Mastodon.HANDLE_PROP), - msg, - true - ) + toot( + getProperty(Mastodon.ACCESS_TOKEN_PROP), + getProperty(Mastodon.INSTANCE_PROP), + getProperty(Mastodon.HANDLE_PROP), + msg, + true + ) ).contains(msg) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index c7dbfc0..b36285b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -32,13 +32,7 @@ package net.thauvin.erik.mobibot.modules 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 assertk.assertions.* import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import org.testng.annotations.DataProvider import org.testng.annotations.Test @@ -57,9 +51,9 @@ class ModuleExceptionTest { @DataProvider(name = "dp") fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> { return arrayOf( - arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))), - arrayOf(ModuleException(debugMessage, message)) + arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), + arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))), + arrayOf(ModuleException(debugMessage, message)) ) } @@ -78,7 +72,7 @@ class ModuleExceptionTest { val apiKey = "1234567890" var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) assertThat( - e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" + e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" ).isNotNull().all { contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") doesNotContain(apiKey, "me") @@ -92,7 +86,7 @@ class ModuleExceptionTest { e = ModuleException(debugMessage, apiKey) assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() - .doesNotContain(apiKey) + .doesNotContain(apiKey) val msg: String? = null e = ModuleException(debugMessage, msg, IOException(msg)) @@ -100,8 +94,8 @@ class ModuleExceptionTest { e = ModuleException(debugMessage, msg, IOException("foo is $apiKey")) assertThat( - e.sanitize(" ", apiKey, "foo").message, - "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" + e.sanitize(" ", apiKey, "foo").message, + "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" ).isNotNull().all { doesNotContain(apiKey) endsWith("xxx is xxxxxxxxxx") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index b4a277e..17e5b92 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -33,14 +33,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertFailure import assertk.assertThat -import assertk.assertions.hasNoCause -import assertk.assertions.index -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 assertk.assertions.* import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote @@ -67,7 +60,7 @@ class StockQuoteTest : LocalProperties() { assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) - .matches(buildMatch("Previous").toRegex()) + .matches(buildMatch("Previous").toRegex()) assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) symbol = "blahfoo" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index ca650bb..d7d65de 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -33,16 +33,7 @@ package net.thauvin.erik.mobibot.modules import assertk.all import assertk.assertFailure import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.endsWith -import assertk.assertions.hasNoCause -import assertk.assertions.index -import assertk.assertions.isEqualTo -import assertk.assertions.isFailure -import assertk.assertions.isInstanceOf -import assertk.assertions.isNotNull -import assertk.assertions.isTrue -import assertk.assertions.prop +import assertk.assertions.* import net.aksingh.owmjapis.api.APIException import net.aksingh.owmjapis.core.OWM import net.thauvin.erik.mobibot.LocalProperties @@ -69,7 +60,7 @@ class Weather2Test : LocalProperties() { assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) - val country = OWM.Country.values() + val country = OWM.Country.entries.toTypedArray() repeat(3) { val rand = country[(country.indices).random()] assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index ae1722d..281d8af 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -45,11 +45,11 @@ class WolframAlphaTest : LocalProperties() { @Test(groups = ["modules"]) fun testAppId() { assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("Error 1: Invalid appid") + .isInstanceOf(ModuleException::class.java) + .hasMessage("Error 1: Invalid appid") assertFailure { queryWolfram("1 gallon to liter", appId = "") } - .isInstanceOf(ModuleException::class.java) + .isInstanceOf(ModuleException::class.java) } @Test(groups = ["modules", "no-ci"]) @@ -62,8 +62,8 @@ class WolframAlphaTest : LocalProperties() { query = "SFO to LAX" assertThat( - queryWolfram(query, WolframAlpha.METRIC, apiKey), - "queryWolfram($query)" + queryWolfram(query, WolframAlpha.METRIC, apiKey), + "queryWolfram($query)" ).contains("kilometers") } catch (e: ModuleException) { // Avoid displaying api key in CI logs diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 46888e3..29f5589 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -30,10 +30,8 @@ */ package net.thauvin.erik.mobibot.modules -import assertk.assertFailure 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 @@ -51,9 +49,9 @@ class WordTimeTest { @Test(groups = ["modules"]) fun testTime() { assertThat(time(), "time()").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() + ("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(""), "time()").endsWith("Los Angeles".bold()) assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) diff --git a/version.properties b/version.properties index b464c4e..b7cc5c2 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Sun Jul 02 02:19:45 PDT 2023 -version.buildmeta=20230702021945 +#Thu Jul 06 10:21:40 PDT 2023 +version.buildmeta=20230706102140 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20230702021945 +version.semver=0.8.0-rc+20230706102140 From c99debd740cb3f50ea742899efef82d4883a6ef8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 1 Sep 2023 15:29:45 -0700 Subject: [PATCH 737/858] Updated dependencies --- .gitignore | 1 - .idea/codeStyles/Project.xml | 35 ++++++++++++------ .idea/codeStyles/codeStyleConfig.xml | 1 + .idea/misc.xml | 1 - build.gradle | 18 ++++----- gradle.properties | 0 gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 4 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- version.properties | 6 +-- 12 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 gradle.properties diff --git a/.gitignore b/.gitignore index 2b959f1..970cefd 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,6 @@ dist/ ehthumbs.db fabric.properties gen/ -gradle.properties hs_err_pid* kobaltBuild kobaltw*-test diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 76b5425..10aa334 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,11 +1,17 @@ <component name="ProjectCodeStyleConfiguration"> - <code_scheme name="Project" version="173"> + <code_scheme name="Project" version="200"> <JetCodeStyleSettings> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </JetCodeStyleSettings> <codeStyleSettings language="JAVA"> <option name="IF_BRACE_FORCE" value="1" /> <arrangement> + <groups> + <group> + <type>OVERRIDDEN_METHODS</type> + <order>BY_NAME</order> + </group> + </groups> <rules> <section> <rule> @@ -17,6 +23,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -29,6 +36,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -41,6 +49,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -53,6 +62,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -64,6 +74,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -73,8 +84,10 @@ <FIELD>true</FIELD> <PROTECTED>true</PROTECTED> <STATIC>true</STATIC> + <visibility /> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -86,6 +99,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -97,6 +111,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -118,6 +133,7 @@ <PUBLIC>true</PUBLIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -129,6 +145,7 @@ <PROTECTED>true</PROTECTED> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -140,6 +157,7 @@ <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -151,6 +169,7 @@ <PRIVATE>true</PRIVATE> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -161,6 +180,7 @@ <PUBLIC>true</PUBLIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -171,6 +191,7 @@ <PROTECTED>true</PROTECTED> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -195,21 +216,12 @@ <order>BY_NAME</order> </rule> </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PRIVATE>false</PRIVATE> - </AND> - </match> - </rule> - </section> <section> <rule> <match> <FIELD>true</FIELD> </match> + <order>BY_NAME</order> </rule> </section> <section> @@ -234,6 +246,7 @@ <STATIC>true</STATIC> </AND> </match> + <order>BY_NAME</order> </rule> </section> <section> diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index d91f848..23f4bb5 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ <component name="ProjectCodeStyleConfiguration"> <state> + <option name="USE_PER_PROJECT_SETTINGS" value="true" /> <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> </state> </component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index f648a66..e7a3cca 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="FrameworkDetectionExcludesConfiguration"> diff --git a/build.gradle b/build.gradle index fa00b54..caf1183 100644 --- a/build.gradle +++ b/build.gradle @@ -7,13 +7,13 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.47.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.23.0' + id 'io.gitlab.arturbosch.detekt' version '1.23.1' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.9.0' - id 'org.jetbrains.kotlin.kapt' version '1.9.0' - id 'org.jetbrains.kotlinx.kover' version '0.7.2' - id 'org.sonarqube' version '4.2.1.3168' + id 'org.jetbrains.kotlin.jvm' version '1.9.10' + id 'org.jetbrains.kotlin.kapt' version '1.9.10' + id 'org.jetbrains.kotlinx.kover' version '0.7.3' + id 'org.sonarqube' version '4.3.1.3277' id 'pmd' } @@ -54,20 +54,20 @@ dependencies { // implementation fileTree(dir: 'lib', include: '*.jar') // Commons (mostly for PircBotX) - implementation 'org.apache.commons:commons-lang3:3.12.0' + implementation 'org.apache.commons:commons-lang3:3.13.0' implementation 'org.apache.commons:commons-text:1.10.0' implementation 'commons-codec:commons-codec:1.16.0' implementation 'commons-net:commons-net:3.9.0' // Google implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.1.1-jre' + implementation 'com.google.guava:guava:32.1.2-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2' - implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' + implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6' // Logging implementation 'org.slf4j:slf4j-api:2.0.7' diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e69de29 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 28216 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 delta 27891 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 62f495d..ac72c34 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index fcb6fca..0adc8e1 100755 --- a/gradlew +++ b/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index a0e8fd4..2760fa7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.jokeapi.exceptions.HttpErrorException import net.thauvin.erik.jokeapi.exceptions.JokeException -import net.thauvin.erik.jokeapi.getJoke +import net.thauvin.erik.jokeapi.joke import net.thauvin.erik.jokeapi.models.Type import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.colorize @@ -83,7 +83,7 @@ class Joke : AbstractModule() { @Throws(ModuleException::class) fun randomJoke(): List<Message> { return try { - val joke = getJoke(safe = true, type = Type.SINGLE, splitNewLine = true) + val joke = joke(safe = true, type = Type.SINGLE, splitNewLine = true) joke.joke.map { PublicMessage(it, Colors.CYAN) } } catch (e: JokeException) { throw ModuleException("randomJoke(): ${e.additionalInfo}", e.message, e) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index e3475f1..0547c95 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -72,7 +72,7 @@ class CryptoPricesTest { @Test(groups = ["modules"]) fun testGetCurrencyName() { - assertThat(getCurrencyName("USD"), "USD").isEqualTo("US Dollar") + assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") } } diff --git a/version.properties b/version.properties index b7cc5c2..854597d 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Jul 06 10:21:40 PDT 2023 -version.buildmeta=20230706102140 +#Fri Sep 01 15:28:31 PDT 2023 +version.buildmeta=20230901152831 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20230706102140 +version.semver=0.8.0-rc+20230901152831 From 65fa55d51f20af2405692feb428171e2b6d99d5d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 22 Sep 2023 03:05:10 -0700 Subject: [PATCH 738/858] Updated dependencies --- .idea/kotlinc.xml | 2 +- build.gradle | 8 ++++---- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fdf8d99..f8467b4 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="KotlinJpsPluginSettings"> - <option name="version" value="1.9.0" /> + <option name="version" value="1.9.10" /> </component> </project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index caf1183..d5a1151 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.47.0' + id 'com.github.ben-manes.versions' version '0.48.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.23.1' id 'java' @@ -70,7 +70,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6' // Logging - implementation 'org.slf4j:slf4j-api:2.0.7' + implementation 'org.slf4j:slf4j-api:2.0.9' implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" @@ -86,9 +86,9 @@ dependencies { implementation 'net.thauvin.erik:cryptoprice:1.0.0' implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' implementation 'net.thauvin.erik:pinboard-poster:1.0.3' - implementation 'net.thauvin.erik:urlencoder:1.3.0' + implementation 'net.thauvin.erik.urlencoder:urlencoder-lib:1.4.0' - testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.26.1' + testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.27.0' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation "org.mockito:mockito-core:4.0.0" testImplementation 'org.testng:testng:7.8.0' diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 0595220..51adc88 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR -import net.thauvin.erik.urlencoder.UrlEncoder +import net.thauvin.erik.urlencoder.UrlEncoderUtil import org.jsoup.Jsoup import org.pircbotx.Colors import org.pircbotx.PircBotX @@ -42,6 +42,7 @@ import org.slf4j.Logger import java.io.* import java.net.HttpURLConnection import java.net.URL +import java.net.URLEncoder import java.nio.file.Files import java.nio.file.Paths import java.time.LocalDateTime @@ -145,7 +146,7 @@ object Utils { * URL encodes the given string. */ @JvmStatic - fun String.encodeUrl(): String = UrlEncoder.encode(this) + fun String.encodeUrl(): String = UrlEncoderUtil.encode(this) /** * Returns a property as an int. From 8fb872ad6fca07974e8143cdfc38895defcf1ee5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 22 Sep 2023 03:06:08 -0700 Subject: [PATCH 739/858] Switched to ExchangeRate-API --- config/detekt/baseline.xml | 3 +- properties/mobibot.properties | 6 ++ .../erik/mobibot/modules/CurrencyConverter.kt | 80 ++++++++++++------- .../mobibot/modules/CurrencyConverterTest.kt | 26 +++--- 4 files changed, 72 insertions(+), 43 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index f7cc151..9f157cf 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -59,7 +59,8 @@ <ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID> <ID>NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String</ID> <ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> - <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(query: String): Message</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?)</ID> + <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>)</ID> <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID> <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> diff --git a/properties/mobibot.properties b/properties/mobibot.properties index fa8ce50..24d9977 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -45,6 +45,12 @@ disabled-modules=mastodon # Automatically post links to Mastodon #mastodon-auto-post=true + +# +# Get Exchange Rate API key from: https://www.exchangerate-api.com/ +# +#exchangerate-api-key= + # # Create custom search engine at: https://programmablesearchengine.google.com/ # and get API key from: https://console.cloud.google.com/apis diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 0bf9d7a..284f9d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -44,6 +44,7 @@ import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException +import java.math.BigDecimal import java.net.URL import java.util.* @@ -57,10 +58,10 @@ class CurrencyConverter : AbstractModule() { override val name = "CurrencyConverter" // Reload currency codes - private fun reload() { - if (SYMBOLS.isEmpty()) { + private fun reload(apiKey: String?) { + if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) { try { - loadSymbols() + loadSymbols(apiKey) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) } @@ -71,12 +72,12 @@ class CurrencyConverter : AbstractModule() { * Converts the specified currencies. */ override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - reload() + reload(properties[API_KEY_PROP]) if (SYMBOLS.isEmpty()) { event.respond(EMPTY_SYMBOLS_TABLE) } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) { - val msg = convertCurrency(args) + val msg = convertCurrency(properties[API_KEY_PROP], args) event.respond(msg.msg) if (msg.isError) { helpResponse(event) @@ -90,7 +91,7 @@ class CurrencyConverter : AbstractModule() { } override fun helpResponse(event: GenericMessageEvent): Boolean { - reload() + reload(properties[API_KEY_PROP]) if (SYMBOLS.isEmpty()) { event.sendMessage(EMPTY_SYMBOLS_TABLE) @@ -99,21 +100,26 @@ class CurrencyConverter : AbstractModule() { event.sendMessage("To convert from one currency to another:") event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) - ) + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) + ) ) event.sendMessage("To list the supported currency codes:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) - ) + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) + ) ) } return true } companion object { + /** + * The API Key property. + */ + const val API_KEY_PROP = "exchangerate-api-key" + // Currency command private const val CURRENCY_CMD = "currency" @@ -130,7 +136,11 @@ class CurrencyConverter : AbstractModule() { * Converts from a currency to another. */ @JvmStatic - fun convertCurrency(query: String): Message { + fun convertCurrency(apiKey: String?, query: String): Message { + if (apiKey.isNullOrEmpty()) { + throw ModuleException("${CURRENCY_CMD}($query)", "No Exchange Rate API key specified.") + } + val cmds = query.split(" ") return if (cmds.size == 4) { if (cmds[3] == cmds[1] || "0" == cmds[0]) { @@ -141,12 +151,14 @@ class CurrencyConverter : AbstractModule() { if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) { try { val amt = cmds[0].replace(",", "") - val url = URL("https://api.exchangerate.host/convert?from=$to&to=$from&amount=$amt") - val json = JSONObject(url.reader().body) + val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/pair/$to/$from/$amt") + val body = url.reader().body + val json = JSONObject(body) - if (json.getBoolean("success")) { + if (json.getString("result") == "success") { + val result = json.getDouble("conversion_result") PublicMessage( - "${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}" + "${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}" ) } else { ErrorMessage("Sorry, an error occurred while converting the currencies.") @@ -158,7 +170,9 @@ class CurrencyConverter : AbstractModule() { ErrorMessage("Sounds like monopoly money to me!") } } - } else ErrorMessage("Invalid query. Let's try again.") + } else { + ErrorMessage("Invalid query. Let's try again.") + } } /** @@ -166,28 +180,32 @@ class CurrencyConverter : AbstractModule() { */ @JvmStatic @Throws(ModuleException::class) - fun loadSymbols() { - try { - val url = URL("https://api.exchangerate.host/symbols") - val json = JSONObject(url.reader().body) - if (json.getBoolean("success")) { - val symbols = json.getJSONObject("symbols") - for (key in symbols.keys()) { - SYMBOLS[key] = symbols.getJSONObject(key).getString("description") + fun loadSymbols(apiKey: String?) { + if (!apiKey.isNullOrEmpty()) { + try { + val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/codes") + val json = JSONObject(url.reader().body) + if (json.getString("result") == "success") { + val codes = json.getJSONArray("supported_codes") + for (i in 0 until codes.length()) { + val code = codes.getJSONArray(i); + SYMBOLS[code.getString(0)] = code.getString(1); + } } - } - } catch (e: IOException) { - throw ModuleException( + } catch (e: IOException) { + throw ModuleException( "loadCodes(): IOE", "An IO error has occurred while retrieving the currencies.", e - ) + ) + } } } } init { commands.add(CURRENCY_CMD) - loadSymbols() + initProperties(API_KEY_PROP) + loadSymbols(properties[ChatGpt.API_KEY_PROP]) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 10a2470..59ea2e7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -36,6 +36,7 @@ import assertk.assertions.contains import assertk.assertions.isInstanceOf import assertk.assertions.matches import assertk.assertions.prop +import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols import net.thauvin.erik.mobibot.msg.ErrorMessage @@ -48,32 +49,35 @@ import org.testng.annotations.Test /** * The `CurrencyConvertTest` class. */ -class CurrencyConverterTest { +class CurrencyConverterTest: LocalProperties() { + @BeforeClass @Throws(ModuleException::class) fun before() { - loadSymbols() + val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) + loadSymbols(apiKey) } @Test(groups = ["modules"]) fun testConvertCurrency() { + val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) assertThat( - convertCurrency("100 USD to EUR").msg, + convertCurrency(apiKey,"100 USD to EUR").msg, "convertCurrency(100 USD to EUR)" ).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex()) assertThat( - convertCurrency("1 USD to BTC").msg, - "convertCurrency(1 USD to BTC)" - ).matches("1 United States Dollar = 0\\.\\d+ Bitcoin".toRegex()) + convertCurrency(apiKey,"1 USD to GBP").msg, + "convertCurrency(1 USD to BGP)" + ).matches("1 United States Dollar = 0\\.\\d+ Pound Sterling".toRegex()) assertThat( - convertCurrency("100,000.00 GBP to BTC").msg, - "convertCurrency(100,000.00 GBP to BTC)" - ).matches("100,000.00 British Pound Sterling = \\d{1,2}\\.\\d+ Bitcoin".toRegex()) - assertThat(convertCurrency("100 USD to USD"), "convertCurrency(100 USD to USD)").all { + convertCurrency(apiKey,"100,000.00 CAD to USD").msg, + "convertCurrency(100,000.00 GBP to USD)" + ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d+ United States Dollar".toRegex()) + assertThat(convertCurrency(apiKey,"100 USD to USD"), "convertCurrency(100 USD to USD)").all { prop(Message::msg).contains("You're kidding, right?") isInstanceOf(PublicMessage::class.java) } - assertThat(convertCurrency("100 USD"), "convertCurrency(100 USD)").all { + assertThat(convertCurrency(apiKey,"100 USD"), "convertCurrency(100 USD)").all { prop(Message::msg).contains("Invalid query.") isInstanceOf(ErrorMessage::class.java) } From 8b6384627025b93d9dae9953006d48bbe4919573 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 22 Sep 2023 04:21:24 -0700 Subject: [PATCH 740/858] Added ExchangeRate-API enviornment variable --- .github/workflows/gradle.yml | 1 + version.properties | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c917819..aa034f9 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -47,6 +47,7 @@ jobs: MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} + EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} with: arguments: build check --stacktrace diff --git a/version.properties b/version.properties index 854597d..f272017 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Sep 01 15:28:31 PDT 2023 -version.buildmeta=20230901152831 +#Fri Sep 22 04:02:33 PDT 2023 +version.buildmeta=20230922040233 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20230901152831 +version.semver=0.8.0-rc+20230922040233 From bc8bf6bf57247a81a934df3a435063c17d95697e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 4 Oct 2023 08:55:48 -0700 Subject: [PATCH 741/858] Added date format to currency converter response. --- .idea/misc.xml | 2 ++ build.gradle | 13 +++++++------ .../erik/mobibot/modules/CurrencyConverter.kt | 13 ++++++++----- version.properties | 6 +++--- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index e7a3cca..67f5457 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="FrameworkDetectionExcludesConfiguration"> @@ -7,6 +8,7 @@ <option name="customRuleSets"> <list> <option value="K:\java\semver\config\pmd.xml" /> + <option value="$PROJECT_DIR$/../../java/bld-exec/config/pmd.xml" /> </list> </option> <option name="skipTestSources" value="false" /> diff --git a/build.gradle b/build.gradle index d5a1151..7e13746 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' version '1.9.10' id 'org.jetbrains.kotlin.kapt' version '1.9.10' id 'org.jetbrains.kotlinx.kover' version '0.7.3' - id 'org.sonarqube' version '4.3.1.3277' + id 'org.sonarqube' version '4.4.1.3373' id 'pmd' } @@ -21,7 +21,7 @@ defaultTasks 'deploy' final def packageName = 'net.thauvin.erik.mobibot' final def deployDir = 'deploy' -final def semverProcessor = "net.thauvin.erik:semver:1.2.0" +final def semverProcessor = "net.thauvin.erik:semver:1.2.1" final def isCI = (System.getenv('CI') != null) @@ -77,15 +77,16 @@ dependencies { implementation 'com.rometools:rome:2.1.0' implementation 'com.squareup.okhttp3:okhttp:4.11.0' + implementation 'com.squareup.okio:okio:3.6.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' implementation 'org.json:json:20230618' implementation 'org.jsoup:jsoup:1.16.1' // Thauvin - implementation 'net.thauvin.erik:cryptoprice:1.0.0' - implementation 'net.thauvin.erik:jokeapi:0.9-SNAPSHOT' - implementation 'net.thauvin.erik:pinboard-poster:1.0.3' + implementation 'net.thauvin.erik:cryptoprice:1.0.1' + implementation 'net.thauvin.erik:jokeapi:0.9.0' + implementation 'net.thauvin.erik:pinboard-poster:1.1.0' implementation 'net.thauvin.erik.urlencoder:urlencoder-lib:1.4.0' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.27.0' @@ -211,7 +212,7 @@ sonarqube { property('sonar.organization', 'ethauvin-github') property('sonar.projectKey', 'ethauvin_mobibot') property('sonar.host.url', 'https://sonarcloud.io') - property('sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/kover/report.xml") + property('sonar.coverage.jacoco.xmlReportPaths', "${layout.buildDirectory.get()}/reports/kover/report.xml") } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 284f9d2..fc56d42 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -44,8 +44,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException -import java.math.BigDecimal import java.net.URL +import java.text.DecimalFormat import java.util.* @@ -101,7 +101,7 @@ class CurrencyConverter : AbstractModule() { event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage( helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled) + helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to USD", nick, isPrivateMsgEnabled) ) ) event.sendMessage("To list the supported currency codes:") @@ -132,6 +132,9 @@ class CurrencyConverter : AbstractModule() { // Currency symbols private val SYMBOLS: TreeMap<String, String> = TreeMap() + // Decimal format + private val DECIMAL_FORMAT = DecimalFormat("0.00") + /** * Converts from a currency to another. */ @@ -156,7 +159,7 @@ class CurrencyConverter : AbstractModule() { val json = JSONObject(body) if (json.getString("result") == "success") { - val result = json.getDouble("conversion_result") + val result = DECIMAL_FORMAT.format(json.getDouble("conversion_result")) PublicMessage( "${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}" ) @@ -188,8 +191,8 @@ class CurrencyConverter : AbstractModule() { if (json.getString("result") == "success") { val codes = json.getJSONArray("supported_codes") for (i in 0 until codes.length()) { - val code = codes.getJSONArray(i); - SYMBOLS[code.getString(0)] = code.getString(1); + val code = codes.getJSONArray(i) + SYMBOLS[code.getString(0)] = code.getString(1) } } } catch (e: IOException) { diff --git a/version.properties b/version.properties index f272017..1765f9b 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Sep 22 04:02:33 PDT 2023 -version.buildmeta=20230922040233 +#Wed Oct 04 08:46:27 PDT 2023 +version.buildmeta=20231004084627 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20230922040233 +version.semver=0.8.0-rc+20231004084627 From b562b674a776a7a108f38c6f31accce1694aad6e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Oct 2023 12:37:20 -0700 Subject: [PATCH 742/858] Updated dependencies --- build.gradle | 8 ++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 7e13746..4bd1a08 100644 --- a/build.gradle +++ b/build.gradle @@ -5,14 +5,14 @@ import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { id 'application' - id 'com.github.ben-manes.versions' version '0.48.0' + id 'com.github.ben-manes.versions' version '0.49.0' id 'idea' id 'io.gitlab.arturbosch.detekt' version '1.23.1' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'org.jetbrains.kotlin.jvm' version '1.9.10' id 'org.jetbrains.kotlin.kapt' version '1.9.10' - id 'org.jetbrains.kotlinx.kover' version '0.7.3' + id 'org.jetbrains.kotlinx.kover' version '0.7.4' id 'org.sonarqube' version '4.4.1.3373' id 'pmd' } @@ -57,11 +57,11 @@ dependencies { implementation 'org.apache.commons:commons-lang3:3.13.0' implementation 'org.apache.commons:commons-text:1.10.0' implementation 'commons-codec:commons-codec:1.16.0' - implementation 'commons-net:commons-net:3.9.0' + implementation 'commons-net:commons-net:3.10.0' // Google implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.1.2-jre' + implementation 'com.google.guava:guava:32.1.3-jre' // Kotlin implementation platform('org.jetbrains.kotlin:kotlin-bom') diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ac72c34..3fa8f86 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 0adc8e1..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -202,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ From 5a3b1d3f1912eb2b68090ec0733afa11000a3240 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Oct 2023 12:37:55 -0700 Subject: [PATCH 743/858] Changed decimal formatting --- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../thauvin/erik/mobibot/modules/CurrencyConverterTest.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index fc56d42..41cb995 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -133,7 +133,7 @@ class CurrencyConverter : AbstractModule() { private val SYMBOLS: TreeMap<String, String> = TreeMap() // Decimal format - private val DECIMAL_FORMAT = DecimalFormat("0.00") + private val DECIMAL_FORMAT = DecimalFormat("0.00#") /** * Converts from a currency to another. diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 59ea2e7..4ee9668 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -64,15 +64,15 @@ class CurrencyConverterTest: LocalProperties() { assertThat( convertCurrency(apiKey,"100 USD to EUR").msg, "convertCurrency(100 USD to EUR)" - ).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex()) + ).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex()) assertThat( convertCurrency(apiKey,"1 USD to GBP").msg, "convertCurrency(1 USD to BGP)" - ).matches("1 United States Dollar = 0\\.\\d+ Pound Sterling".toRegex()) + ).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex()) assertThat( convertCurrency(apiKey,"100,000.00 CAD to USD").msg, "convertCurrency(100,000.00 GBP to USD)" - ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d+ United States Dollar".toRegex()) + ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex()) assertThat(convertCurrency(apiKey,"100 USD to USD"), "convertCurrency(100 USD to USD)").all { prop(Message::msg).contains("You're kidding, right?") isInstanceOf(PublicMessage::class.java) From 105850990b8b7b185088d54def99b21817513431 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Oct 2023 13:34:30 -0700 Subject: [PATCH 744/858] Updated versions --- README.md | 2 +- version.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index df026ab..1ecefeb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.0-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.10-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/version.properties b/version.properties index 1765f9b..8b93b12 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Wed Oct 04 08:46:27 PDT 2023 -version.buildmeta=20231004084627 +#Fri Oct 13 13:31:20 PDT 2023 +version.buildmeta=20231013133120 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20231004084627 +version.semver=0.8.0-rc+20231013133120 From 86b35874bfb6f5ec5f259cfbc3c1c54cc6a2d20c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 13 Oct 2023 13:35:19 -0700 Subject: [PATCH 745/858] Using lorem-rss instead of my server for testing --- src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 7611ae3..51e2296 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -54,12 +54,13 @@ class FeedReaderTest { index(1).prop(Message::msg).contains("erik.thauvin.net") } - messages = readFeed("https://www.mobitopia.org/mobibot/logs/2021-10-27.xml") + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing") - messages = readFeed("https://www.mobitopia.org/mobibot/logs/2005-10-11.xml", 42) + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42) assertThat(messages, "messages").size().isEqualTo(84) - assertThat(messages.last(), "messages.last").prop(Message::msg).contains("techdigest.tv") + assertThat(messages[messages.size - 2], "messages.size - 2").prop(Message::msg).startsWith("Lorem ipsum") + assertThat(messages.last(), "messages.last").prop(Message::msg).contains("http://example.com/test/") assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) From 8e37c3912a5d14bc35a17ad61c67238a856e6fd1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 17 Oct 2023 13:26:24 -0700 Subject: [PATCH 746/858] Improved feed reader testing --- .../net/thauvin/erik/mobibot/FeedReaderTest.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 51e2296..78f5b18 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -59,8 +59,13 @@ class FeedReaderTest { messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42) assertThat(messages, "messages").size().isEqualTo(84) - assertThat(messages[messages.size - 2], "messages.size - 2").prop(Message::msg).startsWith("Lorem ipsum") - assertThat(messages.last(), "messages.last").prop(Message::msg).contains("http://example.com/test/") + messages.forEachIndexed { i, m -> + if (i % 2 == 0) { + assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum") + } else { + assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/") + } + } assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) @@ -68,7 +73,6 @@ class FeedReaderTest { assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) - assertFailure { readFeed("https://www.examplesfoo.com/") } - .isInstanceOf(UnknownHostException::class.java) + assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java) } } From abebca50796430694d12eecd478d5399c78377a9 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Tue, 17 Oct 2023 13:26:43 -0700 Subject: [PATCH 747/858] Updated dependencies --- build.gradle | 4 ++-- version.properties | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 4bd1a08..75db38b 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.20.0', + log4j: '2.21.0', pmd : '6.55.0', ] @@ -80,7 +80,7 @@ dependencies { implementation 'com.squareup.okio:okio:3.6.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20230618' + implementation 'org.json:json:20231013' implementation 'org.jsoup:jsoup:1.16.1' // Thauvin diff --git a/version.properties b/version.properties index 8b93b12..ad204e5 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Oct 13 13:31:20 PDT 2023 -version.buildmeta=20231013133120 +#Fri Oct 13 20:37:28 PDT 2023 +version.buildmeta=20231013203728 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20231013133120 +version.semver=0.8.0-rc+20231013203728 From acaa27f20de4ba34ee3d18d07071d38dae8ea2d0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 26 Oct 2023 20:46:36 -0700 Subject: [PATCH 748/858] Added support for currency X in Y command --- .../net/thauvin/erik/mobibot/modules/CurrencyConverter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 41cb995..c194571 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -76,7 +76,7 @@ class CurrencyConverter : AbstractModule() { if (SYMBOLS.isEmpty()) { event.respond(EMPTY_SYMBOLS_TABLE) - } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) { + } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex())) { val msg = convertCurrency(properties[API_KEY_PROP], args) event.respond(msg.msg) if (msg.isError) { From adaff5ec38a809545b6b2832a8665a228600c2a4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 26 Oct 2023 20:46:53 -0700 Subject: [PATCH 749/858] Updated dependencies --- build.gradle | 7 +++---- version.properties | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 75db38b..9fb85c6 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ def isNonStable = { String version -> mainClassName = packageName + '.Mobibot' ext.versions = [ - log4j: '2.21.0', + log4j: '2.21.1', pmd : '6.55.0', ] @@ -76,12 +76,11 @@ dependencies { implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" implementation 'com.rometools:rome:2.1.0' - implementation 'com.squareup.okhttp3:okhttp:4.11.0' - implementation 'com.squareup.okio:okio:3.6.0' + implementation 'com.squareup.okhttp3:okhttp:4.12.0' implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.objecthunter:exp4j:0.4.8' implementation 'org.json:json:20231013' - implementation 'org.jsoup:jsoup:1.16.1' + implementation 'org.jsoup:jsoup:1.16.2' // Thauvin implementation 'net.thauvin.erik:cryptoprice:1.0.1' diff --git a/version.properties b/version.properties index ad204e5..c73da9e 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Fri Oct 13 20:37:28 PDT 2023 -version.buildmeta=20231013203728 +#Thu Oct 26 20:43:39 PDT 2023 +version.buildmeta=20231026204339 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20231013203728 +version.semver=0.8.0-rc+20231026204339 From d700aa06dfe785ab6dee411482f5866d554f6440 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Thu, 26 Oct 2023 21:13:46 -0700 Subject: [PATCH 750/858] Fixed SonarCloud code smells --- .idea/misc.xml | 1 - .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 22 ++++++++------- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 27 +++++++++++-------- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 27 +++++++++++-------- 6 files changed, 47 insertions(+), 34 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 67f5457..a59e398 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="FrameworkDetectionExcludesConfiguration"> diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 51adc88..38b89e3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -42,7 +42,6 @@ import org.slf4j.Logger import java.io.* import java.net.HttpURLConnection import java.net.URL -import java.net.URLEncoder import java.nio.file.Files import java.nio.file.Paths import java.time.LocalDateTime @@ -125,14 +124,19 @@ object Utils { */ @JvmStatic fun String?.colorize(color: String): String { - return if (isNullOrEmpty()) { - "" - } else if (color == DEFAULT_COLOR) { - this - } else if (Colors.BOLD == color || Colors.REVERSE == color) { - color + this + color - } else { - color + this + Colors.NORMAL + return when { + isNullOrEmpty() -> { + "" + } + color == DEFAULT_COLOR -> { + this + } + Colors.BOLD == color || Colors.REVERSE == color -> { + color + this + color + } + else -> { + color + this + Colors.NORMAL + } } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 88109e8..723108d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -129,7 +129,7 @@ class Ignore : AbstractCommand() { } } - if (ignored.size > 0) { + if (ignored.isNotEmpty()) { event.sendMessage("The following nicks are ignored:") event.sendList(ignored.sorted(), 8, isIndent = true) } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 96800bb..cdeb943 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -85,18 +85,23 @@ class Tell(private val serialObject: String) : AbstractCommand() { override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (isEnabled()) { - if (args.isBlank()) { - helpResponse(channel, args, event) - } else if (args.startsWith(View.VIEW_CMD)) { - if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { - viewAll(event) - } else { - viewMessages(event) + when { + args.isBlank() -> { + helpResponse(channel, args, event) + } + args.startsWith(View.VIEW_CMD) -> { + if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { + viewAll(event) + } else { + viewMessages(event) + } + } + args.startsWith("$TELL_DEL_KEYWORD ") -> { + deleteMessage(channel, args, event) + } + else -> { + newMessage(channel, args, event) } - } else if (args.startsWith("$TELL_DEL_KEYWORD ")) { - deleteMessage(channel, args, event) - } else { - newMessage(channel, args, event) } if (clean()) { save() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index a30ba24..2d87dbf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -141,7 +141,7 @@ class FeedsManager private constructor() { .append("\"><b>") .append(channel) .append("</b></a>") - if (comments.size > 0) { + if (comments.isNotEmpty()) { buff.append(" <br/><br/>") for (j in comments.indices) { if (j > 0) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index c194571..3aad379 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -74,19 +74,24 @@ class CurrencyConverter : AbstractModule() { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { reload(properties[API_KEY_PROP]) - if (SYMBOLS.isEmpty()) { - event.respond(EMPTY_SYMBOLS_TABLE) - } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex())) { - val msg = convertCurrency(properties[API_KEY_PROP], args) - event.respond(msg.msg) - if (msg.isError) { + when { + SYMBOLS.isEmpty() -> { + event.respond(EMPTY_SYMBOLS_TABLE) + } + args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> { + val msg = convertCurrency(properties[API_KEY_PROP], args) + event.respond(msg.msg) + if (msg.isError) { + helpResponse(event) + } + } + args.contains(CODES_KEYWORD) -> { + event.sendMessage("The supported currency codes are:") + event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) + } + else -> { helpResponse(event) } - } else if (args.contains(CODES_KEYWORD)) { - event.sendMessage("The supported currency codes are:") - event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) - } else { - helpResponse(event) } } From 4c90870f4a2f9ff75adb842b6fc10a2d9bd39a55 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 1 Nov 2023 22:02:54 -0700 Subject: [PATCH 751/858] Cleaned up code --- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 60 ++++++++--------- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 29 ++++---- .../thauvin/erik/mobibot/commands/Ignore.kt | 14 ++-- .../net/thauvin/erik/mobibot/commands/Info.kt | 10 +-- .../net/thauvin/erik/mobibot/commands/Msg.kt | 4 +- .../thauvin/erik/mobibot/commands/Recap.kt | 8 +-- .../thauvin/erik/mobibot/commands/Versions.kt | 8 +-- .../erik/mobibot/commands/links/Comment.kt | 46 ++++++------- .../mobibot/commands/links/LinksManager.kt | 6 +- .../erik/mobibot/commands/links/Posting.kt | 20 +++--- .../erik/mobibot/commands/links/Tags.kt | 4 +- .../erik/mobibot/commands/links/View.kt | 10 +-- .../mobibot/commands/seen/NickComparator.kt | 1 + .../erik/mobibot/commands/seen/Seen.kt | 14 ++-- .../erik/mobibot/commands/seen/SeenNick.kt | 1 + .../erik/mobibot/commands/tell/Tell.kt | 47 ++++++------- .../erik/mobibot/commands/tell/TellMessage.kt | 25 +++---- .../thauvin/erik/mobibot/entries/Entries.kt | 8 +-- .../erik/mobibot/entries/EntriesUtils.kt | 6 +- .../erik/mobibot/entries/EntryComment.kt | 1 + .../thauvin/erik/mobibot/entries/EntryLink.kt | 57 ++++++++-------- .../erik/mobibot/entries/FeedsManager.kt | 48 +++++++------- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 38 +++++------ .../erik/mobibot/modules/CryptoPrices.kt | 6 +- .../erik/mobibot/modules/CurrencyConverter.kt | 3 + .../erik/mobibot/modules/GoogleSearch.kt | 30 ++++----- .../thauvin/erik/mobibot/modules/Lookup.kt | 6 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 40 +++++------ .../erik/mobibot/modules/ModuleException.kt | 7 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 24 +++---- .../erik/mobibot/modules/RockPaperScissors.kt | 8 +-- .../erik/mobibot/modules/StockQuote.kt | 66 +++++++++---------- .../thauvin/erik/mobibot/modules/Weather2.kt | 34 +++++----- .../erik/mobibot/modules/WolframAlpha.kt | 28 ++++---- .../thauvin/erik/mobibot/modules/WorldTime.kt | 4 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 10 +-- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 4 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 10 +-- .../erik/mobibot/ExceptionSanitizer.kt | 6 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 32 ++++----- .../thauvin/erik/mobibot/commands/InfoTest.kt | 8 +-- .../erik/mobibot/commands/RecapTest.kt | 6 +- .../commands/links/LinksManagerTest.kt | 4 +- .../erik/mobibot/commands/links/ViewTest.kt | 16 ++--- .../erik/mobibot/entries/EntriesUtilsTest.kt | 20 +++--- .../erik/mobibot/entries/EntryLinkTest.kt | 10 +-- .../erik/mobibot/modules/ChatGptTest.kt | 10 +-- .../mobibot/modules/CurrencyConverterTest.kt | 18 ++--- .../thauvin/erik/mobibot/modules/DiceTest.kt | 8 +-- .../erik/mobibot/modules/GoogleSearchTest.kt | 12 ++-- .../erik/mobibot/modules/MastodonTest.kt | 14 ++-- .../mobibot/modules/ModuleExceptionTest.kt | 38 +++++------ .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 10 +-- .../erik/mobibot/modules/WordTimeTest.kt | 6 +- 61 files changed, 496 insertions(+), 481 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 7cf6719..98ef74a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -63,7 +63,7 @@ object Constants { * User-Agent */ const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 3342077..f91c457 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -97,9 +97,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro event.sendMessage("Type a URL on $channel to post it.") event.sendMessage("For more information on a specific command, type:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) - ) + helpFormat( + helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) + ) ) event.sendMessage("The commands are:") event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) @@ -161,7 +161,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot<PircBotX>()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has joined ${event.channel.name} on $serverHostname" + "$nick has joined ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -209,7 +209,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro with(event.getBot<PircBotX>()) { if (user.nick == nick) { LinksManager.socialManager.notification( - "$nick has left ${event.channel.name} on $serverHostname" + "$nick has left ${event.channel.name} on $serverHostname" ) seen.add(userChannelDao.getChannel(channel).users) } else { @@ -232,22 +232,22 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro // Set up the command line options val parser = ArgParser(Constants.CLI_CMD) val debug by parser.option( - ArgType.Boolean, - Constants.DEBUG_ARG, - Constants.DEBUG_ARG.substring(0, 1), - "Print debug & logging data directly to the console" + ArgType.Boolean, + Constants.DEBUG_ARG, + Constants.DEBUG_ARG.substring(0, 1), + "Print debug & logging data directly to the console" ).default(false) val property by parser.option( - ArgType.String, - Constants.PROPS_ARG, - Constants.PROPS_ARG.substring(0, 1), - "Use alternate properties file" + ArgType.String, + Constants.PROPS_ARG, + Constants.PROPS_ARG.substring(0, 1), + "Use alternate properties file" ).default("./${ReleaseInfo.PROJECT}.properties") val version by parser.option( - ArgType.Boolean, - Constants.VERSION_ARG, - Constants.VERSION_ARG.substring(0, 1), - "Print version info" + ArgType.Boolean, + Constants.VERSION_ARG, + Constants.VERSION_ARG.substring(0, 1), + "Print version info" ).default(false) // Parse the command line @@ -256,8 +256,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro if (version) { // Output the version println( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + + " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" ) println(ReleaseInfo.WEBSITE) } else { @@ -265,7 +265,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro val p = Properties() try { Files.newInputStream( - Paths.get(property) + Paths.get(property) ).use { fis -> p.load(fis) } @@ -284,11 +284,11 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro if (!debug) { try { val stdout = PrintStream( - BufferedOutputStream( - FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true - ) - ), true + BufferedOutputStream( + FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true + ) + ), true ) System.setOut(stdout) } catch (ignore: IOException) { @@ -297,9 +297,9 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro } try { val stderr = PrintStream( - BufferedOutputStream( - FileOutputStream("$logsDir$nickname.err", true) - ), true + BufferedOutputStream( + FileOutputStream("$logsDir$nickname.err", true) + ), true ) System.setErr(stderr) } catch (ignore: IOException) { @@ -324,8 +324,8 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro login = p.getProperty("login", nickname) realName = p.getProperty("realname", nickname) addServer( - ircServer, - p.getIntProperty("port", Constants.DEFAULT_PORT) + ircServer, + p.getIntProperty("port", Constants.DEFAULT_PORT) ) addAutoJoinChannel(channel) addListener(this@Mobibot) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 1a4260d..7cb5aed 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -92,7 +92,7 @@ class Pinboard { */ private fun Date.toTimestamp(): String { return ZonedDateTime.ofInstant( - toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() + toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() ).format(DateTimeFormatter.ISO_INSTANT) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 38b89e3..e4760d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -128,12 +128,15 @@ object Utils { isNullOrEmpty() -> { "" } + color == DEFAULT_COLOR -> { this } + Colors.BOLD == color || Colors.REVERSE == color -> { color + this + color } + else -> { color + this + Colors.NORMAL } @@ -220,7 +223,7 @@ object Utils { if (serialFile.exists() && serialFile.fileSize() > 0) { try { ObjectInputStream( - BufferedInputStream(Files.newInputStream(serialFile)) + BufferedInputStream(Files.newInputStream(serialFile)) ).use { input -> if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") return input.readObject() @@ -307,20 +310,20 @@ object Utils { @JvmStatic @JvmOverloads fun GenericMessageEvent.sendList( - list: List<String>, - maxPerLine: Int, - separator: String = " ", - isBold: Boolean = false, - isIndent: Boolean = false + 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 - ), + helpFormat( + list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""), + isBold, + isIndent + ), ) i += maxPerLine } @@ -419,8 +422,8 @@ object Utils { fun URL.reader(): UrlReaderResponse { val connection = this.openConnection() as HttpURLConnection connection.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + "User-Agent", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" ) return if (connection.responseCode.isHttpSuccess()) { UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 723108d..d083c10 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -50,15 +50,15 @@ class Ignore : AbstractCommand() { override val name = IGNORE_CMD override val help = listOf( - "To ignore a link posted to the channel:", - helpFormat("https://www.foo.bar %n"), - "To check your ignore status:", - helpFormat("%c $name"), - "To toggle your ignore status:", - helpFormat("%c $name $me") + "To ignore a link posted to the channel:", + helpFormat("https://www.foo.bar %n"), + "To check your ignore status:", + helpFormat("%c $name"), + "To toggle your ignore status:", + helpFormat("%c $name $me") ) private val helpOp = help.plus( - arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) + arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) ) override val isOpOnly = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 7eb3bdb..ed0b6ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -48,8 +48,8 @@ import kotlin.time.toDuration class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { private val allVersions = listOf( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", - "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", + "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" ) override val name = "info" override val help = listOf("To view information about the bot:", helpFormat("%c $name")) @@ -104,9 +104,9 @@ class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { event.sendList(allVersions, 1) val info = StringBuilder() info.append("Uptime: ") - .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) - .append(" [Entries: ") - .append(LinksManager.entries.links.size) + .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) + .append(" [Entries: ") + .append(LinksManager.entries.links.size) if (seen.isEnabled()) { info.append(", Seen: ").append(seen.count()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 48ff38f..20a6635 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -39,8 +39,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent 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>") + "To have the bot send a private message to someone:", + helpFormat("%c $name <nick> <text>") ) override val isOpOnly = true override val isPublic = false diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 66e721e..77154c7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -41,8 +41,8 @@ import java.time.LocalDateTime class Recap : AbstractCommand() { override val name = "recap" override val help = listOf( - "To list the last 10 public channel messages:", - helpFormat("%c $name") + "To list the last 10 public channel messages:", + helpFormat("%c $name") ) override val isOpOnly = false override val isPublic = true @@ -60,8 +60,8 @@ class Recap : AbstractCommand() { @JvmStatic fun storeRecap(sender: String, message: String, isAction: Boolean) { recaps.add( - LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() - + " - $sender" + (if (isAction) " " else ": ") + message + LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + + " - $sender" + (if (isAction) " " else ": ") + message ) if (recaps.size > MAX_RECAPS) { recaps.removeFirst() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index f920891..896c569 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -40,10 +40,10 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Versions : AbstractCommand() { private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", - "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + - ", JVM ${System.getProperty("java.runtime.version")}", - "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + + ", JVM ${System.getProperty("java.runtime.version")}", + "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" ) override val name = "versions" override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 9fe250d..1443d44 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -45,13 +45,13 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Comment : AbstractCommand() { override val name = COMMAND override val help = listOf( - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, use its label: ", - helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), - "To delete a comment, use its label and a minus sign: ", - helpFormat("${Constants.LINK_CMD}1.1:-") + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, use its label: ", + helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), + "To delete a comment, use its label and a minus sign: ", + helpFormat("${Constants.LINK_CMD}1.1:-") ) override val isOpOnly = false override val isPublic = true @@ -100,12 +100,12 @@ class Comment : AbstractCommand() { } private fun changeAuthor( - channel: String, - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + channel: String, + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { if (event.isChannelOp(channel) && cmd.length > 1) { val comment = entry.getComment(commentIndex) @@ -118,11 +118,11 @@ class Comment : AbstractCommand() { } private fun deleteComment( - channel: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + channel: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { entry.deleteComment(commentIndex) @@ -134,11 +134,11 @@ class Comment : AbstractCommand() { } private fun setComment( - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent ) { entry.setComment(commentIndex, cmd, event.user.nick) event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index fb1a634..fba6b99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -161,8 +161,8 @@ class LinksManager : AbstractCommand() { internal fun fetchTitle(link: String): String { try { val html = Jsoup.connect(link) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") - .get() + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") + .get() val title = html.title() if (title.isNotBlank()) { return title @@ -178,7 +178,7 @@ class LinksManager : AbstractCommand() { return try { val match = entries.links.single { it.link == link } event.sendMessage( - "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) + "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) ) true } catch (ignore: NoSuchElementException) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index e04cd15..ff4278d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -47,16 +47,16 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Posting : AbstractCommand() { override val name = "posting" override val help = listOf( - "Post a URL, by saying it on a line on its own:", - helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", - "To add a title, use its label and a pipe:", - helpFormat("${Constants.LINK_CMD}1:|This is the title"), - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, see: ", - helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") + "Post a URL, by saying it on a line on its own:", + helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", + "To add a title, use its label and a pipe:", + helpFormat("${Constants.LINK_CMD}1:|This is the title"), + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, see: ", + helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") ) override val isOpOnly = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 9071059..1662857 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -44,8 +44,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent 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> [...]") + "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 isOpOnly = false override val isPublic = true diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index ea1ebf8..825e374 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -46,8 +46,8 @@ import org.pircbotx.hooks.types.GenericMessageEvent class View : AbstractCommand() { override val name = VIEW_CMD override val help = listOf( - "To list or search the current URL posts:", - helpFormat("%c $name [<start>] [<query>]") + "To list or search the current URL posts:", + helpFormat("%c $name [<start>] [<query>]") ) override val isOpOnly = false override val isPublic = true @@ -107,9 +107,9 @@ class View : AbstractCommand() { if (sent == MAX_ENTRIES && index < entries.links.size) { event.sendMessage("To view more, try: ") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) - ) + helpFormat( + helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index d29b30d..cfd2c27 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -39,6 +39,7 @@ class NickComparator : Comparator<String>, Serializable { } companion object { + @Suppress("ConstPropertyName") private const val serialVersionUID = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 05ad330..c9ee0f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -58,7 +58,7 @@ class Seen(private val serialObject: String) : AbstractCommand() { override val name = "seen" override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) private val helpOp = help.plus( - arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) + arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) ) override val isOpOnly = false override val isPublic = true @@ -130,12 +130,12 @@ class Seen(private val serialObject: String) : AbstractCommand() { if (isEnabled()) { @Suppress("UNCHECKED_CAST") seenNicks.putAll( - loadSerialData( - serialObject, - TreeMap<String, SeenNick>(), - logger, - "seen nicknames" - ) as TreeMap<String, SeenNick> + loadSerialData( + serialObject, + TreeMap<String, SeenNick>(), + logger, + "seen nicknames" + ) as TreeMap<String, SeenNick> ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index b09cbf4..7924977 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -35,6 +35,7 @@ import java.io.Serializable data class SeenNick(val nick: String, val lastSeen: Long) : Serializable { companion object { + @Suppress("ConstPropertyName") private const val serialVersionUID = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index cdeb943..061ca6a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -66,11 +66,11 @@ class Tell(private val serialObject: String) : AbstractCommand() { override val name = "tell" override val help = listOf( - "To send a message to someone when they join the channel:", - helpFormat("%c $name <nick> <message>"), - "To view queued and sent messages:", - helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' + "To send a message to someone when they join the channel:", + helpFormat("%c $name <nick> <message>"), + "To view queued and sent messages:", + helpFormat("%c $name ${View.VIEW_CMD}"), + "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' ) override val isOpOnly: Boolean = false override val isPublic: Boolean = isEnabled() @@ -89,6 +89,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { args.isBlank() -> { helpResponse(channel, args, event) } + args.startsWith(View.VIEW_CMD) -> { if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { viewAll(event) @@ -96,9 +97,11 @@ class Tell(private val serialObject: String) : AbstractCommand() { viewMessages(event) } } + args.startsWith("$TELL_DEL_KEYWORD ") -> { deleteMessage(channel, args, event) } + else -> { newMessage(channel, args, event) } @@ -123,9 +126,9 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } else { if (messages.removeIf { - it.id == id && - (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) - }) { + it.id == id && + (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) + }) { save() event.sendMessage("The message was deleted from the queue.") } else { @@ -185,7 +188,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (message.sender == nickname) { if (event !is MessageEvent) { event.user.send().message( - "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" + "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" ) message.isReceived = true message.isNotified = true @@ -193,17 +196,17 @@ class Tell(private val serialObject: String) : AbstractCommand() { } } else { event.user.send().message( - "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" + "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" ) message.isReceived = true save() } } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified + && !message.isNotified ) { event.user.send().message( - "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " - + "${message.recipient.bold()} on ${message.receptionDate}" + "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " + + "${message.recipient.bold()} on ${message.receptionDate}" ) message.isNotified = true save() @@ -224,8 +227,8 @@ class Tell(private val serialObject: String) : AbstractCommand() { if (messages.isNotEmpty()) { for (message in messages) { event.sendMessage( - "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + - (if (message.isReceived) "DELIVERED]" else "QUEUED]") + "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + + (if (message.isReceived) "DELIVERED]" else "QUEUED]") ) } } else { @@ -243,13 +246,13 @@ class Tell(private val serialObject: String) : AbstractCommand() { } if (message.isReceived) { event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" ) } else { event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" ) } event.sendMessage(helpFormat(message.message)) @@ -259,9 +262,9 @@ class Tell(private val serialObject: String) : AbstractCommand() { } else { event.sendMessage("To delete one or all delivered messages:") event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) - ) + helpFormat( + helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) + ) ) event.sendMessage(help.last()) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index 33bc1e9..d17fbb5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -39,20 +39,20 @@ import java.time.format.DateTimeFormatter * Tell Message. */ class TellMessage( - /** - * Returns the message's sender. - */ - val sender: String, + /** + * Returns the message's sender. + */ + val sender: String, - /** - * Returns the message's recipient. - */ - val recipient: String, + /** + * Returns the message's recipient. + */ + val recipient: String, - /** - * Returns the message text. - */ - val message: String + /** + * Returns the message text. + */ + val message: String ) : Serializable { /** * Returns the queued date/time. @@ -98,6 +98,7 @@ class TellMessage( } companion object { + @Suppress("ConstPropertyName") private const val serialVersionUID = 2L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index ba22746..e8676ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.entries import net.thauvin.erik.mobibot.Utils.today class Entries( - var channel: String = "", - var ircServer: String = "", - var logsDir: String = "", - var backlogs: String = "" + var channel: String = "", + var ircServer: String = "", + var logsDir: String = "", + var backlogs: String = "" ) { val links = mutableListOf<EntryLink>() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index ff1e423..9c09626 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -43,7 +43,7 @@ object EntriesUtils { */ @JvmStatic fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") + ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") /** * Prints an entry's link for display on the channel. @@ -52,7 +52,7 @@ object EntriesUtils { @JvmOverloads fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") - .append('[').append(entry.nick).append(']') + .append('[').append(entry.nick).append(']') if (isView && entry.comments.isNotEmpty()) { buff.append("[+").append(entry.comments.size).append(']') } @@ -73,7 +73,7 @@ object EntriesUtils { */ @JvmStatic fun printTags(entryIndex: Int, entry: EntryLink): String = - entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") + entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") /** * Builds link label based on its index. e.g: L1 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index 8de54e4..e18d692 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -46,6 +46,7 @@ data class EntryComment(var comment: String, var nick: String) : Serializable { companion object { // Serial version UID + @Suppress("ConstPropertyName") private const val serialVersionUID: Long = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 80ca536..4a69446 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -40,40 +40,40 @@ import java.util.* * The class used to store link entries. */ class EntryLink( - // Link's comments - val comments: MutableList<EntryComment> = mutableListOf(), + // Link's comments + val comments: MutableList<EntryComment> = mutableListOf(), - // Tags/categories - val tags: MutableList<SyndCategory> = mutableListOf(), + // Tags/categories + val tags: MutableList<SyndCategory> = mutableListOf(), - // Channel - var channel: String, + // Channel + var channel: String, - // Creation date - var date: Date = Calendar.getInstance().time, + // Creation date + var date: Date = Calendar.getInstance().time, - // Link's URL - var link: String, + // Link's URL + var link: String, - // Author's login - var login: String = "", + // Author's login + var login: String = "", - // Author's nickname - var nick: String, + // Author's nickname + var nick: String, - // Link's title - var title: String + // Link's title + var title: String ) : Serializable { /** * Creates a new entry. */ constructor( - link: String, - title: String, - nick: String, - login: String, - channel: String, - tags: List<String?> + link: String, + title: String, + nick: String, + login: String, + channel: String, + tags: List<String?> ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { setTags(tags) } @@ -82,12 +82,12 @@ class EntryLink( * Creates a new entry. */ constructor( - link: String, - title: String, - nick: String, - channel: String, - date: Date, - tags: List<SyndCategory> + link: String, + title: String, + nick: String, + channel: String, + date: Date, + tags: List<SyndCategory> ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { this.tags.addAll(tags) } @@ -207,6 +207,7 @@ class EntryLink( companion object { // Serial version UID + @Suppress("ConstPropertyName") private const val serialVersionUID: Long = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index 2d87dbf..f786cb2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -55,24 +55,24 @@ class FeedsManager private constructor() { private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) // The file containing the current entries. - private const val currentXml = "current.xml" + private const val CURRENT_XML = "current.xml" // The .xml extension. - private const val dotXml = ".xml" + private const val DOT_XML = ".xml" /** * Loads the current feed. */ @JvmStatic @Throws(IOException::class, FeedException::class) - fun loadFeed(entries: Entries, currentFile: String = currentXml): String { + fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): 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 + Files.newInputStream(xml), StandardCharsets.UTF_8 ).use { reader -> val feed = input.build(reader) pubDate = feed.publishedDate.toIsoLocalDate() @@ -81,12 +81,12 @@ class FeedsManager private constructor() { 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 + 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/>")) { @@ -110,7 +110,7 @@ class FeedsManager private constructor() { * Saves the feeds. */ @JvmStatic - fun saveFeed(entries: Entries, currentFile: String = currentXml) { + fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) { if (logger.isDebugEnabled) logger.debug("Saving the feeds...") if (entries.logsDir.isNotBlank()) { try { @@ -119,7 +119,7 @@ class FeedsManager private constructor() { val items: MutableList<SyndEntry> = mutableListOf() var item: SyndEntry OutputStreamWriter( - Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 + Files.newOutputStream(Paths.get("${entries.logsDir}${currentFile}")), StandardCharsets.UTF_8 ).use { fw -> with(rss) { feedType = "rss_2.0" @@ -134,13 +134,13 @@ class FeedsManager private constructor() { 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>") + .append(nick) + .append("</b> on <a href=\"irc://") + .append(entries.ircServer).append('/') + .append(channel) + .append("\"><b>") + .append(channel) + .append("</b></a>") if (comments.isNotEmpty()) { buff.append(" <br/><br/>") for (j in comments.indices) { @@ -165,11 +165,11 @@ class FeedsManager private constructor() { output.output(rss, fw) } OutputStreamWriter( - Files.newOutputStream( - Paths.get( - entries.logsDir + today() + dotXml - ) - ), StandardCharsets.UTF_8 + Files.newOutputStream( + Paths.get( + entries.logsDir + today() + DOT_XML + ) + ), StandardCharsets.UTF_8 ).use { fw -> output.output(rss, fw) } } catch (e: FeedException) { if (logger.isWarnEnabled) logger.warn("Unable to generate the entries feed.", e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index e1e86df..bd92332 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -56,8 +56,8 @@ class ChatGpt : AbstractModule() { if (args.isNotBlank()) { try { val answer = chat( - args.trim(), properties[API_KEY_PROP], - properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() + args.trim(), properties[API_KEY_PROP], + properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() ) if (answer.isNotBlank()) { event.sendMessage(WordUtils.wrap(answer, 400)) @@ -107,13 +107,13 @@ class ChatGpt : AbstractModule() { if (!apiKey.isNullOrEmpty()) { val prompt = JSONWriter.valueToString("Q:$query\nA:") val request = HttpRequest.newBuilder() - .uri(URI.create(API_URL)) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .header("User-Agent", Constants.USER_AGENT) - .POST( - HttpRequest.BodyPublishers.ofString( - """{ + .uri(URI.create(API_URL)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .header("User-Agent", Constants.USER_AGENT) + .POST( + HttpRequest.BodyPublishers.ofString( + """{ "model": "text-davinci-003", "prompt": $prompt, "temperature": 0, @@ -122,9 +122,9 @@ class ChatGpt : AbstractModule() { "frequency_penalty": 0, "presence_penalty": 0 }""".trimIndent() - ) ) - .build() + ) + .build() try { val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) if (response.statusCode() == 200) { @@ -134,16 +134,16 @@ class ChatGpt : AbstractModule() { return choices.getJSONObject(0).getString("text").trim() } catch (e: JSONException) { throw ModuleException( - "$CHATGPT_CMD($query): JSON", - "A JSON error has occurred while conversing with $CHATGPT_NAME.", - e + "$CHATGPT_CMD($query): JSON", + "A JSON error has occurred while conversing with $CHATGPT_NAME.", + e ) } } else { if (response.statusCode() == 429) { throw ModuleException( - "$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later." + "$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later." ) } else { throw IOException("HTTP Status Code: " + response.statusCode()) @@ -151,9 +151,9 @@ class ChatGpt : AbstractModule() { } } catch (e: IOException) { throw ModuleException( - "$CHATGPT_CMD($query): IO", - "An IO error has occurred while conversing with $CHATGPT_NAME.", - e + "$CHATGPT_CMD($query): IO", + "An IO error has occurred while conversing with $CHATGPT_NAME.", + e ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 5136504..d14056e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -134,9 +134,9 @@ class CryptoPrices : AbstractModule() { } } catch (e: CryptoException) { throw ModuleException( - "loadCurrencies(): CE", - "An error has occurred while retrieving the currencies table.", - e + "loadCurrencies(): CE", + "An error has occurred while retrieving the currencies table.", + e ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 3aad379..da0efd8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -78,6 +78,7 @@ class CurrencyConverter : AbstractModule() { SYMBOLS.isEmpty() -> { event.respond(EMPTY_SYMBOLS_TABLE) } + args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> { val msg = convertCurrency(properties[API_KEY_PROP], args) event.respond(msg.msg) @@ -85,10 +86,12 @@ class CurrencyConverter : AbstractModule() { helpResponse(event) } } + args.contains(CODES_KEYWORD) -> { event.sendMessage("The supported currency codes are:") event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) } + else -> { helpResponse(event) } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index b0e911c..f426d1e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -65,10 +65,10 @@ class GoogleSearch : AbstractModule() { if (args.isNotBlank()) { try { val results = searchGoogle( - args, - properties[API_KEY_PROP], - properties[CSE_KEY_PROP], - event.user.nick + args, + properties[API_KEY_PROP], + properties[CSE_KEY_PROP], + event.user.nick ) for (msg in results) { if (msg.isError) { @@ -104,23 +104,23 @@ class GoogleSearch : AbstractModule() { @JvmStatic @Throws(ModuleException::class) fun searchGoogle( - query: String, - apiKey: String?, - cseKey: String?, - quotaUser: String = ReleaseInfo.PROJECT + query: String, + apiKey: String?, + cseKey: String?, + quotaUser: String = ReleaseInfo.PROJECT ): List<Message> { if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { throw ModuleException( - "${GoogleSearch::class.java.name} is disabled.", - "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." + "${GoogleSearch::class.java.name} is disabled.", + "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." ) } val results = mutableListOf<Message>() if (query.isNotBlank()) { try { val url = URL( - "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" + "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + + ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" ) val json = JSONObject(url.reader().body) if (json.has("items")) { @@ -141,9 +141,9 @@ class GoogleSearch : AbstractModule() { throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) } catch (e: JSONException) { throw ModuleException( - "searchGoogle($query): JSON", - "A JSON error has occurred searching Google.", - e + "searchGoogle($query): JSON", + "A JSON error has occurred searching Google.", + e ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index fc85226..9ab2ead 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -55,9 +55,9 @@ class Lookup : AbstractModule() { event.respondWith(nslookup(args).prependIndent()) } catch (ignore: UnknownHostException) { if (args.matches( - ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") - .toRegex() - ) + ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + .toRegex() + ) ) { try { val lines = whois(args) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 4cf2fe9..3be3a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -65,7 +65,7 @@ class Mastodon : SocialModule() { private fun formatTags(entry: EntryLink): String { return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } - .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } + .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } } /** @@ -74,11 +74,11 @@ class Mastodon : SocialModule() { @Throws(ModuleException::class) override fun post(message: String, isDm: Boolean): String { return toot( - apiKey = properties[ACCESS_TOKEN_PROP], - instance = properties[INSTANCE_PROP], - handle = handle, - message = message, - isDm = isDm + apiKey = properties[ACCESS_TOKEN_PROP], + instance = properties[INSTANCE_PROP], + handle = handle, + message = message, + isDm = isDm ) } @@ -99,21 +99,21 @@ class Mastodon : SocialModule() { @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { val request = HttpRequest.newBuilder() - .uri(URI.create("https://$instance/api/v1/statuses")) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .POST( - HttpRequest.BodyPublishers.ofString( - JSONWriter.valueToString( - if (isDm) { - mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") - } else { - mapOf("status" to message) - } - ) - ) + .uri(URI.create("https://$instance/api/v1/statuses")) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .POST( + HttpRequest.BodyPublishers.ofString( + JSONWriter.valueToString( + if (isDm) { + mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") + } else { + mapOf("status" to message) + } + ) ) - .build() + ) + .build() try { val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) if (response.statusCode() == 200) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 017efd4..a7416c2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -34,11 +34,12 @@ package net.thauvin.erik.mobibot.modules * The `ModuleException` class. */ class ModuleException @JvmOverloads constructor( - val debugMessage: String, - message: String? = null, - cause: Throwable? = null + val debugMessage: String, + message: String? = null, + cause: Throwable? = null ) : Exception(message, cause) { companion object { + @Suppress("ConstPropertyName") private const val serialVersionUID = 1L } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index de5c1e8..944dbc1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -50,18 +50,18 @@ class Ping : AbstractModule() { */ @JvmField val PINGS = listOf( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!" + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!" ) @JvmStatic diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 359956a..a299d8d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -52,10 +52,10 @@ class RockPaperScissors : AbstractModule() { with(help) { add("To play Rock Paper Scissors:") add( - helpFormat( - "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" - + " | ${Hands.SCISSORS.name.lowercase()}" - ) + helpFormat( + "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" + + " | ${Hands.SCISSORS.name.lowercase()}" + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 661a4e8..dcae5e7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -132,8 +132,8 @@ class StockQuote : AbstractModule() { fun getQuote(symbol: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${StockQuote::class.java.name} is disabled.", - "${STOCK_CMD.capitalise()} is disabled. The API key is missing." + "${StockQuote::class.java.name} is disabled.", + "${STOCK_CMD.capitalise()} is disabled. The API key is missing." ) } val messages = mutableListOf<Message>() @@ -144,8 +144,8 @@ class StockQuote : AbstractModule() { with(messages) { // Search for symbol/keywords response = URL( - "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" - + apiKey.encodeUrl() + "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + + apiKey.encodeUrl() ).reader().body var json = getJsonResponse(response, debugMessage) val symbols = json.getJSONArray("bestMatches") @@ -156,9 +156,9 @@ class StockQuote : AbstractModule() { // Get quote for symbol response = URL( - "${API_URL}GLOBAL_QUOTE&symbol=" - + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" - + apiKey.encodeUrl() + "${API_URL}GLOBAL_QUOTE&symbol=" + + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + + apiKey.encodeUrl() ).reader().body json = getJsonResponse(response, debugMessage) val quote = json.getJSONObject("Global Quote") @@ -167,50 +167,50 @@ class StockQuote : AbstractModule() { } else { add( - PublicMessage( - "Symbol: " + quote.getString("01. symbol").unescapeXml() - + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' - ) + PublicMessage( + "Symbol: " + quote.getString("01. symbol").unescapeXml() + + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' + ) ) val pad = 10 add( - PublicMessage( - "Price:".padEnd(pad).prependIndent() - + quote.getString("05. price").unescapeXml() - ) + PublicMessage( + "Price:".padEnd(pad).prependIndent() + + quote.getString("05. price").unescapeXml() + ) ) add( - PublicMessage( - "Previous:".padEnd(pad).prependIndent() - + quote.getString("08. previous close").unescapeXml() - ) + PublicMessage( + "Previous:".padEnd(pad).prependIndent() + + quote.getString("08. previous close").unescapeXml() + ) ) 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" + "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() - + quote.getString(it.second).unescapeXml() - ) + NoticeMessage( + "${it.first}:".padEnd(pad).prependIndent() + + quote.getString(it.second).unescapeXml() + ) ) } add( - NoticeMessage( - "Change:".padEnd(pad).prependIndent() - + quote.getString("09. change").unescapeXml() - + " [" + quote.getString("10. change percent").unescapeXml() + ']' - ) + NoticeMessage( + "Change:".padEnd(pad).prependIndent() + + quote.getString("09. change").unescapeXml() + + " [" + quote.getString("10. change percent").unescapeXml() + ']' + ) ) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 533cce6..80a06fa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -120,8 +120,8 @@ class Weather2 : AbstractModule() { fun getWeather(query: String, apiKey: String?): List<Message> { if (apiKey.isNullOrBlank()) { throw ModuleException( - "${Weather2::class.java.name} is disabled.", - "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." + "${Weather2::class.java.name} is disabled.", + "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." ) } val owm = OWM(apiKey) @@ -145,10 +145,10 @@ class Weather2 : AbstractModule() { } if (cwd.hasCityName()) { messages.add( - PublicMessage( - "City: ${cwd.cityName}, " + - country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" - ) + PublicMessage( + "City: ${cwd.cityName}, " + + country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" + ) ) cwd.mainData?.let { with(it) { @@ -181,8 +181,8 @@ class Weather2 : AbstractModule() { for (w in it) { w?.let { condition.append(' ') - .append(w.getDescription().capitalise()) - .append('.') + .append(w.getDescription().capitalise()) + .append('.') } } messages.add(NoticeMessage(condition.toString())) @@ -192,15 +192,15 @@ class Weather2 : AbstractModule() { cwd.cityId?.let { if (it > 0) { messages.add( - NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) + NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) ) } else { messages.add( - NoticeMessage( - "https://openweathermap.org/find?q=" - + "$city,${code.uppercase()}".encodeUrl(), - Colors.GREEN - ) + NoticeMessage( + "https://openweathermap.org/find?q=" + + "$city,${code.uppercase()}".encodeUrl(), + Colors.GREEN + ) ) } } @@ -209,9 +209,9 @@ class Weather2 : AbstractModule() { } catch (e: APIException) { if (e.code == 404) { throw ModuleException( - "getWeather($query): API ${e.code}", - "The requested city was not found.", - e + "getWeather($query): API ${e.code}", + "The requested city was not found.", + e ) } else { throw ModuleException("getWeather($query): API ${e.code}", e.message, e) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 049807a..a72efab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -60,15 +60,15 @@ class WolframAlpha : AbstractModule() { try { val query = args.trim().split("units=", limit = 2, ignoreCase = true) event.sendMessage( - queryWolfram( - query[0].trim(), - units = if (query.size == 2) { - getUnits(query[1].trim()) - } else { - getUnits(properties[UNITS_PROP]) - }, - appId = properties[APPID_KEY_PROP] - ) + queryWolfram( + query[0].trim(), + units = if (query.size == 2) { + getUnits(query[1].trim()) + } else { + getUnits(properties[UNITS_PROP]) + }, + appId = properties[APPID_KEY_PROP] + ) ) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) @@ -111,15 +111,15 @@ class WolframAlpha : AbstractModule() { return urlReader.body } else { throw ModuleException( - "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", - urlReader.body.ifEmpty { - "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" - } + "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", + urlReader.body.ifEmpty { + "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" + } ) } } catch (ioe: IOException) { throw ModuleException( - "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe + "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe ) } } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index debbe98..18072bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -322,7 +322,7 @@ class WorldTime : AbstractModule() { put("ZULU", "Zulu") put("ZW", "Africa/Harare") ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } - .forEach { tz -> put(tz, tz) } + .forEach { tz -> put(tz, tz) } } // The Time command @@ -336,7 +336,7 @@ class WorldTime : AbstractModule() { // Date/Time Format private var dtf = - DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") + DateTimeFormatter.ofPattern("'The time is ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") /** * Returns the current Internet (beat) Time. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 2695a3b..0607936 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -34,4 +34,4 @@ package net.thauvin.erik.mobibot.msg * The `ErrorMessage` class. */ class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isError = true) + Message(msg, color, isError = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 3b4be49..23a33b9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -36,11 +36,11 @@ import net.thauvin.erik.semver.Constants * The `Message` class. */ open class Message @JvmOverloads constructor( - var msg: String, - var color: String = DEFAULT_COLOR, - var isNotice: Boolean = false, - isError: Boolean = false, - var isPrivate: Boolean = false + var msg: String, + var color: String = DEFAULT_COLOR, + var isNotice: Boolean = false, + isError: Boolean = false, + var isPrivate: Boolean = false ) { companion object { var DEFAULT_COLOR = Constants.EMPTY diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index cd6721c..037d504 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -34,5 +34,5 @@ package net.thauvin.erik.mobibot.msg * The `NoticeMessage` class. */ class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isNotice = true) + Message(msg, color, isNotice = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 3033d1a..b424fdf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -34,4 +34,4 @@ package net.thauvin.erik.mobibot.msg * The `PrivateMessage` class. */ class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isPrivate = true) + Message(msg, color, isPrivate = true) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index 32e670a..b594670 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -76,8 +76,8 @@ abstract class SocialModule : AbstractModule() { post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) } catch (e: ModuleException) { if (logger.isWarnEnabled) logger.warn( - "Failed to post entry ${index.toLinkLabel()} on $name.", - e + "Failed to post entry ${index.toLinkLabel()} on $name.", + e ) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 5a8a638..3241bf0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -76,11 +76,11 @@ class AddonsTest { assertThat(addons.names.ops, "names.ops").containsExactly("cycle") assertThat(addons.names.commands, "names.command").containsExactly( - "joke", - "rock", - "paper", - "scissors", - "ignore" + "joke", + "rock", + "paper", + "scissors", + "ignore" ) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index be2deb3..a3994ec 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -46,9 +46,9 @@ object ExceptionSanitizer { with(this) { if (!cause?.message.isNullOrBlank()) { return ModuleException( - debugMessage, - cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), - this + debugMessage, + cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), + this ) } else if (!message.isNullOrBlank()) { return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 4ebb53c..87617e8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -68,7 +68,7 @@ class PinboardTest : LocalProperties() { private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { val response = - URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body + URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body matches.forEach { if (!response.contains(it)) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 8ddb013..7a3f5d2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -73,7 +73,7 @@ import java.util.* */ class UtilsTest { private val ascii = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" private val cal = Calendar.getInstance() private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) private val test = "This is a test." @@ -89,7 +89,7 @@ class UtilsTest { val sep = '/' val url = "https://erik.thauvin.net" assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") - .isEqualTo(dir + File.separatorChar) + .isEqualTo(dir + File.separatorChar) assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") } @@ -115,24 +115,24 @@ class UtilsTest { fun textCapitaliseWords() { assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") assertThat("Already Capitalized".capitalizeWords(), "already capitalized") - .isEqualTo("Already Capitalized") + .isEqualTo("Already Capitalized") assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") } @Test fun testColorize() { assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( - Colors.REVERSE + ascii + Colors.REVERSE + Colors.REVERSE + ascii + Colors.REVERSE ) assertThat(ascii.colorize(Colors.RED), "red.colorize()") - .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + .isEqualTo(Colors.RED + ascii + Colors.NORMAL) assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") - .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") - .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) + .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) } @Test @@ -164,19 +164,19 @@ class UtilsTest { fun testHelpCmdSyntax() { val bot = "mobibot" assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") - .isEqualTo("$bot: $test $bot $test") + .isEqualTo("$bot: $test $bot $test") assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") - .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") } @Test fun testHelpFormat() { assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") - .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") + .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") - .isEqualTo(test.prependIndent()) + .isEqualTo(test.prependIndent()) assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") - .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) + .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) } @@ -218,15 +218,15 @@ class UtilsTest { val search = arrayOf("one", "two", "three") val replace = arrayOf("1", "2", "3") assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") - .isEqualTo(replace.joinToString(",")) + .isEqualTo(replace.joinToString(",")) assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") - .isEqualTo(test.replace("t", "").replace("e", "E")) + .isEqualTo(test.replace("t", "").replace("e", "E")) assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") - .isEqualTo(test) + .isEqualTo(test) } @Test @@ -258,7 +258,7 @@ class UtilsTest { @Test fun testUnescapeXml() { assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( - "<a name=\"test & ''\">" + "<a name=\"test & ''\">" ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 1f28049..265009b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -40,14 +40,14 @@ class InfoTest { @Test(groups = ["commands"]) fun testToUptime() { assertThat( - 547800300076L.toUptime(), - "upTime(full)" + 547800300076L.toUptime(), + "upTime(full)" ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") assertThat( - 1320300000L.toUptime(), - "upTime(weeks days hours minutes)" + 1320300000L.toUptime(), + "upTime(weeks days hours minutes)" ).isEqualTo("2 weeks 1 day 6 hours 45 minutes") assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 5f1a690..f1fbe11 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -48,13 +48,13 @@ class RecapTest { assertThat(Recap.recaps, "Recap.recaps").all { size().isEqualTo(Recap.MAX_RECAPS) prop(MutableList<String>::first) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) prop(MutableList<String>::last) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) } Recap.storeRecap("sender", "test action", true) assertThat(Recap.recaps.last()) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index 8fcbd8b..8e49b5e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -47,8 +47,8 @@ class LinksManagerTest { fun fetchTitle() { assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") assertThat( - linksManager.fetchTitle("https://www.google.com/foo"), - "fetchTitle(Foo)" + linksManager.fetchTitle("https://www.google.com/foo"), + "fetchTitle(Foo)" ).isEqualTo(Constants.NO_TITLE) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index 0853a9d..c28090d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -45,14 +45,14 @@ class ViewTest { for (i in 1..10) { LinksManager.entries.links.add( - EntryLink( - "https://www.example.com/$i", - "Example $i", - "nick$i", - "login$i", - "#channel", - emptyList() - ) + EntryLink( + "https://www.example.com/$i", + "Example $i", + "nick$i", + "login$i", + "#channel", + emptyList() + ) ) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index a09ebb9..6eef16e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -46,14 +46,14 @@ class EntriesUtilsTest { private val links = buildList { for (i in 0..5) { add( - EntryLink( - "https://www.mobitopia.org/$i", - "Mobitopia$i", - "Skynx$i", - "JimH$i", - "#mobitopia$i", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") - ) + EntryLink( + "https://www.mobitopia.org/$i", + "Mobitopia$i", + "Skynx$i", + "JimH$i", + "#mobitopia$i", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) ) } } @@ -67,7 +67,7 @@ class EntriesUtilsTest { fun printLinkTest() { for (i in links.indices) { assertThat( - printLink(i - 1, links[i]), "link $i" + printLink(i - 1, links[i]), "link $i" ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") } @@ -79,7 +79,7 @@ class EntriesUtilsTest { fun printTagsTest() { for (i in links.indices) { assertThat( - printTags(i - 1, links[i]), "tag $i" + printTags(i - 1, links[i]), "tag $i" ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 4c20525..ab3feee 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -48,8 +48,8 @@ import java.util.* */ class EntryLinkTest { private val entryLink = EntryLink( - "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") ) @Test(groups = ["entries"]) @@ -117,12 +117,12 @@ class EntryLinkTest { entryLink.setTags("+mobitopia") entryLink.setTags("-mobitopia") assertThat( - entryLink.formatTags(","), - "formatTags(',')" + entryLink.formatTags(","), + "formatTags(',')" ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") entryLink.setTags("-tag4 tag5") assertThat( - entryLink.formatTags(" ", ","), "formatTag(' ',',')" + entryLink.formatTags(" ", ","), "formatTag(' ',',')" ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") val size = entryLink.tags.size entryLink.setTags("") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index fa50fcb..66fb98d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -42,21 +42,21 @@ class ChatGptTest : LocalProperties() { @Test(groups = ["modules"]) fun testApiKey() { assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } - .isInstanceOf(ModuleException::class.java) - .hasNoCause() + .isInstanceOf(ModuleException::class.java) + .hasNoCause() } @Test(groups = ["modules", "no-ci"]) fun testChat() { val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) + ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") assertThat( - ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) + ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ).contains("URLEncoder") assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } - .isInstanceOf(ModuleException::class.java) + .isInstanceOf(ModuleException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 4ee9668..5375784 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -49,7 +49,7 @@ import org.testng.annotations.Test /** * The `CurrencyConvertTest` class. */ -class CurrencyConverterTest: LocalProperties() { +class CurrencyConverterTest : LocalProperties() { @BeforeClass @Throws(ModuleException::class) @@ -62,22 +62,22 @@ class CurrencyConverterTest: LocalProperties() { fun testConvertCurrency() { val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) assertThat( - convertCurrency(apiKey,"100 USD to EUR").msg, - "convertCurrency(100 USD to EUR)" + convertCurrency(apiKey, "100 USD to EUR").msg, + "convertCurrency(100 USD to EUR)" ).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex()) assertThat( - convertCurrency(apiKey,"1 USD to GBP").msg, - "convertCurrency(1 USD to BGP)" + convertCurrency(apiKey, "1 USD to GBP").msg, + "convertCurrency(1 USD to BGP)" ).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex()) assertThat( - convertCurrency(apiKey,"100,000.00 CAD to USD").msg, - "convertCurrency(100,000.00 GBP to USD)" + convertCurrency(apiKey, "100,000.00 CAD to USD").msg, + "convertCurrency(100,000.00 GBP to USD)" ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex()) - assertThat(convertCurrency(apiKey,"100 USD to USD"), "convertCurrency(100 USD to USD)").all { + assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all { prop(Message::msg).contains("You're kidding, right?") isInstanceOf(PublicMessage::class.java) } - assertThat(convertCurrency(apiKey,"100 USD"), "convertCurrency(100 USD)").all { + assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all { prop(Message::msg).contains("Invalid query.") isInstanceOf(ErrorMessage::class.java) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 4225e3b..cdc04f0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -42,12 +42,12 @@ class DiceTest { fun testRoll() { assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") assertThat(Dice.roll(2, 1), "roll(2d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") + .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") assertThat(Dice.roll(5, 1), "roll(5d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") + .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") assertThat(Dice.roll(2, 6), "roll(2d6)") - .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) + .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) assertThat(Dice.roll(3, 7), "roll(3d7)") - .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) + .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 640a721..fa50f8c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -48,19 +48,19 @@ class GoogleSearchTest : LocalProperties() { @Test(groups = ["modules"]) fun testAPIKeys() { assertThat( - searchGoogle("", "apikey", "cssKey").first(), - "searchGoogle(empty)" + searchGoogle("", "apikey", "cssKey").first(), + "searchGoogle(empty)" ).isInstanceOf(ErrorMessage::class.java) assertFailure { searchGoogle("test", "", "apiKey") } - .isInstanceOf(ModuleException::class.java).hasNoCause() + .isInstanceOf(ModuleException::class.java).hasNoCause() assertFailure { searchGoogle("test", "apiKey", "") } - .isInstanceOf(ModuleException::class.java).hasNoCause() + .isInstanceOf(ModuleException::class.java).hasNoCause() assertFailure { searchGoogle("test", "apiKey", "cssKey") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("API key not valid. Please pass a valid API key.") + .isInstanceOf(ModuleException::class.java) + .hasMessage("API key not valid. Please pass a valid API key.") } @Test(groups = ["no-ci", "modules"]) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 84f9375..34f778a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -42,13 +42,13 @@ class MastodonTest : LocalProperties() { fun testToot() { val msg = "Testing Mastodon API from ${getHostName()}" assertThat( - toot( - getProperty(Mastodon.ACCESS_TOKEN_PROP), - getProperty(Mastodon.INSTANCE_PROP), - getProperty(Mastodon.HANDLE_PROP), - msg, - true - ) + toot( + getProperty(Mastodon.ACCESS_TOKEN_PROP), + getProperty(Mastodon.INSTANCE_PROP), + getProperty(Mastodon.HANDLE_PROP), + msg, + true + ) ).contains(msg) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index b36285b..db68280 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -44,58 +44,58 @@ import java.lang.reflect.Method */ class ModuleExceptionTest { companion object { - const val debugMessage = "debugMessage" - const val message = "message" + const val DEBUG_MESSAGE = "debugMessage" + const val MESSAGE = "message" } @DataProvider(name = "dp") fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array<Array<Any>> { return arrayOf( - arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(debugMessage, message, IOException("URL http://foobar.com?"))), - arrayOf(ModuleException(debugMessage, message)) + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))), + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))), + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE)) ) } @Test(dataProvider = "dp") fun testGetDebugMessage(e: ModuleException) { - assertThat(e::debugMessage).isEqualTo(debugMessage) + assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE) } @Test(dataProvider = "dp") fun testGetMessage(e: ModuleException) { - assertThat(e).hasMessage(message) + assertThat(e).hasMessage(MESSAGE) } @Test(groups = ["modules"]) fun testSanitizeMessage() { val apiKey = "1234567890" - var e = ModuleException(debugMessage, message, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) + var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) assertThat( - e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" + e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" ).isNotNull().all { contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") doesNotContain(apiKey, "me") } - e = ModuleException(debugMessage, message, null) - assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(message) + e = ModuleException(DEBUG_MESSAGE, MESSAGE, null) + assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE) - e = ModuleException(debugMessage, message, IOException()) - assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(message) + e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException()) + assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE) - e = ModuleException(debugMessage, apiKey) + e = ModuleException(DEBUG_MESSAGE, apiKey) assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() - .doesNotContain(apiKey) + .doesNotContain(apiKey) val msg: String? = null - e = ModuleException(debugMessage, msg, IOException(msg)) + e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg)) assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull() - e = ModuleException(debugMessage, msg, IOException("foo is $apiKey")) + e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey")) assertThat( - e.sanitize(" ", apiKey, "foo").message, - "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" + e.sanitize(" ", apiKey, "foo").message, + "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" ).isNotNull().all { doesNotContain(apiKey) endsWith("xxx is xxxxxxxxxx") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 17e5b92..aff4188 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -60,7 +60,7 @@ class StockQuoteTest : LocalProperties() { assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) - .matches(buildMatch("Previous").toRegex()) + .matches(buildMatch("Previous").toRegex()) assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) symbol = "blahfoo" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 281d8af..ae1722d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -45,11 +45,11 @@ class WolframAlphaTest : LocalProperties() { @Test(groups = ["modules"]) fun testAppId() { assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("Error 1: Invalid appid") + .isInstanceOf(ModuleException::class.java) + .hasMessage("Error 1: Invalid appid") assertFailure { queryWolfram("1 gallon to liter", appId = "") } - .isInstanceOf(ModuleException::class.java) + .isInstanceOf(ModuleException::class.java) } @Test(groups = ["modules", "no-ci"]) @@ -62,8 +62,8 @@ class WolframAlphaTest : LocalProperties() { query = "SFO to LAX" assertThat( - queryWolfram(query, WolframAlpha.METRIC, apiKey), - "queryWolfram($query)" + queryWolfram(query, WolframAlpha.METRIC, apiKey), + "queryWolfram($query)" ).contains("kilometers") } catch (e: ModuleException) { // Avoid displaying api key in CI logs diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 29f5589..3602a27 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -49,9 +49,9 @@ class WordTimeTest { @Test(groups = ["modules"]) fun testTime() { assertThat(time(), "time()").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() + ("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(""), "time()").endsWith("Los Angeles".bold()) assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) From 1cd7c5a79e1b38d5f582a051bd2e20538b1d9edf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Wed, 1 Nov 2023 22:10:50 -0700 Subject: [PATCH 752/858] Upgraded to Kotlin 1.9.20 --- .idea/kotlinc.xml | 2 +- build.gradle | 8 ++++---- config/detekt/baseline.xml | 17 ++++------------- version.properties | 6 +++--- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index f8467b4..e805548 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="KotlinJpsPluginSettings"> - <option name="version" value="1.9.10" /> + <option name="version" value="1.9.20" /> </component> </project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9fb85c6..6507e20 100644 --- a/build.gradle +++ b/build.gradle @@ -7,11 +7,11 @@ plugins { id 'application' id 'com.github.ben-manes.versions' version '0.49.0' id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.23.1' + id 'io.gitlab.arturbosch.detekt' version '1.23.3' id 'java' id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.9.10' - id 'org.jetbrains.kotlin.kapt' version '1.9.10' + id 'org.jetbrains.kotlin.jvm' version '1.9.20' + id 'org.jetbrains.kotlin.kapt' version '1.9.20' id 'org.jetbrains.kotlinx.kover' version '0.7.4' id 'org.sonarqube' version '4.4.1.3373' id 'pmd' @@ -55,7 +55,7 @@ dependencies { // Commons (mostly for PircBotX) implementation 'org.apache.commons:commons-lang3:3.13.0' - implementation 'org.apache.commons:commons-text:1.10.0' + implementation 'org.apache.commons:commons-text:1.11.0' implementation 'commons-codec:commons-codec:1.16.0' implementation 'commons-net:commons-net:3.10.0' diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 9f157cf..5c6be73 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -2,9 +2,9 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> - <ID>CyclomaticComplexMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>CyclomaticComplexMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID> <ID>CyclomaticComplexMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID> - <ID>LongMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>LongMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID> <ID>LongMethod:Mobibot.kt$Mobibot.Companion$@JvmStatic @Throws(Exception::class) 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> @@ -46,15 +46,6 @@ <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>MaxLineLength:DiceTest.kt$DiceTest$.</ID> - <ID>MaxLineLength:Lookup.kt$Lookup$("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")</ID> - <ID>MaxLineLength:Mastodon.kt$Mastodon.Companion$mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct")</ID> - <ID>MaxLineLength:Mobibot.kt$Mobibot$helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent)</ID> - <ID>MaxLineLength:PinboardTest.kt$PinboardTest$URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body</ID> - <ID>MaxLineLength:StockQuote.kt$StockQuote.Companion$+</ID> - <ID>MaxLineLength:Utils.kt$Utils$list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = "")</ID> - <ID>MaxLineLength:View.kt$View$helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent)</ID> - <ID>MaxLineLength:Weather2.kt$Weather2.Companion$country.name.replace('_', ' ').capitalizeWords()</ID> <ID>NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean</ID> <ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID> <ID>NestedBlockDepth:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String</ID> @@ -62,8 +53,8 @@ <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?)</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>)</ID> - <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID> - <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> + <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String</ID> + <ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID> <ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message></ID> <ID>NestedBlockDepth:LinksManager.kt$LinksManager$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID> diff --git a/version.properties b/version.properties index c73da9e..9c446af 100644 --- a/version.properties +++ b/version.properties @@ -1,9 +1,9 @@ #Generated by the Semver Plugin for Gradle -#Thu Oct 26 20:43:39 PDT 2023 -version.buildmeta=20231026204339 +#Wed Nov 01 22:09:32 PDT 2023 +version.buildmeta=20231101220932 version.major=0 version.minor=8 version.patch=0 version.prerelease=rc version.project=mobibot -version.semver=0.8.0-rc+20231026204339 +version.semver=0.8.0-rc+20231101220932 From c68c25aa9504a4217cb9495b8086ea9d795a5aa6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" <erik@thauvin.net> Date: Fri, 10 Nov 2023 23:43:20 -0800 Subject: [PATCH 753/858] Moved from Gradle to bld --- .circleci/config.yml | 42 +- .github/workflows/gradle.yml | 48 +- .gitignore | 150 +++--- .gitlab-ci.yml | 31 +- .idea/app.iml | 30 ++ .idea/bld.iml | 14 + .idea/codeStyles/Project.xml | 298 ------------ .idea/codeStyles/codeStyleConfig.xml | 6 - .idea/compiler.xml | 6 - ...{Erik_s_Copyright_Notice.xml => BSD_3.xml} | 4 +- .idea/copyright/profiles_settings.xml | 2 +- .idea/inspectionProfiles/Project_Default.xml | 64 --- .idea/jarRepositories.xml | 45 -- .idea/kotlinc.xml | 6 - .idea/libraries/bld.xml | 17 + .idea/libraries/compile.xml | 13 + .idea/libraries/runtime.xml | 14 + .idea/libraries/test.xml | 14 + .idea/misc.xml | 18 +- .idea/modules.xml | 9 + .idea/runConfigurations/Run Tests.xml | 9 + .vscode/launch.json | 11 + .vscode/settings.json | 15 + README.md | 6 +- bin/main/log4j2.xml | 69 +++ bin/main/net/thauvin/erik/mobibot/Addons.kt | 190 ++++++++ .../net/thauvin/erik/mobibot/Constants.kt | 102 ++++ .../net/thauvin/erik/mobibot/FeedReader.kt | 92 ++++ bin/main/net/thauvin/erik/mobibot/Mobibot.kt | 421 +++++++++++++++++ bin/main/net/thauvin/erik/mobibot/Pinboard.kt | 113 +++++ bin/main/net/thauvin/erik/mobibot/Utils.kt | 439 ++++++++++++++++++ .../erik/mobibot/commands/AbstractCommand.kt | 79 ++++ .../erik/mobibot/commands/ChannelFeed.kt | 62 +++ .../thauvin/erik/mobibot/commands/Cycle.kt | 66 +++ .../net/thauvin/erik/mobibot/commands/Die.kt | 62 +++ .../thauvin/erik/mobibot/commands/Ignore.kt | 147 ++++++ .../net/thauvin/erik/mobibot/commands/Info.kt | 124 +++++ .../net/thauvin/erik/mobibot/commands/Me.kt | 51 ++ .../thauvin/erik/mobibot/commands/Modules.kt | 63 +++ .../net/thauvin/erik/mobibot/commands/Msg.kt | 60 +++ .../net/thauvin/erik/mobibot/commands/Nick.kt | 51 ++ .../thauvin/erik/mobibot/commands/Recap.kt | 81 ++++ .../net/thauvin/erik/mobibot/commands/Say.kt | 51 ++ .../thauvin/erik/mobibot/commands/Users.kt | 50 ++ .../thauvin/erik/mobibot/commands/Versions.kt | 59 +++ .../erik/mobibot/commands/links/Comment.kt | 151 ++++++ .../mobibot/commands/links/LinksManager.kt | 207 +++++++++ .../erik/mobibot/commands/links/Posting.kt | 164 +++++++ .../erik/mobibot/commands/links/Tags.kt | 87 ++++ .../erik/mobibot/commands/links/View.kt | 120 +++++ .../mobibot/commands/seen/NickComparator.kt | 45 ++ .../erik/mobibot/commands/seen/Seen.kt | 150 ++++++ .../erik/mobibot/commands/seen/SeenNick.kt | 41 ++ .../erik/mobibot/commands/tell/Tell.kt | 306 ++++++++++++ .../erik/mobibot/commands/tell/TellManager.kt | 74 +++ .../erik/mobibot/commands/tell/TellMessage.kt | 104 +++++ .../thauvin/erik/mobibot/entries/Entries.kt | 54 +++ .../erik/mobibot/entries/EntriesUtils.kt | 83 ++++ .../erik/mobibot/entries/EntryComment.kt | 52 +++ .../thauvin/erik/mobibot/entries/EntryLink.kt | 213 +++++++++ .../erik/mobibot/entries/FeedsManager.kt | 187 ++++++++ .../erik/mobibot/modules/AbstractModule.kt | 131 ++++++ .../net/thauvin/erik/mobibot/modules/Calc.kt | 87 ++++ .../thauvin/erik/mobibot/modules/ChatGpt.kt | 176 +++++++ .../erik/mobibot/modules/CryptoPrices.kt | 159 +++++++ .../erik/mobibot/modules/CurrencyConverter.kt | 222 +++++++++ .../net/thauvin/erik/mobibot/modules/Dice.kt | 87 ++++ .../erik/mobibot/modules/GoogleSearch.kt | 162 +++++++ .../net/thauvin/erik/mobibot/modules/Joke.kt | 105 +++++ .../thauvin/erik/mobibot/modules/Lookup.kt | 171 +++++++ .../thauvin/erik/mobibot/modules/Mastodon.kt | 149 ++++++ .../erik/mobibot/modules/ModuleException.kt | 45 ++ .../net/thauvin/erik/mobibot/modules/Ping.kt | 83 ++++ .../erik/mobibot/modules/RockPaperScissors.kt | 114 +++++ .../erik/mobibot/modules/StockQuote.kt | 236 ++++++++++ .../thauvin/erik/mobibot/modules/War.class | Bin 0 -> 2452 bytes .../thauvin/erik/mobibot/modules/Weather2.kt | 250 ++++++++++ .../erik/mobibot/modules/WolframAlpha.kt | 142 ++++++ .../thauvin/erik/mobibot/modules/WorldTime.kt | 390 ++++++++++++++++ .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 37 ++ .../net/thauvin/erik/mobibot/msg/Message.kt | 65 +++ .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 38 ++ .../erik/mobibot/msg/PrivateMessage.kt | 37 ++ .../thauvin/erik/mobibot/msg/PublicMessage.kt | 36 ++ .../erik/mobibot/social/SocialManager.kt | 116 +++++ .../erik/mobibot/social/SocialModule.kt | 96 ++++ .../erik/mobibot/social/SocialTimer.kt | 40 ++ bin/test/current.xml | 67 +++ .../net/thauvin/erik/mobibot/AddonsTest.kt | 86 ++++ .../erik/mobibot/ExceptionSanitizer.kt | 60 +++ .../thauvin/erik/mobibot/FeedReaderTest.kt | 78 ++++ .../thauvin/erik/mobibot/LocalProperties.kt | 85 ++++ .../net/thauvin/erik/mobibot/PinboardTest.kt | 81 ++++ .../net/thauvin/erik/mobibot/UtilsTest.kt | 278 +++++++++++ .../thauvin/erik/mobibot/commands/InfoTest.kt | 58 +++ .../erik/mobibot/commands/RecapTest.kt | 60 +++ .../commands/links/LinksManagerTest.kt | 77 +++ .../erik/mobibot/commands/links/ViewTest.kt | 111 +++++ .../erik/mobibot/commands/seen/SeenTest.kt | 85 ++++ .../mobibot/commands/tell/TellMessageTest.kt | 72 +++ .../commands/tell/TellMessagesMgrTest.kt | 87 ++++ .../erik/mobibot/entries/EntriesUtilsTest.kt | 91 ++++ .../erik/mobibot/entries/EntryLinkTest.kt | 133 ++++++ .../erik/mobibot/entries/FeedMgrTest.kt | 115 +++++ .../thauvin/erik/mobibot/modules/CalcTest.kt | 53 +++ .../erik/mobibot/modules/ChatGptTest.kt | 62 +++ .../erik/mobibot/modules/CryptoPricesTest.kt | 78 ++++ .../mobibot/modules/CurrencyConverterTest.kt | 85 ++++ .../thauvin/erik/mobibot/modules/DiceTest.kt | 53 +++ .../erik/mobibot/modules/GoogleSearchTest.kt | 95 ++++ .../thauvin/erik/mobibot/modules/JokeTest.kt | 57 +++ .../erik/mobibot/modules/LookupTest.kt | 60 +++ .../erik/mobibot/modules/MastodonTest.kt | 54 +++ .../mobibot/modules/ModuleExceptionTest.kt | 105 +++++ .../thauvin/erik/mobibot/modules/PingTest.kt | 54 +++ .../mobibot/modules/RockPaperScissorsTest.kt | 50 ++ .../erik/mobibot/modules/StockQuoteTest.kt | 85 ++++ .../erik/mobibot/modules/Weather2Test.kt | 117 +++++ .../erik/mobibot/modules/WolframAlphaTest.kt | 77 +++ .../erik/mobibot/modules/WordTimeTest.kt | 70 +++ .../thauvin/erik/mobibot/msg/MessageTest.kt | 109 +++++ bitbucket-pipelines.yml | 9 +- bld | 2 + bld.bat | 4 + build.gradle | 247 ---------- deploy.sh | 2 +- gradle.properties | 0 gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 7 - gradlew | 249 ---------- gradlew.bat | 92 ---- lib/bld/bld-wrapper.jar | Bin 0 -> 27321 bytes lib/bld/bld-wrapper.properties | 10 + release_info.txt | 27 ++ settings.gradle | 18 - sonar-project.properties | 7 + .../java/net/thauvin/erik/MobibotBuild.java | 170 +++++++ .../net/thauvin/erik/mobibot/modules/War.java | 108 ----- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 6 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 27 ++ .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 4 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../mobibot/commands/links/LinksManager.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../mobibot/commands/seen/NickComparator.kt | 2 +- .../erik/mobibot/commands/seen/Seen.kt | 2 +- .../erik/mobibot/commands/seen/SeenNick.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellManager.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 2 +- .../erik/mobibot/modules/CryptoPrices.kt | 4 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.kt | 89 ++++ .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/WolframAlpha.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 6 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 2 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- src/main/resources/log4j2.xml | 38 ++ .../net/thauvin/erik/mobibot/AddonsTest.kt | 4 +- .../net/thauvin/erik/mobibot/DisableOnCi.kt | 44 ++ .../erik/mobibot/DisableOnCiCondition.kt | 51 ++ .../erik/mobibot/ExceptionSanitizer.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 4 +- .../thauvin/erik/mobibot/LocalProperties.kt | 6 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 8 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 8 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 6 +- .../erik/mobibot/commands/RecapTest.kt | 6 +- .../commands/links/LinksManagerTest.kt | 10 +- .../erik/mobibot/commands/links/ViewTest.kt | 6 +- .../erik/mobibot/commands/seen/SeenTest.kt | 52 ++- .../mobibot/commands/tell/TellMessageTest.kt | 6 +- .../commands/tell/TellMessagesMgrTest.kt | 30 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 12 +- .../erik/mobibot/entries/EntryLinkTest.kt | 12 +- .../erik/mobibot/entries/FeedMgrTest.kt | 10 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 6 +- .../erik/mobibot/modules/ChatGptTest.kt | 10 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 13 +- .../mobibot/modules/CurrencyConverterTest.kt | 12 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 6 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 10 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 6 +- .../erik/mobibot/modules/LookupTest.kt | 8 +- .../erik/mobibot/modules/MastodonTest.kt | 6 +- .../mobibot/modules/ModuleExceptionTest.kt | 34 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 8 +- .../mobibot/modules/RockPaperScissorsTest.kt | 6 +- .../erik/mobibot/modules/StockQuoteTest.kt | 6 +- .../erik/mobibot/modules/Weather2Test.kt | 12 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 10 +- .../erik/mobibot/modules/WordTimeTest.kt | 8 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 4 +- src/test/resources/current.xml | 31 -- version.mustache | 32 -- version.properties | 9 - 240 files changed, 11508 insertions(+), 1650 deletions(-) create mode 100644 .idea/app.iml create mode 100644 .idea/bld.iml delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/compiler.xml rename .idea/copyright/{Erik_s_Copyright_Notice.xml => BSD_3.xml} (93%) delete mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/kotlinc.xml create mode 100644 .idea/libraries/bld.xml create mode 100644 .idea/libraries/compile.xml create mode 100644 .idea/libraries/runtime.xml create mode 100644 .idea/libraries/test.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations/Run Tests.xml create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 bin/main/log4j2.xml create mode 100644 bin/main/net/thauvin/erik/mobibot/Addons.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/Constants.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/FeedReader.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/Mobibot.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/Pinboard.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/Utils.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Die.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Info.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Me.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Modules.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Msg.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Nick.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Recap.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Say.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Users.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Versions.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/View.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/Entries.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Calc.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Dice.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Joke.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Ping.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/War.class create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/Message.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt create mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt create mode 100644 bin/test/current.xml create mode 100644 bin/test/net/thauvin/erik/mobibot/AddonsTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/LocalProperties.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/PinboardTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/UtilsTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt create mode 100644 bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt create mode 100755 bld create mode 100644 bld.bat delete mode 100644 build.gradle delete mode 100644 gradle.properties delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100755 gradlew delete mode 100644 gradlew.bat create mode 100644 lib/bld/bld-wrapper.jar create mode 100644 lib/bld/bld-wrapper.properties create mode 100644 release_info.txt delete mode 100644 settings.gradle create mode 100644 sonar-project.properties create mode 100644 src/bld/java/net/thauvin/erik/MobibotBuild.java delete mode 100644 src/main/java/net/thauvin/erik/mobibot/modules/War.java create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt create mode 100644 src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt create mode 100644 src/main/resources/log4j2.xml create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt delete mode 100644 version.mustache delete mode 100644 version.properties diff --git a/.circleci/config.yml b/.circleci/config.yml index 1868b37..77889be 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,49 +6,39 @@ defaults: &defaults TERM: dumb CI_NAME: "CircleCI" -defaults_gradle: &defaults_gradle +defaults_gradle: &defaults_bld steps: - checkout - - restore_cache: - keys: - - gradle-dependencies-{{ checksum "build.gradle" }} - # fallback to using the latest cache if no exact match is found - - gradle-dependencies- - run: - name: Gradle Dependencies - command: ./gradlew dependencies - - save_cache: - paths: - - ~/.m2 - key: gradle-dependencies-{{ checksum "build.gradle" }} + name: Download the bld dependencies + command: ./bld download - run: - name: Run All Checks - command: ./gradlew check - - store_artifacts: - path: build/reports/ - destination: reports - - store_test_results: - path: build/reports/ + name: Compile source with bld + command: ./bld compile + - run: + name: Run tests with bld + command: ./bld test jobs: - build_gradle_jdk17: + bld_jdk17: <<: *defaults docker: - image: cimg/openjdk:17.0 - <<: *defaults_gradle + <<: *defaults_bld - build_gradle_jdk19: + bld_jdk20: <<: *defaults docker: - - image: cimg/openjdk:19.0 + - image: cimg/openjdk:20.0 - <<: *defaults_gradle + <<: *defaults_bld workflows: version: 2 - gradle: + bld: jobs: - - build_gradle_jdk17 + - bld_jdk17 + - bld_jdk20 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index aa034f9..ae6c66f 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,21 +1,21 @@ -name: gradle-ci +name: bld-ci on: [ push, pull_request, workflow_dispatch ] jobs: - build: + build-bld-project: runs-on: ubuntu-latest env: - GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m" - SONAR_JDK: "17" + COVERAGE_SDK: "17" strategy: matrix: java-version: [ 17, 20 ] steps: - - uses: actions/checkout@v3 + - name: Checkout source repository + uses: actions/checkout@v3 with: fetch-depth: 0 @@ -25,35 +25,21 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java-version }} - - name: Grant execute permission for gradlew - run: chmod +x gradlew + - name: Grant bld execute permission + run: chmod +x bld - - name: Cache SonarCloud packages - if: matrix.java-version == env.SONAR_JDK - uses: actions/cache@v3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar + - name: Download the bld dependencies + run: ./bld download - - name: Test with Gradle - uses: gradle/gradle-build-action@v2 - env: - CI_NAME: "GitHub CI" - ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} - CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} - OWM_API_KEY: ${{ secrets.OWM_API_KEY }} - PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} - MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} - MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} - MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} - EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} - with: - arguments: build check --stacktrace + - name: Compile source with bld + run: ./bld compile - - name: SonarCloud - if: success() && matrix.java-version == env.SONAR_JDK + - name: Run tests with bld + run: ./bld jacoco + + - name: SonarCloud Scan + uses: sonarsource/sonarcloud-github-action@master + if: success() && matrix.java-version == env.COVERAGE_SDK env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew sonar --info diff --git a/.gitignore b/.gitignore index 970cefd..fa550e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,89 +1,61 @@ -!.vscode/extensions.json -!.vscode/launch.json -!.vscode/settings.json -!.vscode/tasks.json -!gradle-wrapper.jar -!properties/* -*.class -*.code-workspace -*.ctxt -*.ear -*.iws -*.jar -*.log -*.nar -*.rar -*.sublime-* -*.tar.gz -*.war -*.zip -.DS_Store -.classpath -.gradle -.history -.idea/**/caches/build_file_checksums.ser -.idea/**/contentModel.xml -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/dataSources/ -.idea/**/dbnavigator.xml -.idea/**/dictionaries -.idea/**/dynamic.xml -.idea/**/gradle.xml -.idea/**/httpRequests -.idea/**/libraries -.idea/**/mongoSettings.xml -.idea/**/replstate.xml -.idea/**/shelf -.idea/**/shelf/ -.idea/**/sonarlint* -.idea/**/sqlDataSources.xml -.idea/**/tasks.xml -.idea/**/uiDesigner.xml -.idea/**/usage.statistics.xml -.idea/**/workspace.xml -.idea_modules/ -.kobalt -.mtj.tmp/ -.mvn/timing.properties -.mvn/wrapper/maven-wrapper.jar -.nb-gradle -.project -.scannerwork -.settings -.vscode/* -Thumbs.db -__pycache__ -atlassian-ide-plugin.xml -bin/ -build/ -cmake-build-*/ -com_crashlytics_export_strings.xml -crashlytics-build.properties -crashlytics.properties -dependency-reduced-pom.xml -deploy/ -dist/ -ehthumbs.db -fabric.properties -gen/ -hs_err_pid* -kobaltBuild -kobaltw*-test -lib/kotlin* -libs/ -local.properties -log4j2.xml -logs/ -mobibot.properties -out/ -pom.xml.next -pom.xml.releaseBackup -pom.xml.tag -pom.xml.versionsBackup -proguard-project.txt -project.properties -release.properties -target/ -test-output -venv +.gradle +.DS_Store +build +lib/bld/** +!lib/bld/bld-wrapper.jar +!lib/bld/bld-wrapper.properties +lib/compile/ +lib/runtime/ +lib/standalone/ +lib/test/ + +# IDEA ignores + +# User-specific +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Editor-based Rest Client +.idea/httpRequests + +local.properties + +deploy +logs +mobibot.properties diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 48a3396..052df48 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,32 +1,11 @@ -image: gradle:8-jdk17 - -variables: - GRADLE_OPTS: "-Dorg.gradle.daemon=false" - CI_NAME: "GitLab CI" - -before_script: - - export GRADLE_USER_HOME=`pwd`/.gradle +image: openjdk:17 stages: - - build - test -build: - stage: build - script: gradle --build-cache assemble - cache: - key: "$CI_COMMIT_REF_NAME" - policy: push - paths: - - build - - .gradle - test: stage: test - script: gradle check - cache: - key: "$CI_COMMIT_REF_NAME" - policy: pull - paths: - - build - - .gradle + script: + - ./bld download + - ./bld compile + - ./bld test diff --git a/.idea/app.iml b/.idea/app.iml new file mode 100644 index 0000000..2c1fe21 --- /dev/null +++ b/.idea/app.iml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager"> + <output url="file://$MODULE_DIR$/build/main" /> + <output-test url="file://$MODULE_DIR$/build/test" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" relativeOutputPath="resources" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library" scope="RUNTIME"> + <library> + <CLASSES> + <root url="file://$MODULE_DIR$/src/main/resources/templates" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="library" name="compile" level="project" /> + <orderEntry type="library" scope="RUNTIME" name="runtime" level="project" /> + <orderEntry type="library" scope="TEST" name="test" level="project" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/bld.iml b/.idea/bld.iml new file mode 100644 index 0000000..e63e11e --- /dev/null +++ b/.idea/bld.iml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager"> + <output url="file://$MODULE_DIR$/build/bld" /> + <output-test url="file://$MODULE_DIR$/build/bld" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/src/bld"> + <sourceFolder url="file://$MODULE_DIR$/src/bld/java" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="bld" level="project" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 10aa334..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,298 +0,0 @@ -<component name="ProjectCodeStyleConfiguration"> - <code_scheme name="Project" version="200"> - <JetCodeStyleSettings> - <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> - </JetCodeStyleSettings> - <codeStyleSettings language="JAVA"> - <option name="IF_BRACE_FORCE" value="1" /> - <arrangement> - <groups> - <group> - <type>OVERRIDDEN_METHODS</type> - <order>BY_NAME</order> - </group> - </groups> - <rules> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PUBLIC>true</PUBLIC> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PROTECTED>true</PROTECTED> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PRIVATE>true</PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PUBLIC>true</PUBLIC> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PROTECTED>true</PROTECTED> - <STATIC>true</STATIC> - <visibility /> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PRIVATE>true</PRIVATE> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <INITIALIZER_BLOCK>true</INITIALIZER_BLOCK> - <STATIC>true</STATIC> - </AND> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PUBLIC>true</PUBLIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PROTECTED>true</PROTECTED> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <FINAL>true</FINAL> - <PRIVATE>true</PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PUBLIC>true</PUBLIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PROTECTED>true</PROTECTED> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PACKAGE_PRIVATE>true</PACKAGE_PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <FIELD>true</FIELD> - <PRIVATE>true</PRIVATE> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <FIELD>true</FIELD> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <INITIALIZER_BLOCK>true</INITIALIZER_BLOCK> - </match> - </rule> - </section> - <section> - <rule> - <match> - <CONSTRUCTOR>true</CONSTRUCTOR> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <METHOD>true</METHOD> - <STATIC>true</STATIC> - </AND> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <METHOD>true</METHOD> - </match> - <order>BY_NAME</order> - </rule> - </section> - <section> - <rule> - <match> - <ENUM>true</ENUM> - </match> - </rule> - </section> - <section> - <rule> - <match> - <INTERFACE>true</INTERFACE> - </match> - </rule> - </section> - <section> - <rule> - <match> - <AND> - <CLASS>true</CLASS> - <STATIC>true</STATIC> - </AND> - </match> - </rule> - </section> - <section> - <rule> - <match> - <CLASS>true</CLASS> - </match> - </rule> - </section> - </rules> - </arrangement> - </codeStyleSettings> - <codeStyleSettings language="kotlin"> - <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> - </codeStyleSettings> - </code_scheme> -</component> \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 23f4bb5..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,6 +0,0 @@ -<component name="ProjectCodeStyleConfiguration"> - <state> - <option name="USE_PER_PROJECT_SETTINGS" value="true" /> - <option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" /> - </state> -</component> \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b589d56..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="CompilerConfiguration"> - <bytecodeTargetLevel target="17" /> - </component> -</project> \ No newline at end of file diff --git a/.idea/copyright/Erik_s_Copyright_Notice.xml b/.idea/copyright/BSD_3.xml similarity index 93% rename from .idea/copyright/Erik_s_Copyright_Notice.xml rename to .idea/copyright/BSD_3.xml index 055999a..275eca9 100644 --- a/.idea/copyright/Erik_s_Copyright_Notice.xml +++ b/.idea/copyright/BSD_3.xml @@ -1,6 +1,6 @@ <component name="CopyrightManager"> <copyright> - <option name="notice" value="&#36;file.fileName Copyright 2004-&#36;today.year Erik C. Thauvin (erik@thauvin.net) 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." /> - <option name="myName" value="Erik's Copyright Notice" /> + <option name="notice" value="&#36;file.fileName Copyright 2021-&#36;today.year Erik C. Thauvin (erik@thauvin.net) 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." /> + <option name="myName" value="BSD-3" /> </copyright> </component> \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml index 1419e40..3203074 100644 --- a/.idea/copyright/profiles_settings.xml +++ b/.idea/copyright/profiles_settings.xml @@ -1,3 +1,3 @@ <component name="CopyrightManager"> - <settings default="Erik's Copyright Notice" /> + <settings default="BSD-3" /> </component> \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 60682bf..1e01b48 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,72 +1,8 @@ <component name="InspectionProjectProfileManager"> <profile version="1.0"> <option name="myName" value="Project Default" /> - <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="TOP_LEVEL_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="INNER_CLASS_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="METHOD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> - </value> - </option> - <option name="FIELD_OPTIONS"> - <value> - <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> - <option name="REQUIRED_TAGS" value="" /> - </value> - </option> - <option name="IGNORE_DEPRECATED" value="false" /> - <option name="IGNORE_JAVADOC_PERIOD" value="true" /> - <option name="IGNORE_DUPLICATED_THROWS" value="false" /> - <option name="IGNORE_POINT_TO_ITSELF" value="false" /> - <option name="myAdditionalJavadocTags" value="created" /> - </inspection_tool> <inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true"> <option name="ADDITIONAL_TAGS" value="created" /> </inspection_tool> - <inspection_tool class="MissingJavadoc" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="PACKAGE_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="MODULE_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="TOP_LEVEL_CLASS_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="INNER_CLASS_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="METHOD_SETTINGS"> - <Options> - <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> - <option name="ENABLED" value="false" /> - </Options> - </option> - <option name="FIELD_SETTINGS"> - <Options> - <option name="ENABLED" value="false" /> - </Options> - </option> - </inspection_tool> </profile> </component> \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index 0e29f96..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="RemoteRepositoriesConfiguration"> - <remote-repository> - <option name="id" value="central" /> - <option name="name" value="Maven Central repository" /> - <option name="url" value="https://repo1.maven.org/maven2" /> - </remote-repository> - <remote-repository> - <option name="id" value="jboss.community" /> - <option name="name" value="JBoss Community repository" /> - <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$MAVEN_REPOSITORY$/" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$MAVEN_REPOSITORY$/" /> - </remote-repository> - <remote-repository> - <option name="id" value="maven2" /> - <option name="name" value="maven2" /> - <option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenRepo" /> - <option name="name" value="MavenRepo" /> - <option name="url" value="https://repo.maven.apache.org/maven2/" /> - </remote-repository> - <remote-repository> - <option name="id" value="maven" /> - <option name="name" value="maven" /> - <option name="url" value="https://jitpack.io" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$PROJECT_DIR$/../../maven/repository/" /> - </remote-repository> - </component> -</project> \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index e805548..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="KotlinJpsPluginSettings"> - <option name="version" value="1.9.20" /> - </component> -</project> \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml new file mode 100644 index 0000000..cf75013 --- /dev/null +++ b/.idea/libraries/bld.xml @@ -0,0 +1,17 @@ +<component name="libraryTable"> + <library name="bld"> + <CLASSES> + <root url="file://$PROJECT_DIR$/lib/bld" /> + <root url="jar://$USER_HOME$/.bld/dist/bld-1.7.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$USER_HOME$/.bld/dist/bld-1.7.5-sources.jar!/" /> + </SOURCES> + <excluded> + <root url="jar://$PROJECT_DIR$/lib/bld/bld-wrapper.jar!/" /> + </excluded> + <jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" /> + <jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" type="SOURCES" /> + </library> +</component> \ No newline at end of file diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml new file mode 100644 index 0000000..9bd86aa --- /dev/null +++ b/.idea/libraries/compile.xml @@ -0,0 +1,13 @@ +<component name="libraryTable"> + <library name="compile"> + <CLASSES> + <root url="file://$PROJECT_DIR$/lib/compile" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="file://$PROJECT_DIR$/lib/compile" /> + </SOURCES> + <jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="false" /> + <jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="false" type="SOURCES" /> + </library> +</component> \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml new file mode 100644 index 0000000..2ae5c4b --- /dev/null +++ b/.idea/libraries/runtime.xml @@ -0,0 +1,14 @@ +<component name="libraryTable"> + <library name="runtime"> + <CLASSES> + <root url="file://$PROJECT_DIR$/lib/runtime" /> + <root url="file://$PROJECT_DIR$/src/main/resources" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="file://$PROJECT_DIR$/lib/runtime" /> + </SOURCES> + <jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="false" /> + <jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="false" type="SOURCES" /> + </library> +</component> \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml new file mode 100644 index 0000000..b80486a --- /dev/null +++ b/.idea/libraries/test.xml @@ -0,0 +1,14 @@ +<component name="libraryTable"> + <library name="test"> + <CLASSES> + <root url="file://$PROJECT_DIR$/lib/test" /> + <root url="file://$PROJECT_DIR$/src/test/resources" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="file://$PROJECT_DIR$/lib/test" /> + </SOURCES> + <jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="false" /> + <jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="false" type="SOURCES" /> + </library> +</component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index a59e398..e853f87 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,16 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="FrameworkDetectionExcludesConfiguration"> - <file type="web" url="file://$PROJECT_DIR$" /> + <component name="EntryPointsManager"> + <pattern value="net.thauvin.erik.MobibotBuild" method="deploy" /> + <pattern value="net.thauvin.erik.MobibotBuild" method="jacoco" /> + <pattern value="net.thauvin.erik.MobibotBuild" /> </component> <component name="PDMPlugin"> - <option name="customRuleSets"> - <list> - <option value="K:\java\semver\config\pmd.xml" /> - <option value="$PROJECT_DIR$/../../java/bld-exec/config/pmd.xml" /> - </list> - </option> <option name="skipTestSources" value="false" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="19" project-jdk-type="JavaSDK" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/build" /> + </component> </project> \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..55adcb9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/app.iml" filepath="$PROJECT_DIR$/.idea/app.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/bld.iml" filepath="$PROJECT_DIR$/.idea/bld.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff --git a/.idea/runConfigurations/Run Tests.xml b/.idea/runConfigurations/Run Tests.xml new file mode 100644 index 0000000..37dc742 --- /dev/null +++ b/.idea/runConfigurations/Run Tests.xml @@ -0,0 +1,9 @@ +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="Run Tests" type="Application" factoryName="Application" nameIsGenerated="true"> + <option name="MAIN_CLASS_NAME" value="net.thauvin.erik.MobibotTest" /> + <module name="app" /> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c6500f2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Run Tests", + "request": "launch", + "mainClass": "net.thauvin.erik.MobibotTest" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..133aa45 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "java.project.sourcePaths": [ + "src/main/java", + "src/main/resources", + "src/test/java", + "src/bld/java" + ], + "java.configuration.updateBuildConfiguration": "automatic", + "java.project.referencedLibraries": [ + "${HOME}/.bld/dist/bld-1.7.5.jar", + "lib/compile/*.jar", + "lib/runtime/*.jar", + "lib/test/*.jar" + ] +} diff --git a/README.md b/README.md index 1ecefeb..fbcacc2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.10-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.20-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) @@ -14,8 +14,8 @@ Some very basic instructions: cd mobibot - # build with gradle - ./gradlew + # build JAR and deploy + ./bld jar deploy cd deploy diff --git a/bin/main/log4j2.xml b/bin/main/log4j2.xml new file mode 100644 index 0000000..265c88f --- /dev/null +++ b/bin/main/log4j2.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ log4j2.xml + ~ + ~ Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + ~ + ~ 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. + --> + +<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> + <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="trace" name="net.thauvin.erik.mobibot" additivity="false"> + <appender-ref ref="stderr"/> + </logger> + </Loggers> +</Configuration> diff --git a/bin/main/net/thauvin/erik/mobibot/Addons.kt b/bin/main/net/thauvin/erik/mobibot/Addons.kt new file mode 100644 index 0000000..2c5f05d --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Addons.kt @@ -0,0 +1,190 @@ +/* + * Addons.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import net.thauvin.erik.mobibot.Utils.notContains +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.LinksManager +import net.thauvin.erik.mobibot.modules.AbstractModule +import org.pircbotx.hooks.events.PrivateMessageEvent +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.util.* + +/** + * Modules and Commands addons. + */ +class Addons(private val props: Properties) { + private val logger: Logger = LoggerFactory.getLogger(Addons::class.java) + private val disabledModules = props.getProperty("disabled-modules", "").split(LinksManager.TAG_MATCH) + private val disableCommands = props.getProperty("disabled-commands", "").split(LinksManager.TAG_MATCH) + + val commands: MutableList<AbstractCommand> = mutableListOf() + val modules: MutableList<AbstractModule> = mutableListOf() + val names = Names + + /** + * Add a module with properties. + */ + fun add(module: AbstractModule): Boolean { + var enabled = false + with(module) { + if (disabledModules.notContains(name, true)) { + if (hasProperties()) { + propertyKeys.forEach { + setProperty(it, props.getProperty(it, "")) + } + } + + if (isEnabled) { + modules.add(this) + names.modules.add(name) + names.commands.addAll(commands) + enabled = true + } else { + if (logger.isDebugEnabled) { + logger.debug("Module $name is disabled.") + } + names.disabledModules.add(name) + } + } else { + names.disabledModules.add(name) + } + } + return enabled + } + + /** + * Add a command with properties. + */ + fun add(command: AbstractCommand): Boolean { + var enabled = false + with(command) { + if (disableCommands.notContains(name, true)) { + if (properties.isNotEmpty()) { + properties.keys.forEach { + setProperty(it, props.getProperty(it, "")) + } + } + if (isEnabled()) { + commands.add(this) + if (isVisible) { + if (isOpOnly) { + names.ops.add(name) + } else { + names.commands.add(name) + } + } + enabled = true + } else { + if (logger.isDebugEnabled) { + logger.debug("Command $name is disabled.") + } + names.disabledCommands.add(name) + } + } else { + names.disabledCommands.add(name) + } + } + return enabled + } + + /** + * Execute a command or module. + */ + 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(channel, args, event) + return true + } + } + val mods = if (event is PrivateMessageEvent) modules.filter { it.isPrivateMsgEnabled } else modules + for (module in mods) { + if (module.commands.contains(cmd)) { + module.commandResponse(channel, cmd, args, event) + return true + } + } + return false + } + + /** + * Match a command. + */ + fun match(channel: String, event: GenericMessageEvent): Boolean { + for (command in commands) { + if (command.matches(event.message)) { + command.commandResponse(channel, event.message, event) + return true + } + } + return false + } + + /** + * Commands and Modules help. + */ + fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean { + for (command in commands) { + if (command.isVisible && command.name.startsWith(topic)) { + return command.helpResponse(channel, topic, event) + } + } + for (module in modules) { + if (module.commands.contains(topic)) { + return module.helpResponse(event) + } + } + return false + } + + /** + * Holds commands and modules names. + */ + object Names { + val modules: MutableList<String> = mutableListOf() + val disabledModules: MutableList<String> = mutableListOf() + val commands: MutableList<String> = mutableListOf() + val disabledCommands: MutableList<String> = mutableListOf() + val ops: MutableList<String> = mutableListOf() + + fun sort() { + modules.sort() + disabledModules.sort() + commands.sort() + disabledCommands.sort() + ops.sort() + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/Constants.kt b/bin/main/net/thauvin/erik/mobibot/Constants.kt new file mode 100644 index 0000000..98ef74a --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Constants.kt @@ -0,0 +1,102 @@ +/* + * Constants.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +/** + * The `Constants`. + */ +object Constants { + /** + * The connect/read timeout in ms. + */ + const val CONNECT_TIMEOUT = 5000 + + /** + * Debug command line argument. + */ + const val DEBUG_ARG = "debug" + + /** + * Default IRC Port. + */ + const val DEFAULT_PORT = 6667 + + /** + * Default IRC Server. + */ + const val DEFAULT_SERVER = "irc.libera.chat" + + /** + * CLI command for usage. + */ + const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar" + + /** + * User-Agent + */ + const val USER_AGENT = + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + + /** + * The help command. + */ + const val HELP_CMD = "help" + + /** + * The link command. + */ + const val LINK_CMD = "L" + + /** + * The empty title string. + */ + const val NO_TITLE = "No Title" + + /** + * Properties command line argument. + */ + const val PROPS_ARG = "properties" + + /** + * The tag command + */ + const val TAG_CMD = "T" + + /** + * The timer delay in minutes. + */ + const val TIMER_DELAY = 10L + + /** + * Properties version line argument. + */ + const val VERSION_ARG = "version" +} diff --git a/bin/main/net/thauvin/erik/mobibot/FeedReader.kt b/bin/main/net/thauvin/erik/mobibot/FeedReader.kt new file mode 100644 index 0000000..d82f011 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/FeedReader.kt @@ -0,0 +1,92 @@ +/* + * FeedReader.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import com.rometools.rome.io.FeedException +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.FeedsManager +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 java.io.IOException +import java.net.URL + +/** + * Reads an RSS feed. + */ +class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable { + private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) + + /** + * Fetches the Feed's items. + */ + override fun run() { + try { + readFeed(url).forEach { + event.sendMessage("", it) + } + } catch (e: FeedException) { + if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e) + event.sendMessage("An error has occurred while parsing the feed: ${e.message}") + } catch (e: IOException) { + if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e) + event.sendMessage("An IO error has occurred while fetching the feed: ${e.message}") + } + } + + companion object { + @JvmStatic + @Throws(FeedException::class, IOException::class) + fun readFeed(url: String, maxItems: Int = 5): List<Message> { + val messages = mutableListOf<Message>() + val input = SyndFeedInput() + XmlReader(URL(url).openStream()).use { reader -> + val feed = input.build(reader) + val items = feed.entries + if (items.isEmpty()) { + messages.add(NoticeMessage("There is currently nothing to view.")) + } else { + items.take(maxItems).forEach { + messages.add(NoticeMessage(it.title)) + messages.add(NoticeMessage(helpFormat(it.link.green(), false))) + } + } + } + return messages + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/Mobibot.kt b/bin/main/net/thauvin/erik/mobibot/Mobibot.kt new file mode 100644 index 0000000..f91c457 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Mobibot.kt @@ -0,0 +1,421 @@ +/* + * Mobibot.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import kotlinx.cli.ArgParser +import kotlinx.cli.ArgType +import kotlinx.cli.default +import net.thauvin.erik.mobibot.Utils.appendIfMissing +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.getIntProperty +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.lastOrEmpty +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.commands.* +import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap +import net.thauvin.erik.mobibot.commands.links.* +import net.thauvin.erik.mobibot.commands.seen.Seen +import net.thauvin.erik.mobibot.commands.tell.Tell +import net.thauvin.erik.mobibot.modules.* +import net.thauvin.erik.semver.Version +import org.pircbotx.Configuration +import org.pircbotx.PircBotX +import org.pircbotx.hooks.ListenerAdapter +import org.pircbotx.hooks.events.* +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.* +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* +import java.util.regex.Pattern +import kotlin.system.exitProcess + +@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") +class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { + // The bot configuration. + private val config: Configuration + + // Commands and Modules + private val addons: Addons + + // Seen command + private val seen: Seen + + // Tell command + private val tell: Tell + + /** Logger. */ + val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java) + + /** + * Connects to the server and joins the channel. + */ + fun connect() { + PircBotX(config).startBot() + } + + /** + * Responds with the default help. + */ + private fun helpDefault(event: GenericMessageEvent) { + event.sendMessage("Type a URL on $channel to post it.") + event.sendMessage("For more information on a specific command, type:") + event.sendMessage( + helpFormat( + helpCmdSyntax("%c ${Constants.HELP_CMD} <command>", event.bot().nick, event is PrivateMessageEvent) + ) + ) + event.sendMessage("The commands are:") + event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) + if (event.isChannelOp(channel)) { + if (addons.names.disabledCommands.isNotEmpty()) { + event.sendMessage("The disabled commands are:") + event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true) + } + event.sendMessage("The op commands are:") + event.sendList(addons.names.ops, 8, isBold = true, isIndent = true) + } + } + + /** + * Responds with the default, commands or modules help. + */ + private fun helpResponse(event: GenericMessageEvent, topic: String) { + if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) { + helpDefault(event) + } + } + + override fun onAction(event: ActionEvent?) { + event?.channel?.let { + if (channel == it.name) { + event.user?.let { user -> + storeRecap(user.nick, event.action, true) + } + } + } + } + + override fun onDisconnect(event: DisconnectEvent?) { + event?.let { + with(event.getBot<PircBotX>()) { + LinksManager.socialManager.notification("$nick disconnected from $serverHostname") + seen.add(userChannelDao.getChannel(channel).users) + } + } + LinksManager.socialManager.shutdown() + } + + override fun onPrivateMessage(event: PrivateMessageEvent?) { + event?.user?.let { user -> + if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}") + val cmds = event.message.trim().split(" ".toRegex(), 2) + val cmd = cmds[0].lowercase() + val args = cmds.lastOrEmpty().trim() + if (cmd.startsWith(Constants.HELP_CMD)) { // help + helpResponse(event, args) + } else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module + helpDefault(event) + } + } + } + + override fun onJoin(event: JoinEvent?) { + event?.user?.let { user -> + with(event.getBot<PircBotX>()) { + if (user.nick == nick) { + LinksManager.socialManager.notification( + "$nick has joined ${event.channel.name} on $serverHostname" + ) + seen.add(userChannelDao.getChannel(channel).users) + } else { + tell.send(event) + seen.add(user.nick) + } + } + } + } + + override fun onMessage(event: MessageEvent?) { + event?.user?.let { user -> + tell.send(event) + if (event.message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command> + if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") + val cmds = event.message.substring(event.bot().nick.length + 1).trim().split(" ".toRegex(), 2) + val cmd = cmds[0].lowercase() + val args = cmds.lastOrEmpty().trim() + if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help + helpResponse(event, args) + } else { + // Execute module or command + addons.exec(channel, cmd, args, event) + } + } else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc. + if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") + } + storeRecap(user.nick, event.message, false) + seen.add(user.nick) + } + } + + override fun onNickChange(event: NickChangeEvent?) { + event?.let { + tell.send(event) + if (!it.oldNick.equals(it.newNick, true)) { + seen.add(it.oldNick) + } + seen.add(it.newNick) + } + } + + override fun onPart(event: PartEvent?) { + event?.user?.let { user -> + with(event.getBot<PircBotX>()) { + if (user.nick == nick) { + LinksManager.socialManager.notification( + "$nick has left ${event.channel.name} on $serverHostname" + ) + seen.add(userChannelDao.getChannel(channel).users) + } else { + seen.add(user.nick) + } + } + } + } + + override fun onQuit(event: QuitEvent?) { + event?.user?.let { user -> + seen.add(user.nick) + } + } + + companion object { + @JvmStatic + @Throws(Exception::class) + fun main(args: Array<String>) { + // Set up the command line options + val parser = ArgParser(Constants.CLI_CMD) + val debug by parser.option( + ArgType.Boolean, + Constants.DEBUG_ARG, + Constants.DEBUG_ARG.substring(0, 1), + "Print debug & logging data directly to the console" + ).default(false) + val property by parser.option( + ArgType.String, + Constants.PROPS_ARG, + Constants.PROPS_ARG.substring(0, 1), + "Use alternate properties file" + ).default("./${ReleaseInfo.PROJECT}.properties") + val version by parser.option( + ArgType.Boolean, + Constants.VERSION_ARG, + Constants.VERSION_ARG.substring(0, 1), + "Print version info" + ).default(false) + + // Parse the command line + parser.parse(args) + + if (version) { + // Output the version + println( + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + + " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + ) + println(ReleaseInfo.WEBSITE) + } else { + // Load the properties + val p = Properties() + try { + Files.newInputStream( + Paths.get(property) + ).use { fis -> + p.load(fis) + } + } catch (ignore: FileNotFoundException) { + System.err.println("Unable to find properties file.") + exitProcess(1) + } catch (ignore: IOException) { + System.err.println("Unable to open properties file.") + exitProcess(1) + } + val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) + val channel = p.getProperty("channel") + val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar) + + // Redirect stdout and stderr + if (!debug) { + try { + val stdout = PrintStream( + BufferedOutputStream( + FileOutputStream( + logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true + ) + ), true + ) + System.setOut(stdout) + } catch (ignore: IOException) { + System.err.println("Unable to open output (stdout) log file.") + exitProcess(1) + } + try { + val stderr = PrintStream( + BufferedOutputStream( + FileOutputStream("$logsDir$nickname.err", true) + ), true + ) + System.setErr(stderr) + } catch (ignore: IOException) { + System.err.println("Unable to open error (stderr) log file.") + exitProcess(1) + } + } + + // Start the bot + Mobibot(nickname, channel, logsDir, p).connect() + } + } + } + + /** + * Initialize the bot. + */ + init { + 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() + + // Load the current entries + with(LinksManager) { + entries.channel = channel + entries.ircServer = ircServer + entries.logsDir = logsDirPath + entries.backlogs = p.getProperty("backlogs", "") + entries.load() + + // Set up pinboard + pinboard.setApiToken(p.getProperty("pinboard-api-token", "")) + } + + addons = Addons(p) + + // Load the commands + addons.add(ChannelFeed(channel.removePrefix("#"))) + addons.add(Comment()) + addons.add(Cycle()) + addons.add(Die()) + addons.add(Ignore()) + addons.add(LinksManager()) + addons.add(Me()) + addons.add(Modules(addons.names.modules, addons.names.disabledModules)) + addons.add(Msg()) + addons.add(Nick()) + addons.add(Posting()) + addons.add(Recap()) + addons.add(Say()) + + // Seen command + seen = Seen("${logsDirPath}${nickname}-seen.ser") + addons.add(seen) + + addons.add(Tags()) + + // Tell command + tell = Tell("${logsDirPath}${nickname}.ser") + addons.add(tell) + + addons.add(Users()) + addons.add(Versions()) + addons.add(View()) + + // Load social modules + LinksManager.socialManager.add(addons, Mastodon()) + + // Load the modules + addons.add(Calc()) + addons.add(ChatGpt()) + addons.add(CryptoPrices()) + addons.add(CurrencyConverter()) + addons.add(Dice()) + addons.add(GoogleSearch()) + addons.add(Info(tell, seen)) + addons.add(Joke()) + addons.add(Lookup()) + addons.add(Ping()) + addons.add(RockPaperScissors()) + addons.add(StockQuote()) + addons.add(War()) + addons.add(Weather2()) + addons.add(WolframAlpha()) + addons.add(WorldTime()) + + // Sort the addons + addons.names.sort() + } +} + diff --git a/bin/main/net/thauvin/erik/mobibot/Pinboard.kt b/bin/main/net/thauvin/erik/mobibot/Pinboard.kt new file mode 100644 index 0000000..7cb5aed --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Pinboard.kt @@ -0,0 +1,113 @@ +/* + * Pinboard.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.pinboard.PinboardPoster +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit +import java.util.* + +/** + * Handles posts to pinboard.in. + */ +class Pinboard { + private val poster = PinboardPoster() + + /** + * Adds a pin. + */ + fun addPin(ircServer: String, entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + with(entry) { + poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) + } + } + } + + /** + * Sets the pinboard API token. + */ + fun setApiToken(apiToken: String) { + poster.apiToken = apiToken + } + + /** + * Deletes a pin. + */ + fun deletePin(entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + poster.deletePin(entry.link) + } + + } + + /** + * Updates a pin. + */ + fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) { + if (poster.apiToken.isNotBlank()) { + with(entry) { + if (oldUrl != link) { + poster.deletePin(oldUrl) + } + poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) + } + } + } + + /** + * Formats a date to a UTC timestamp. + */ + private fun Date.toTimestamp(): String { + return ZonedDateTime.ofInstant( + toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() + ).format(DateTimeFormatter.ISO_INSTANT) + } + + /** + * Formats the tags for pinboard. + */ + private fun EntryLink.formatTags(): String { + return nick + formatTags(",", ",") + } + + /** + * Returns the pinboard.in extended attribution line. + */ + private fun EntryLink.postedBy(ircServer: String): String { + return "Posted by $nick on $channel ( $ircServer )" + } +} + diff --git a/bin/main/net/thauvin/erik/mobibot/Utils.kt b/bin/main/net/thauvin/erik/mobibot/Utils.kt new file mode 100644 index 0000000..e4760d2 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/Utils.kt @@ -0,0 +1,439 @@ +/* + * Utils.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR +import net.thauvin.erik.urlencoder.UrlEncoderUtil +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 org.slf4j.Logger +import java.io.* +import java.net.HttpURLConnection +import java.net.URL +import java.nio.file.Files +import java.nio.file.Paths +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.util.* +import kotlin.io.path.exists +import kotlin.io.path.fileSize + +/** + * Miscellaneous utilities. + */ +@Suppress("TooManyFunctions") +object Utils { + private val searchFlags = arrayOf("%c", "%n") + + /** + * Prepends a prefix if not present. + */ + @JvmStatic + fun String.prefixIfMissing(prefix: Char): String { + return if (first() != prefix) { + "$prefix${this}" + } else { + this + } + } + + /** + * Appends a suffix to the end of the String if not present. + */ + @JvmStatic + fun String.appendIfMissing(suffix: Char): String { + return if (last() != suffix) { + "$this${suffix}" + } else { + this + } + } + + /** + * Makes the given int bold. + */ + @JvmStatic + fun Int.bold(): String = toString().bold() + + /** + * Makes the given long bold. + */ + @JvmStatic + fun Long.bold(): String = toString().bold() + + /** + * Makes the given string bold. + */ + @JvmStatic + fun String?.bold(): String = colorize(Colors.BOLD) + + /** + * Returns the [PircBotX] instance. + */ + fun GenericMessageEvent.bot(): PircBotX { + return getBot() as PircBotX + } + + /** + * Capitalize a string. + */ + @JvmStatic + fun String.capitalise(): String = lowercase().replaceFirstChar { it.uppercase() } + + /** + * Capitalize words + */ + @JvmStatic + fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() } + + /** + * Colorize a string. + */ + @JvmStatic + fun String?.colorize(color: String): String { + return when { + isNullOrEmpty() -> { + "" + } + + color == DEFAULT_COLOR -> { + this + } + + Colors.BOLD == color || Colors.REVERSE == color -> { + color + this + color + } + + else -> { + color + this + Colors.NORMAL + } + } + } + + /** + * Makes the given string cyan. + */ + @JvmStatic + fun String?.cyan(): String = colorize(Colors.CYAN) + + /** + * URL encodes the given string. + */ + @JvmStatic + fun String.encodeUrl(): String = UrlEncoderUtil.encode(this) + + /** + * Returns a property as an int. + */ + @JvmStatic + fun Properties.getIntProperty(key: String, defaultValue: Int): Int { + return getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue + } + + /** + * Makes the given string green. + */ + @JvmStatic + fun String?.green(): String = colorize(Colors.DARK_GREEN) + + /** + * Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's + * nick. + */ + @JvmStatic + fun helpCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { + val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) + return text.replaceEach(searchFlags, replace) + } + + /** + * Returns a formatted help string. + */ + @JvmStatic + @JvmOverloads + fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { + val s = if (isBold) help.bold() else help + return if (isIndent) s.prependIndent() else s + } + + /** + * Returns `true` if the specified user is an operator on the [channel]. + */ + @JvmStatic + fun GenericMessageEvent.isChannelOp(channel: String): Boolean { + return this.bot().userChannelDao.getChannel(channel).isOp(this.user) + } + + /** + * Returns `true` if a HTTP status code indicates a successful response. + */ + @JvmStatic + fun Int.isHttpSuccess() = this in 200..399 + + /** + * Returns the last item of a list of strings or empty if none. + */ + @JvmStatic + fun List<String>.lastOrEmpty(): String { + return if (this.size >= 2) { + this.last() + } else + "" + } + + /** + * Load serial data from file. + */ + @JvmStatic + fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any { + val serialFile = Paths.get(file) + if (serialFile.exists() && serialFile.fileSize() > 0) { + try { + ObjectInputStream( + BufferedInputStream(Files.newInputStream(serialFile)) + ).use { input -> + if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") + return input.readObject() + } + } catch (e: IOException) { + logger.error("An IO error occurred loading the ${description}.", e) + } catch (e: ClassNotFoundException) { + logger.error("An error occurred loading the ${description}.", e) + } + } + return default + } + + /** + * Returns `true` if the list does not contain the given string. + */ + @JvmStatic + fun List<String>.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) } + + /** + * Obfuscates the given string. + */ + @JvmStatic + fun String.obfuscate(): String { + return if (isNotBlank()) { + "x".repeat(length) + } else this + } + + /** + * Returns the plural form of a word, if count > 1. + */ + @JvmStatic + fun String.plural(count: Long): String { + return if (count > 1) "${this}s" else this + } + + /** + * Makes the given string red. + */ + @JvmStatic + fun String?.red(): String = colorize(Colors.RED) + + /** + * Replaces all occurrences of Strings within another String. + */ + @JvmStatic + fun String.replaceEach(search: Array<out String>, replace: Array<out String>): String { + var result = this + if (search.size == replace.size) { + search.forEachIndexed { i, s -> + result = result.replace(s, replace[i]) + } + } + return result + } + + /** + * Makes the given string reverse color. + */ + @JvmStatic + fun String?.reverseColor(): String = colorize(Colors.REVERSE) + + /** + * Save data + */ + @JvmStatic + fun saveSerialData(file: String, data: Any, logger: Logger, description: String) { + try { + BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> + ObjectOutputStream(bos).use { output -> + if (logger.isDebugEnabled) logger.debug("Saving the ${description}.") + output.writeObject(data) + } + } + } catch (e: IOException) { + logger.error("Unable to save the ${description}.", e) + } + } + + /** + * Send a formatted commands/modules, etc. list. + */ + @JvmStatic + @JvmOverloads + 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, message.msg.colorize(message.color)) + } else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) { + respondPrivateMessage(message.msg.colorize(message.color)) + } else { + bot().sendIRC().message(channel, message.msg.colorize(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. + */ + @JvmStatic + fun today(): String = LocalDateTime.now().toIsoLocalDate() + + /** + * Converts a string to an int. + */ + @JvmStatic + fun String.toIntOrDefault(defaultValue: Int): Int { + return try { + toInt() + } catch (e: NumberFormatException) { + defaultValue + } + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun Date.toIsoLocalDate(): String { + return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toIsoLocalDate() + } + + /** + * Returns the specified date as an ISO local date string. + */ + @JvmStatic + fun LocalDateTime.toIsoLocalDate(): String = format(DateTimeFormatter.ISO_LOCAL_DATE) + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun Date.toUtcDateTime(): String { + return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toUtcDateTime() + } + + /** + * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. + */ + @JvmStatic + fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + + /** + * Makes the given string bold. + */ + @JvmStatic + fun String?.underline(): String = colorize(Colors.UNDERLINE) + + + /** + * Converts XML/XHTML entities to plain text. + */ + @JvmStatic + fun String.unescapeXml(): String = Jsoup.parse(this).text() + + /** + * Reads contents of a URL. + */ + @JvmStatic + @Throws(IOException::class) + fun URL.reader(): UrlReaderResponse { + val connection = this.openConnection() as HttpURLConnection + connection.setRequestProperty( + "User-Agent", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + ) + return if (connection.responseCode.isHttpSuccess()) { + UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) + } else { + UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() }) + } + } + + /** + * Holds the [URL.reader] response code and body text. + */ + data class UrlReaderResponse(val responseCode: Int, val body: String) +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt new file mode 100644 index 0000000..5f79472 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -0,0 +1,79 @@ +/* + * AbstractCommand.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +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 + +abstract class AbstractCommand { + abstract val name: String + abstract val help: List<String> + abstract val isOpOnly: Boolean + abstract val isPublic: Boolean + abstract val isVisible: Boolean + + val properties: MutableMap<String, String> = mutableMapOf() + + abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent) + + open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + if (!isOpOnly || isOpOnly == event.isChannelOp(channel)) { + for (h in help) { + event.sendMessage(helpCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic)) + } + return true + } + return false + } + + open fun initProperties(vararg keys: String) { + keys.forEach { + properties[it] = "" + } + } + + open fun isEnabled(): Boolean { + return true + } + + open fun matches(message: String): Boolean { + return false + } + + open fun setProperty(key: String, value: String) { + properties[key] = value + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt new file mode 100644 index 0000000..038e378 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -0,0 +1,62 @@ +/* + * ChannelFeed.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.FeedReader +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent + +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 isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val FEED_PROP = "feed" + } + + init { + initProperties(FEED_PROP) + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isEnabled()) { + properties[FEED_PROP]?.let { FeedReader(it, event).run() } + } + } + + override fun isEnabled(): Boolean { + return !properties[FEED_PROP].isNullOrBlank() + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt b/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt new file mode 100644 index 0000000..9608ca8 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -0,0 +1,66 @@ +/* + * Cycle.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 kotlinx.coroutines.delay +import kotlinx.coroutines.launch +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 : 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 isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { + if (event.isChannelOp(channel)) { + runBlocking { + launch { + 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 { + helpResponse(channel, args, event) + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Die.kt b/bin/main/net/thauvin/erik/mobibot/commands/Die.kt new file mode 100644 index 0000000..f271bfa --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Die.kt @@ -0,0 +1,62 @@ +/* + * Die.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.bot +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Die : AbstractCommand() { + override val name = "die" + override val help = emptyList<String>() + override val isOpOnly = true + override val isPublic = false + override val isVisible = false + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { + if (event.isChannelOp(channel) && (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 { + const val DIE_PROP = "die" + } + + init { + initProperties(DIE_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt b/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt new file mode 100644 index 0000000..d083c10 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -0,0 +1,147 @@ +/* + * Ignore.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +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.LinksManager +import org.pircbotx.hooks.types.GenericMessageEvent + +class Ignore : AbstractCommand() { + private val me = "me" + + init { + initProperties(IGNORE_PROP) + } + + override val name = IGNORE_CMD + override val help = listOf( + "To ignore a link posted to the channel:", + helpFormat("https://www.foo.bar %n"), + "To check your ignore status:", + helpFormat("%c $name"), + "To toggle your ignore status:", + helpFormat("%c $name $me") + ) + private val helpOp = help.plus( + arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]")) + ) + + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val IGNORE_CMD = "ignore" + const val IGNORE_PROP = IGNORE_CMD + private val ignored = mutableSetOf<String>() + + @JvmStatic + fun isNotIgnored(nick: String): Boolean { + return !ignored.contains(nick.lowercase()) + } + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val isMe = args.trim().equals(me, true) + if (isMe || !event.isChannelOp(channel)) { + val nick = event.user.nick.lowercase() + ignoreNick(nick, isMe, event) + } else { + ignoreOp(args, event) + } + } + + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + return if (event.isChannelOp(channel)) { + for (h in helpOp) { + event.sendMessage(helpCmdSyntax(h, event.bot().nick, true)) + } + true + } else { + super.helpResponse(channel, topic, event) + } + } + + private fun ignoreNick(sender: String, isMe: Boolean, event: GenericMessageEvent) { + if (isMe) { + if (ignored.remove(sender)) { + event.sendMessage("You are no longer ignored.") + } else { + ignored.add(sender) + event.sendMessage("You are now ignored.") + } + } else { + if (ignored.contains(sender)) { + event.sendMessage("You are currently ignored.") + } else { + event.sendMessage("You are not currently ignored.") + } + } + } + + private fun ignoreOp(args: String, event: GenericMessageEvent) { + if (args.isNotEmpty()) { + val nicks = args.lowercase().split(" ") + for (nick in nicks) { + val ignore = if (me == nick) { + nick.lowercase() + } else { + nick + } + if (!ignored.remove(ignore)) { + ignored.add(ignore) + } + } + } + + if (ignored.isNotEmpty()) { + event.sendMessage("The following nicks are ignored:") + event.sendList(ignored.sorted(), 8, isIndent = true) + } else { + event.sendMessage("No one is currently ${"ignored".bold()}.") + } + } + + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (IGNORE_PROP == key) { + ignored.addAll(value.split(LinksManager.TAG_MATCH)) + } + } + +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Info.kt b/bin/main/net/thauvin/erik/mobibot/commands/Info.kt new file mode 100644 index 0000000..ed0b6ef --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Info.kt @@ -0,0 +1,124 @@ +/* + * Info.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.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.plural +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.links.LinksManager +import net.thauvin.erik.mobibot.commands.seen.Seen +import net.thauvin.erik.mobibot.commands.tell.Tell +import org.pircbotx.hooks.types.GenericMessageEvent +import java.lang.management.ManagementFactory +import kotlin.time.DurationUnit +import kotlin.time.toDuration + +class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { + private val allVersions = listOf( + "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", + "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" + ) + override val name = "info" + override val help = listOf("To view information about the bot:", helpFormat("%c $name")) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + /** + * Converts milliseconds to year month week day hour and minutes. + */ + @JvmStatic + fun Long.toUptime(): String { + this.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, seconds, _ -> + val years = wholeDays / 365 + var days = wholeDays % 365 + val months = days / 30 + days %= 30 + val weeks = days / 7 + days %= 7 + + with(StringBuffer()) { + if (years > 0) { + append(years).append(" year".plural(years)).append(' ') + } + if (months > 0) { + append(months).append(" month".plural(months)).append(' ') + } + if (weeks > 0) { + append(weeks).append(" week".plural(weeks)).append(' ') + } + if (days > 0) { + append(days).append(" day".plural(days)).append(' ') + } + if (hours > 0) { + append(hours).append(" hour".plural(hours.toLong())).append(' ') + } + + if (minutes > 0) { + append(minutes).append(" minute".plural(minutes.toLong())) + } else { + append(seconds).append(" second".plural(seconds.toLong())) + } + + return toString() + } + } + } + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + event.sendList(allVersions, 1) + val info = StringBuilder() + info.append("Uptime: ") + .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) + .append(" [Entries: ") + .append(LinksManager.entries.links.size) + if (seen.isEnabled()) { + info.append(", Seen: ").append(seen.count()) + } + if (event.isChannelOp(channel)) { + if (tell.isEnabled()) { + info.append(", Messages: ").append(tell.size()) + } + if (LinksManager.socialManager.entriesCount() > 0) { + info.append(", Social: ").append(LinksManager.socialManager.entriesCount()) + } + } + info.append(", Recap: ").append(Recap.recaps.size).append(']') + event.sendMessage(info.toString()) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Me.kt b/bin/main/net/thauvin/erik/mobibot/commands/Me.kt new file mode 100644 index 0000000..ec7823b --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Me.kt @@ -0,0 +1,51 @@ +/* + * Me.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Me : AbstractCommand() { + override val name = "me" + override val help = listOf("To have the bot perform an action:", helpFormat("%c $name <action>")) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + event.bot().sendIRC().action(channel, args) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt b/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt new file mode 100644 index 0000000..b2293b0 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt @@ -0,0 +1,63 @@ +/* + * Modules.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import net.thauvin.erik.mobibot.Utils.sendList +import org.pircbotx.hooks.types.GenericMessageEvent + +class Modules(private val modules: List<String>, private val disabledModules: List<String>) : AbstractCommand() { + override val name = "modules" + override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name")) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + if (modules.isEmpty()) { + event.respondPrivateMessage("There are no enabled modules.") + } else { + event.respondPrivateMessage("The enabled modules are: ") + event.sendList(modules, 7, isIndent = true) + } + if (disabledModules.isNotEmpty()) { + event.respondPrivateMessage("The disabled modules are: ") + event.sendList(disabledModules, 7, isIndent = true) + } + } else { + helpResponse(channel, args, event) + } + } +} + diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt b/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt new file mode 100644 index 0000000..20a6635 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt @@ -0,0 +1,60 @@ +/* + * Msg.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +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 isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + val msg = args.split(" ", limit = 2) + if (args.length > 2) { + event.bot().sendIRC().message(msg[0], msg[1]) + event.respondPrivateMessage("A message was sent to ${msg[0]}") + } else { + helpResponse(channel, args, event) + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt b/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt new file mode 100644 index 0000000..85a03ab --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt @@ -0,0 +1,51 @@ +/* + * Nick.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +class Nick : AbstractCommand() { + override val name = "nick" + override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <new_nick>")) + override val isOpOnly = true + override val isPublic = true + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + event.bot().sendIRC().changeNick(args) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt b/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt new file mode 100644 index 0000000..77154c7 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt @@ -0,0 +1,81 @@ +/* + * Recap.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.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 : AbstractCommand() { + override val name = "recap" + override val help = listOf( + "To list the last 10 public channel messages:", + helpFormat("%c $name") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val MAX_RECAPS = 10 + + @JvmField + val recaps = mutableListOf<String>() + + /** + * Stores the last 10 public messages and actions. + */ + @JvmStatic + fun storeRecap(sender: String, message: String, isAction: Boolean) { + recaps.add( + LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + + " - $sender" + (if (isAction) " " else ": ") + message + ) + if (recaps.size > MAX_RECAPS) { + recaps.removeFirst() + } + } + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (recaps.isNotEmpty()) { + for (r in recaps) { + event.sendMessage(r) + } + } else { + event.sendMessage("Sorry, nothing to recap.") + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Say.kt b/bin/main/net/thauvin/erik/mobibot/commands/Say.kt new file mode 100644 index 0000000..7f76d35 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Say.kt @@ -0,0 +1,51 @@ +/* + * Say.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.isChannelOp +import org.pircbotx.hooks.types.GenericMessageEvent + +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 isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + event.bot().sendIRC().message(channel, args) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Users.kt b/bin/main/net/thauvin/erik/mobibot/commands/Users.kt new file mode 100644 index 0000000..33d6fef --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Users.kt @@ -0,0 +1,50 @@ +/* + * Users.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendList +import org.pircbotx.hooks.types.GenericMessageEvent + +class Users : AbstractCommand() { + override val name = "users" + override val help = listOf("To list the users present on the channel:", helpFormat("%c $name")) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val ch = event.bot().userChannelDao.getChannel(channel) + event.sendList(ch.users.map { if (it.channelsOpIn.contains(ch)) "@${it.nick}" else it.nick }, 8) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt b/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt new file mode 100644 index 0000000..896c569 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt @@ -0,0 +1,59 @@ +/* + * Versions.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.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.PircBotX +import org.pircbotx.hooks.types.GenericMessageEvent + +class Versions : AbstractCommand() { + private val allVersions = listOf( + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + + ", JVM ${System.getProperty("java.runtime.version")}", + "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" + ) + override val name = "versions" + override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) + override val isOpOnly = true + override val isPublic = false + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + event.sendList(allVersions, 1) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt new file mode 100644 index 0000000..1443d44 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -0,0 +1,151 @@ +/* + * Comment.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.links + +import net.thauvin.erik.mobibot.Constants +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.printComment +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.types.GenericMessageEvent + +class Comment : AbstractCommand() { + override val name = COMMAND + override val help = listOf( + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, use its label: ", + helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), + "To delete a comment, use its label and a minus sign: ", + helpFormat("${Constants.LINK_CMD}1.1:-") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val COMMAND = "comment" + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val cmds = args.substring(1).split("[.:]".toRegex(), 3) + val entryIndex = cmds[0].toInt() - 1 + + if (entryIndex < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) { + val entry: EntryLink = LinksManager.entries.links[entryIndex] + val commentIndex = cmds[1].toInt() - 1 + if (commentIndex < entry.comments.size) { + when (val cmd = cmds[2].trim()) { + "" -> showComment(entry, entryIndex, commentIndex, event) // L1.1: + "-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:- + else -> { + if (cmd.startsWith('?')) { // L1.1:?<author> + changeAuthor(channel, cmd, entry, entryIndex, commentIndex, event) + } else { // L1.1:<comment> + setComment(cmd, entry, entryIndex, commentIndex, event) + } + } + } + } + } + } + + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + if (super.helpResponse(channel, topic, event)) { + if (event.isChannelOp(channel)) { + event.sendMessage("To change a comment's author:") + event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?<nick>")) + } + return true + } + return false + } + + override fun matches(message: String): Boolean { + return message.matches("^${Constants.LINK_CMD}\\d+\\.\\d+:.*".toRegex()) + } + + private fun changeAuthor( + channel: String, + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent + ) { + if (event.isChannelOp(channel) && cmd.length > 1) { + val comment = entry.getComment(commentIndex) + comment.nick = cmd.substring(1) + event.sendMessage(printComment(entryIndex, commentIndex, comment)) + LinksManager.entries.save() + } else { + event.sendMessage("Please ask a channel op to change the author of this comment for you.") + } + } + + private fun deleteComment( + channel: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent + ) { + if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { + entry.deleteComment(commentIndex) + event.sendMessage("Comment ${entryIndex.toLinkLabel()}.${commentIndex + 1} removed.") + LinksManager.entries.save() + } else { + event.sendMessage("Please ask a channel op to delete this comment for you.") + } + } + + private fun setComment( + cmd: String, + entry: EntryLink, + entryIndex: Int, + commentIndex: Int, + event: GenericMessageEvent + ) { + entry.setComment(commentIndex, cmd, event.user.nick) + event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + LinksManager.entries.save() + } + + private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) { + event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt new file mode 100644 index 0000000..fba6b99 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -0,0 +1,207 @@ +/* + * LinksManager.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.links + +import net.thauvin.erik.mobibot.Constants +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.Companion.isNotIgnored +import net.thauvin.erik.mobibot.entries.Entries +import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.social.SocialManager +import org.jsoup.Jsoup +import org.pircbotx.hooks.types.GenericMessageEvent +import java.io.IOException + +class LinksManager : 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 isOpOnly = false + override val isPublic = false + override val isVisible = false + + init { + initProperties(TAGS_PROP, KEYWORDS_PROP) + } + + companion object { + val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*".toRegex() + const val KEYWORDS_PROP = "tags-keywords" + const val TAGS_PROP = "tags" + val TAG_MATCH = ", *| +".toRegex() + + /** + * Entries array + */ + @JvmField + val entries = Entries() + + /** + * Pinboard handler. + */ + @JvmField + val pinboard = Pinboard() + + /** + * Social Manager handler. + */ + @JvmField + val socialManager = SocialManager() + + /** + * Let the user know if the entries are too old to be modified. + */ + @JvmStatic + fun isUpToDate(event: GenericMessageEvent): Boolean { + if (entries.lastPubDate != today()) { + event.sendMessage("The links are too old to be updated.") + return false + } + return true + } + } + + 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 (isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(botNick))) { + val link = cmds[0].trim() + if (!isDupEntry(link, event)) { + var title = "" + val tags = ArrayList<String>(defaultTags) + if (cmds.size == 2) { + val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) + title = data[0].trim() + if (data.size > 1) { + tags.addAll(data[1].split(TAG_MATCH)) + } + } + + if (title.isBlank()) { + title = fetchTitle(link) + } + + if (title != Constants.NO_TITLE) { + matchTagKeywords(title, tags) + } + + // Links are old, clear them + if (entries.lastPubDate != today()) { + entries.links.clear() + } + + val entry = EntryLink(link, title, sender, login, channel, tags) + entries.links.add(entry) + val index = entries.links.lastIndexOf(entry) + event.sendMessage(printLink(index, entry)) + + pinboard.addPin(event.bot().serverHostname, entry) + + // Queue link for posting to social media. + socialManager.queueEntry(index) + + entries.save() + + if (Constants.NO_TITLE == entry.title) { + event.sendMessage("Please specify a title, by typing:") + event.sendMessage(helpFormat("${index.toLinkLabel()}:|This is the title")) + } + } + } + } + + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false + + override fun matches(message: String): Boolean { + return message.matches(LINK_MATCH) + } + + internal fun fetchTitle(link: String): String { + try { + val html = Jsoup.connect(link) + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") + .get() + val title = html.title() + if (title.isNotBlank()) { + return title + } + } catch (ignore: IOException) { + // Do nothing + } + return Constants.NO_TITLE + } + + private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean { + synchronized(entries) { + return try { + val match = entries.links.single { it.link == link } + event.sendMessage( + "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) + ) + true + } catch (ignore: NoSuchElementException) { + false + } + } + } + + internal fun matchTagKeywords(title: String, tags: MutableList<String>) { + for (match in keywords) { + val m = Regex.escape(match) + if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { + tags.add(match) + } + } + } + + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (KEYWORDS_PROP == key) { + keywords.addAll(value.split(TAG_MATCH)) + } else if (TAGS_PROP == key) { + defaultTags.addAll(value.split(TAG_MATCH)) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt new file mode 100644 index 0000000..ff4278d --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -0,0 +1,164 @@ +/* + * Posting.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.links + +import net.thauvin.erik.mobibot.Constants +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.LinksManager.Companion.entries +import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import net.thauvin.erik.mobibot.entries.EntryLink +import org.pircbotx.hooks.types.GenericMessageEvent + +class Posting : AbstractCommand() { + override val name = "posting" + override val help = listOf( + "Post a URL, by saying it on a line on its own:", + helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", + "To add a title, use its label and a pipe:", + helpFormat("${Constants.LINK_CMD}1:|This is the title"), + "To add a comment:", + helpFormat("${Constants.LINK_CMD}1:This is a comment"), + "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", + "To edit a comment, see: ", + helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + val cmds = args.substring(1).split(":", limit = 2) + val entryIndex = cmds[0].toInt() - 1 + + if (entryIndex < entries.links.size) { + val cmd = cmds[1].trim() + if (cmd.isBlank()) { + showEntry(entryIndex, event) // L1: + } else if (LinksManager.isUpToDate(event)) { + if (cmd == "-") { + removeEntry(channel, entryIndex, event) // L1:- + } else { + when (cmd[0]) { + '|' -> 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> + } + } + } + } + } + + override fun matches(message: String): Boolean { + return message.matches("${Constants.LINK_CMD}\\d+:.*".toRegex()) + } + + 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) + event.sendMessage(EntriesUtils.printComment(entryIndex, commentIndex, comment)) + entries.save() + } + + private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) { + if (cmd.length > 1) { + val entry: EntryLink = entries.links[entryIndex] + entry.title = cmd.substring(1).trim() + LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) + entries.save() + } + } + + private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[entryIndex] + if (entry.login == event.user.login || event.isChannelOp(channel)) { + val link = cmd.substring(1) + if (link.matches(LinksManager.LINK_MATCH)) { + val oldLink = entry.link + entry.link = link + LinksManager.pinboard.updatePin(event.bot().serverHostname, oldLink, entry) + event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) + entries.save() + } + } + } + + private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) { + if (event.isChannelOp(channel)) { + if (cmd.length > 1) { + val entry: EntryLink = entries.links[index] + entry.nick = cmd.substring(1) + LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.printLink(index, entry)) + entries.save() + } + } else { + event.sendMessage("Please ask a channel op to change the author of this link for you.") + } + } + + private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[index] + if (entry.login == event.user.login || event.isChannelOp(channel)) { + LinksManager.pinboard.deletePin(entry) + LinksManager.socialManager.removeEntry(index) + entries.links.removeAt(index) + event.sendMessage("Entry ${index.toLinkLabel()} removed.") + entries.save() + } else { + event.sendMessage("Please ask a channel op to remove this entry for you.") + } + } + + private fun showEntry(index: Int, event: GenericMessageEvent) { + val entry: EntryLink = entries.links[index] + event.sendMessage(EntriesUtils.printLink(index, entry)) + if (entry.tags.isNotEmpty()) { + event.sendMessage(EntriesUtils.printTags(index, entry)) + } + if (entry.comments.isNotEmpty()) { + val comments = entry.comments + for (i in comments.indices) { + event.sendMessage(EntriesUtils.printComment(index, i, comments[i])) + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt new file mode 100644 index 0000000..1662857 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -0,0 +1,87 @@ +/* + * Tags.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.links + +import net.thauvin.erik.mobibot.Constants +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 : 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 isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val COMMAND = "tags" + } + + 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 < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) { + val cmd = cmds[1].trim() + val entry: EntryLink = LinksManager.entries.links[index] + if (cmd.isNotEmpty()) { + if (entry.login == event.user.login || event.isChannelOp(channel)) { + entry.setTags(cmd) + LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) + event.sendMessage(EntriesUtils.printTags(index, entry)) + LinksManager.entries.save() + } else { + event.sendMessage("Please ask a channel op to change the tags for you.") + } + } else { + if (entry.tags.isNotEmpty()) { + event.sendMessage(EntriesUtils.printTags(index, entry)) + } else { + event.sendMessage("The entry has no tags. Why don't add some?") + } + } + } + } + + override fun matches(message: String): Boolean { + return message.matches("^${Constants.LINK_CMD}\\d+${Constants.TAG_CMD}:.*".toRegex()) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt new file mode 100644 index 0000000..825e374 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt @@ -0,0 +1,120 @@ +/* + * View.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.links + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.lastOrEmpty +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.links.LinksManager.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 : AbstractCommand() { + override val name = VIEW_CMD + override val help = listOf( + "To list or search the current URL posts:", + helpFormat("%c $name [<start>] [<query>]") + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + companion object { + const val MAX_ENTRIES = 6 + const val VIEW_CMD = "view" + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (entries.links.isNotEmpty()) { + val p = parseArgs(args) + showPosts(p.first, p.second, event) + } else { + event.sendMessage("There is currently nothing to view. Why don't you post something?") + } + } + + internal fun parseArgs(args: String): Pair<Int, String> { + var query = args.lowercase().trim() + var start = 0 + if (query.isEmpty() && entries.links.size > MAX_ENTRIES) { + start = entries.links.size - MAX_ENTRIES + } + if (query.matches("^\\d+(| .*)".toRegex())) { // view [<start>] [<query>] + val split = query.split(" ", limit = 2) + try { + start = split[0].toInt() - 1 + query = split.lastOrEmpty().trim() + if (start > entries.links.size) { + start = 0 + } + } catch (ignore: NumberFormatException) { + // Do nothing + } + } + return Pair(start, query) + } + + private fun showPosts(start: Int, query: String, event: GenericMessageEvent) { + var index = start + var entry: EntryLink + var sent = 0 + while (index < entries.links.size && sent < MAX_ENTRIES) { + entry = entries.links[index] + if (query.isNotBlank()) { + if (entry.matches(query)) { + event.sendMessage(EntriesUtils.printLink(index, entry, true)) + sent++ + } + } else { + event.sendMessage(EntriesUtils.printLink(index, entry, true)) + sent++ + } + index++ + if (sent == MAX_ENTRIES && index < entries.links.size) { + event.sendMessage("To view more, try: ") + event.sendMessage( + helpFormat( + helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) + ) + ) + } + } + if (sent == 0) { + event.sendMessage("No matches. Please try again.") + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt new file mode 100644 index 0000000..cfd2c27 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -0,0 +1,45 @@ +/* + * NickComparator.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.seen + +import java.io.Serializable + +class NickComparator : Comparator<String>, Serializable { + override fun compare(a: String, b: String): Int { + return a.lowercase().compareTo(b.lowercase()) + } + + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt new file mode 100644 index 0000000..c9ee0f3 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -0,0 +1,150 @@ +/* + * Seen.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.seen + +import com.google.common.collect.ImmutableSortedSet +import net.thauvin.erik.mobibot.Utils +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.loadSerialData +import net.thauvin.erik.mobibot.Utils.saveSerialData +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.commands.AbstractCommand +import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime +import org.pircbotx.User +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.util.* + + +class Seen(private val serialObject: String) : AbstractCommand() { + private val logger: Logger = LoggerFactory.getLogger(Seen::class.java) + private val allKeyword = "all" + val seenNicks = TreeMap<String, SeenNick>(NickComparator()) + + override val name = "seen" + override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) + private val helpOp = help.plus( + arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) + ) + override val isOpOnly = false + override val isPublic = true + override val isVisible = true + + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isEnabled()) { + if (args.isNotBlank() && !args.contains(' ')) { + val ch = event.bot().userChannelDao.getChannel(channel) + if (args == allKeyword && ch.isOp(event.user) && seenNicks.isNotEmpty()) { + event.sendMessage("The ${"seen".bold()} nicks are:") + event.sendList(seenNicks.keys.toList(), 7, separator = ", ", isIndent = true) + return + } + ch.users.forEach { + if (args.equals(it.nick, true)) { + event.sendMessage("${it.nick} is on ${channel}.") + return + } + } + if (seenNicks.containsKey(args)) { + val seenNick = seenNicks.getValue(args) + val lastSeen = System.currentTimeMillis() - seenNick.lastSeen + event.sendMessage("${seenNick.nick} was last seen on $channel ${lastSeen.toUptime()} ago.") + return + } + event.sendMessage("I haven't seen $args on $channel lately.") + } else { + helpResponse(channel, args, event) + } + } + } + + fun add(nick: String) { + if (isEnabled()) { + seenNicks[nick] = SeenNick(nick, System.currentTimeMillis()) + save() + } + } + + fun add(users: ImmutableSortedSet<User>) { + if (isEnabled()) { + users.forEach { + seenNicks[it.nick] = SeenNick(it.nick, System.currentTimeMillis()) + } + save() + } + } + + fun clear() { + seenNicks.clear() + } + + fun count(): Int = seenNicks.size + + override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { + return if (event.isChannelOp(channel)) { + for (h in helpOp) { + event.sendMessage(Utils.helpCmdSyntax(h, event.bot().nick, true)) + } + true + } else { + super.helpResponse(channel, topic, event) + } + } + + fun load() { + if (isEnabled()) { + @Suppress("UNCHECKED_CAST") + seenNicks.putAll( + loadSerialData( + serialObject, + TreeMap<String, SeenNick>(), + logger, + "seen nicknames" + ) as TreeMap<String, SeenNick> + ) + } + } + + fun save() { + saveSerialData(serialObject, seenNicks, logger, "seen nicknames") + } + + init { + load() + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt new file mode 100644 index 0000000..7924977 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -0,0 +1,41 @@ +/* + * SeenNick.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.seen + +import java.io.Serializable + +data class SeenNick(val nick: String, val lastSeen: Long) : Serializable { + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt new file mode 100644 index 0000000..061ca6a --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -0,0 +1,306 @@ +/* + * Tell.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.tell + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +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(private val serialObject: String) : AbstractCommand() { + // Messages queue + private val messages: MutableList<TellMessage> = mutableListOf() + + // Maximum number of days to keep messages + private var maxDays = 7 + + // Message maximum queue size + private var maxSize = 50 + + /** + * The tell command. + */ + override val name = "tell" + + override val help = listOf( + "To send a message to someone when they join the channel:", + helpFormat("%c $name <nick> <message>"), + "To view queued and sent messages:", + helpFormat("%c $name ${View.VIEW_CMD}"), + "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' + ) + override val isOpOnly: Boolean = false + override val isPublic: Boolean = isEnabled() + override val isVisible: Boolean = isEnabled() + + /** + * Cleans the messages queue. + */ + private fun clean(): Boolean { + return TellManager.clean(messages, maxDays.toLong()) + } + + override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { + if (isEnabled()) { + when { + args.isBlank() -> { + helpResponse(channel, args, event) + } + + args.startsWith(View.VIEW_CMD) -> { + if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { + viewAll(event) + } else { + viewMessages(event) + } + } + + args.startsWith("$TELL_DEL_KEYWORD ") -> { + deleteMessage(channel, args, event) + } + + else -> { + newMessage(channel, args, event) + } + } + if (clean()) { + save() + } + } + } + + // Delete message. + private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) { + val split = args.split(" ") + if (split.size == 2) { + val id = split[1] + if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) { + if (messages.removeIf { it.sender.equals(event.user.nick, true) && it.isReceived }) { + save() + event.sendMessage("Delivered messages have been deleted.") + } else { + event.sendMessage("No delivered messages were found.") + } + } else { + if (messages.removeIf { + it.id == id && + (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) + }) { + save() + event.sendMessage("The message was deleted from the queue.") + } else { + event.sendMessage("The specified message [ID $id] could not be found.") + } + } + } else { + helpResponse(channel, args, event) + } + } + + override fun isEnabled(): Boolean { + return maxSize > 0 && maxDays > 0 + } + + override fun setProperty(key: String, value: String) { + super.setProperty(key, value) + if (MAX_DAYS_PROP == key) { + maxDays = value.toIntOrDefault(maxDays) + } else if (MAX_SIZE_PROP == key) { + maxSize = value.toIntOrDefault(maxSize) + } + } + + // New message. + 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(event.user.nick, split[0], split[1].trim()) + messages.add(message) + save() + event.sendMessage("Message [ID ${message.id}] was queued for ${message.recipient.bold()}") + } else { + event.sendMessage("Sorry, the messages queue is currently full.") + } + } else { + helpResponse(channel, args, event) + } + } + + /** + * Saves the messages queue. + */ + private fun save() { + TellManager.save(serialObject, messages) + } + + /** + * Checks and sends messages. + */ + fun send(event: GenericUserEvent) { + val nickname = event.user.nick + if (isEnabled() && nickname != event.getBot<PircBotX>().nick) { + messages.filter { it.isMatch(nickname) }.forEach { message -> + if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) { + if (message.sender == nickname) { + if (event !is MessageEvent) { + event.user.send().message( + "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" + ) + message.isReceived = true + message.isNotified = true + save() + } + } else { + event.user.send().message( + "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" + ) + message.isReceived = true + save() + } + } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived + && !message.isNotified + ) { + event.user.send().message( + "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " + + "${message.recipient.bold()} on ${message.receptionDate}" + ) + message.isNotified = true + save() + } + } + } + } + + /** + * Returns the messages queue size. + * + * @return The size. + */ + fun size(): Int = messages.size + + // View all messages. + private fun viewAll(event: GenericMessageEvent) { + if (messages.isNotEmpty()) { + for (message in messages) { + event.sendMessage( + "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + + (if (message.isReceived) "DELIVERED]" else "QUEUED]") + ) + } + } else { + event.sendMessage("There are no messages in the queue.") + } + } + + // View messages. + private fun viewMessages(event: GenericMessageEvent) { + var hasMessage = false + for (message in messages.filter { it.isMatch(event.user.nick) }) { + if (!hasMessage) { + hasMessage = true + event.sendMessage("Here are your messages: ") + } + if (message.isReceived) { + event.sendMessage( + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" + ) + } else { + event.sendMessage( + message.sender.bold() + ARROW + message.recipient.bold() + + " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" + ) + } + event.sendMessage(helpFormat(message.message)) + } + if (!hasMessage) { + event.sendMessage("You have no messages in the queue.") + } else { + event.sendMessage("To delete one or all delivered messages:") + event.sendMessage( + helpFormat( + helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) + ) + ) + event.sendMessage(help.last()) + } + } + + companion object { + /** + * Max days property. + */ + const val MAX_DAYS_PROP = "tell-max-days" + + /** + * Max size property. + */ + const val MAX_SIZE_PROP = "tell-max-size" + + // Arrow + private const val ARROW = " --> " + + // All keyword + private const val TELL_ALL_KEYWORD = "all" + + //T he delete command. + private const val TELL_DEL_KEYWORD = "del" + } + + /** + * Creates a new instance. + */ + init { + initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP) + + // Load the message queue + messages.addAll(TellManager.load(serialObject)) + if (clean()) { + save() + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt new file mode 100644 index 0000000..b65a4da --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -0,0 +1,74 @@ +/* + * TellManager.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.tell + +import net.thauvin.erik.mobibot.Utils.loadSerialData +import net.thauvin.erik.mobibot.Utils.saveSerialData +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.time.Clock +import java.time.LocalDateTime + +/** + * The Tell Messages Manager. + */ +object TellManager { + private val logger: Logger = LoggerFactory.getLogger(TellManager::class.java) + + /** + * Cleans the messages queue. + */ + @JvmStatic + fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Long): Boolean { + if (logger.isDebugEnabled) logger.debug("Cleaning the messages.") + val today = LocalDateTime.now(Clock.systemUTC()) + return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays).isBefore(today) } + } + + /** + * Loads the messages. + */ + @JvmStatic + fun load(file: String): List<TellMessage> { + @Suppress("UNCHECKED_CAST") + return loadSerialData(file, emptyList<TellMessage>(), logger, "message queue") as List<TellMessage> + } + + /** + * Saves the messages. + */ + @JvmStatic + fun save(file: String, messages: List<TellMessage?>?) { + if (messages != null) { + saveSerialData(file, messages, logger, "messages") + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt new file mode 100644 index 0000000..d17fbb5 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -0,0 +1,104 @@ +/* + * TellMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.tell + +import java.io.Serializable +import java.time.Clock +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +/** + * Tell Message. + */ +class TellMessage( + /** + * Returns the message's sender. + */ + val sender: String, + + /** + * Returns the message's recipient. + */ + val recipient: String, + + /** + * Returns the message text. + */ + val message: String +) : Serializable { + /** + * Returns the queued date/time. + */ + var queued: LocalDateTime = LocalDateTime.now(Clock.systemUTC()) + + /** + * Returns the message id. + */ + var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + + /** + * Returns `true` if a notification was sent. + */ + var isNotified = false + + /** + * Returns `true` if the message was received. + */ + var isReceived = false + set(value) { + if (value) { + receptionDate = LocalDateTime.now(Clock.systemUTC()) + } + field = value + } + + /** + * Returns the message creating date. + */ + var receptionDate: LocalDateTime = LocalDateTime.MIN + + /** + * Matches the message sender or recipient. + */ + fun isMatch(nick: String?): Boolean { + return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true) + } + + override fun toString(): String { + return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " + + "queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}") + } + + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 2L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt b/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt new file mode 100644 index 0000000..e8676ec --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt @@ -0,0 +1,54 @@ +/* + * Entries.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 net.thauvin.erik.mobibot.Utils.today + +class Entries( + var channel: String = "", + var ircServer: String = "", + var logsDir: String = "", + var backlogs: String = "" +) { + val links = mutableListOf<EntryLink>() + + var lastPubDate = today() + + fun load() { + lastPubDate = FeedsManager.loadFeed(this) + } + + fun save() { + lastPubDate = today() + FeedsManager.saveFeed(this) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt new file mode 100644 index 0000000..9c09626 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -0,0 +1,83 @@ +/* + * EntriesUtils.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.green + +/** + * Entries utilities. + */ +object EntriesUtils { + /** + * Prints an entry's comment for display on the channel. + */ + @JvmStatic + fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = + ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") + + /** + * Prints an entry's link for display on the channel. + */ + @JvmStatic + @JvmOverloads + fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { + val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") + .append('[').append(entry.nick).append(']') + if (isView && entry.comments.isNotEmpty()) { + buff.append("[+").append(entry.comments.size).append(']') + } + buff.append(' ') + with(entry) { + if (Constants.NO_TITLE == title) { + buff.append(title) + } else { + buff.append(title.bold()) + } + buff.append(" ( ").append(link.green()).append(" )") + } + return buff.toString() + } + + /** + * Prints an entry's tags/categories for display on the channel. e.g. L1T: tag1, tag2 + */ + @JvmStatic + fun printTags(entryIndex: Int, entry: EntryLink): String = + entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") + + /** + * Builds link label based on its index. e.g: L1 + */ + @JvmStatic + fun Int.toLinkLabel(): String = Constants.LINK_CMD + (this + 1) +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt new file mode 100644 index 0000000..e18d692 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -0,0 +1,52 @@ +/* + * EntryComment.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 java.io.Serializable +import java.time.LocalDateTime + +/** + * Entry comments data class. + */ +data class EntryComment(var comment: String, var nick: String) : Serializable { + /** + * Creation date. + */ + val date: LocalDateTime = LocalDateTime.now() + + override fun toString(): String = "EntryComment{comment='$comment', date=$date, nick='$nick'}" + + companion object { + // Serial version UID + @Suppress("ConstPropertyName") + private const val serialVersionUID: Long = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt new file mode 100644 index 0000000..4a69446 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -0,0 +1,213 @@ +/* + * EntryLink.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.SyndCategory +import com.rometools.rome.feed.synd.SyndCategoryImpl +import net.thauvin.erik.mobibot.commands.links.LinksManager +import java.io.Serializable +import java.util.* + +/** + * The class used to store link entries. + */ +class EntryLink( + // Link's comments + val comments: MutableList<EntryComment> = mutableListOf(), + + // Tags/categories + val tags: MutableList<SyndCategory> = mutableListOf(), + + // Channel + var channel: String, + + // Creation date + var date: Date = Calendar.getInstance().time, + + // Link's URL + var link: String, + + // Author's login + var login: String = "", + + // Author's nickname + var nick: String, + + // Link's title + var title: String +) : Serializable { + /** + * Creates a new entry. + */ + constructor( + link: String, + title: String, + nick: String, + login: String, + channel: String, + tags: List<String?> + ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { + setTags(tags) + } + + /** + * Creates a new entry. + */ + constructor( + link: String, + title: String, + nick: String, + channel: String, + date: Date, + tags: List<SyndCategory> + ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { + this.tags.addAll(tags) + } + + /** + * Adds a new comment + */ + fun addComment(comment: EntryComment): Int { + comments.add(comment) + return comments.lastIndex + } + + /** + * Adds a new comment. + */ + fun addComment(comment: String, nick: String): Int { + return addComment(EntryComment(comment, nick)) + } + + /** + * Deletes a specific comment. + */ + 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) + } + + /** + * Formats the tags. + */ + fun formatTags(sep: String, prefix: String = ""): String { + return tags.joinToString(separator = sep, prefix = prefix) { it.name } + } + + /** + * Returns a comment. + */ + fun getComment(index: Int): EntryComment = comments[index] + + /** + * Returns true if a string is contained in the link, title, or nick. + */ + fun matches(match: String?): Boolean { + return if (match.isNullOrEmpty()) { + false + } else { + link.contains(match, true) || title.contains(match, true) || nick.contains(match, true) + } + } + + /** + * Sets a comment. + */ + fun setComment(index: Int, comment: String?, nick: String?) { + if (index < comments.size && !comment.isNullOrBlank() && !nick.isNullOrBlank()) { + comments[index] = EntryComment(comment, nick) + } + } + + /** + * Sets the tags. + */ + fun setTags(tags: String) { + setTags(tags.split(LinksManager.TAG_MATCH)) + } + + /** + * Sets the tags. + */ + private fun setTags(tags: List<String?>) { + if (tags.isNotEmpty()) { + var category: SyndCategoryImpl + for (tag in tags) { + if (!tag.isNullOrBlank()) { + val t = tag.lowercase() + val mod = t[0] + if (mod == '-') { + // Don't remove the channel tag + if (channel.substring(1) != t.substring(1)) { + category = SyndCategoryImpl() + category.name = t.substring(1) + this.tags.remove(category) + } + } else { + category = SyndCategoryImpl() + if (mod == '+') { + category.name = t.substring(1) + } else { + category.name = t + } + if (!this.tags.contains(category)) { + this.tags.add(category) + } + } + } + } + } + } + + /** + * Returns a string representation of the object. + */ + override fun toString(): String { + return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," + + "nick='$nick', tags=$tags, title='$title'}") + } + + companion object { + // Serial version UID + @Suppress("ConstPropertyName") + private const val serialVersionUID: Long = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt new file mode 100644 index 0000000..f786cb2 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -0,0 +1,187 @@ +/* + * FeedsManager.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.* +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.* +import kotlin.io.path.exists + +/** + * Manages the RSS feeds. + */ +class FeedsManager private constructor() { + companion object { + private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) + + // The file containing the current entries. + private const val CURRENT_XML = "current.xml" + + // The .xml extension. + private const val DOT_XML = ".xml" + + /** + * Loads the current feed. + */ + @JvmStatic + @Throws(IOException::class, FeedException::class) + fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): 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. + */ + @JvmStatic + fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) { + 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.isNotEmpty()) { + 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.removePrefix("#")}@${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() + DOT_XML + ) + ), 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.") + } + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt new file mode 100644 index 0000000..8c8e736 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -0,0 +1,131 @@ +/* + * AbstractModule.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +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 { + /** + * The module name. + */ + abstract val name: String + + /** + * The module's commands, if any. + */ + @JvmField + val commands: MutableList<String> = mutableListOf() + + @JvmField + val help: MutableList<String> = mutableListOf() + val properties: MutableMap<String, String> = mutableMapOf() + + /** + * Responds to a command. + */ + abstract fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) + + /** + * Returns the module's property keys. + */ + val propertyKeys: Set<String> + get() = properties.keys + + /** + * Returns `true` if the module has properties. + */ + fun hasProperties(): Boolean { + return properties.isNotEmpty() + } + + /** + * Responds with the module's help. + */ + open fun helpResponse(event: GenericMessageEvent): Boolean { + for (h in help) { + event.sendMessage(helpCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent)) + } + return true + } + + /** + * Initializes the properties. + */ + fun initProperties(vararg keys: String) { + for (key in keys) { + properties[key] = "" + } + } + + /** + * Returns `true` if the module is enabled. + */ + val isEnabled: Boolean + get() = if (hasProperties()) { + isValidProperties + } else { + true + } + + /** + * Returns `true` if the module responds to private messages. + */ + open val isPrivateMsgEnabled: Boolean = false + + /** + * Ensures that all properties have values. + */ + open val isValidProperties: Boolean + get() { + for (s in properties.keys) { + if (properties[s].isNullOrBlank()) { + return false + } + } + return true + } + + /** + * Sets a property key and value. + */ + fun setProperty(key: String, value: String) { + if (key.isNotBlank()) { + properties[key] = value + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt b/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt new file mode 100644 index 0000000..b7aae28 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt @@ -0,0 +1,87 @@ +/* + * Calc.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.objecthunter.exp4j.ExpressionBuilder +import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException +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 : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Calc::class.java) + + override val name = "Calc" + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + event.respond(calculate(args)) + } catch (e: IllegalArgumentException) { + if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e) + event.respond("No idea. This is the kind of math I don't get.") + } catch (e: UnknownFunctionOrVariableException) { + if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e) + event.respond("No idea. I must've some form of Dyscalculia.") + } + } else { + helpResponse(event) + } + } + + companion object { + // Calc command + private const val CALC_CMD = "calc" + + /** + * Performs a calculation. e.g.: 1 + 1 * 2 + */ + @JvmStatic + @Throws(IllegalArgumentException::class) + fun calculate(query: String): String { + val decimalFormat = DecimalFormat("#.##") + val calc = ExpressionBuilder(query).build() + return query.replace(" ", "") + " = " + decimalFormat.format(calc.evaluate()).bold() + } + } + + init { + commands.add(CALC_CMD) + help.add("To solve a mathematical calculation:") + help.add(helpFormat("%c $CALC_CMD <calculation>")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt new file mode 100644 index 0000000..bd92332 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -0,0 +1,176 @@ +/* + * ChatGpt.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.apache.commons.text.WordUtils +import org.json.JSONException +import org.json.JSONObject +import org.json.JSONWriter +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse + +class ChatGpt : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) + + override val name = CHATGPT_NAME + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val answer = chat( + args.trim(), properties[API_KEY_PROP], + properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() + ) + if (answer.isNotBlank()) { + event.sendMessage(WordUtils.wrap(answer, 400)) + } else { + event.respond("$name is stumped.") + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } catch (e: NumberFormatException) { + if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e) + event.respond("The $name module is misconfigured.") + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The service name. + */ + const val CHATGPT_NAME = "ChatGPT" + + /** + * The API Key property. + */ + const val API_KEY_PROP = "chatgpt-api-key" + + /** + * The max tokens property. + */ + const val MAX_TOKENS_PROP = "chatgpt-max-tokens" + + // ChatGPT API URL + private const val API_URL = "https://api.openai.com/v1/completions" + + // ChatGPT command + private const val CHATGPT_CMD = "chatgpt" + + + @JvmStatic + @Throws(ModuleException::class) + fun chat(query: String, apiKey: String?, maxTokens: Int): String { + if (!apiKey.isNullOrEmpty()) { + val prompt = JSONWriter.valueToString("Q:$query\nA:") + val request = HttpRequest.newBuilder() + .uri(URI.create(API_URL)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .header("User-Agent", Constants.USER_AGENT) + .POST( + HttpRequest.BodyPublishers.ofString( + """{ + "model": "text-davinci-003", + "prompt": $prompt, + "temperature": 0, + "max_tokens": $maxTokens, + "top_p": 1, + "frequency_penalty": 0, + "presence_penalty": 0 + }""".trimIndent() + ) + ) + .build() + try { + val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) + if (response.statusCode() == 200) { + try { + val jsonResponse = JSONObject(response.body()) + val choices = jsonResponse.getJSONArray("choices") + return choices.getJSONObject(0).getString("text").trim() + } catch (e: JSONException) { + throw ModuleException( + "$CHATGPT_CMD($query): JSON", + "A JSON error has occurred while conversing with $CHATGPT_NAME.", + e + ) + } + } else { + if (response.statusCode() == 429) { + throw ModuleException( + "$CHATGPT_CMD($query): Rate limit reached", + "Rate limit reached. Please try again later." + ) + } else { + throw IOException("HTTP Status Code: " + response.statusCode()) + } + } + } catch (e: IOException) { + throw ModuleException( + "$CHATGPT_CMD($query): IO", + "An IO error has occurred while conversing with $CHATGPT_NAME.", + e + ) + } + } else { + throw ModuleException("$CHATGPT_CMD($query)", "No $CHATGPT_NAME API key specified.") + } + } + } + + init { + commands.add(CHATGPT_CMD) + with(help) { + add("To get answers from $name:") + add(Utils.helpFormat("%c $CHATGPT_CMD <query>")) + add("For example:") + add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) + add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) + } + initProperties(API_KEY_PROP, MAX_TOKENS_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt new file mode 100644 index 0000000..d14056e --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -0,0 +1,159 @@ +/* + * CryptoPrices.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.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.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.sendList +import net.thauvin.erik.mobibot.Utils.sendMessage +import org.json.JSONObject +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException + +/** + * The Cryptocurrency Prices module. + */ +class CryptoPrices : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java) + + override val name = "CryptoPrices" + + /** + * Returns the cryptocurrency market price from + * [Coinbase](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices#get-spot-price). + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (CURRENCIES.isEmpty()) { + try { + loadCurrencies() + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + } + } + + val debugMessage = "crypto($cmd $args)" + if (args == CODES_KEYWORD) { + event.sendMessage("The supported currencies are:") + event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true) + } else 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 + } + event.respond("${price.base} current price is $amount [${CURRENCIES[price.currency]}]") + } catch (e: CryptoException) { + if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) + e.message?.let { + event.respond(it) + } + } catch (e: IOException) { + if (logger.isErrorEnabled) logger.error(debugMessage, e) + event.respond("An IO error has occurred while retrieving the cryptocurrency market price.") + } + } else { + helpResponse(event) + } + + } + + companion object { + // Crypto command + private const val CRYPTO_CMD = "crypto" + + // Fiat Currencies + private val CURRENCIES: MutableMap<String, String> = mutableMapOf() + + // Currency codes keyword + private const val CODES_KEYWORD = "codes" + + /** + * Get current market price. + */ + @JvmStatic + fun currentPrice(args: List<String>): CryptoPrice { + return if (args.size == 2) + spotPrice(args[0], args[1]) + else + spotPrice(args[0]) + } + + /** + * For testing purposes. + */ + fun getCurrencyName(code: String): String? { + return CURRENCIES[code] + } + + /** + * Loads the Fiat currencies.. + */ + @JvmStatic + @Throws(ModuleException::class) + fun loadCurrencies() { + try { + val json = JSONObject(CryptoPrice.apiCall(listOf("currencies"))) + val data = json.getJSONArray("data") + for (i in 0 until data.length()) { + val d = data.getJSONObject(i) + CURRENCIES[d.getString("id")] = d.getString("name") + } + } catch (e: CryptoException) { + throw ModuleException( + "loadCurrencies(): CE", + "An error has occurred while retrieving the currencies table.", + e + ) + } + } + } + + init { + commands.add(CRYPTO_CMD) + with(help) { + add("To retrieve a cryptocurrency's market price:") + add(helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) + add("For example:") + add(helpFormat("%c $CRYPTO_CMD BTC")) + add(helpFormat("%c $CRYPTO_CMD ETH EUR")) + add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) + add("To list the supported currencies:") + add(helpFormat("%c $CRYPTO_CMD $CODES_KEYWORD")) + } + loadCurrencies() + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt new file mode 100644 index 0000000..da0efd8 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -0,0 +1,222 @@ +/* + * CurrencyConverter.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.Utils.sendList +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.PublicMessage +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 +import java.text.DecimalFormat +import java.util.* + + +/** + * The CurrencyConverter module. + */ +class CurrencyConverter : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java) + + override val name = "CurrencyConverter" + + // Reload currency codes + private fun reload(apiKey: String?) { + if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) { + try { + loadSymbols(apiKey) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + } + } + } + + /** + * Converts the specified currencies. + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + reload(properties[API_KEY_PROP]) + + when { + SYMBOLS.isEmpty() -> { + event.respond(EMPTY_SYMBOLS_TABLE) + } + + args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> { + val msg = convertCurrency(properties[API_KEY_PROP], args) + event.respond(msg.msg) + if (msg.isError) { + helpResponse(event) + } + } + + args.contains(CODES_KEYWORD) -> { + event.sendMessage("The supported currency codes are:") + event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) + } + + else -> { + helpResponse(event) + } + } + } + + override fun helpResponse(event: GenericMessageEvent): Boolean { + reload(properties[API_KEY_PROP]) + + if (SYMBOLS.isEmpty()) { + event.sendMessage(EMPTY_SYMBOLS_TABLE) + } else { + val nick = event.bot().nick + event.sendMessage("To convert from one currency to another:") + event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) + event.sendMessage( + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to USD", nick, isPrivateMsgEnabled) + ) + ) + event.sendMessage("To list the supported currency codes:") + event.sendMessage( + helpFormat( + helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) + ) + ) + } + return true + } + + companion object { + /** + * The API Key property. + */ + const val API_KEY_PROP = "exchangerate-api-key" + + // Currency command + private const val CURRENCY_CMD = "currency" + + // Currency codes keyword + private const val CODES_KEYWORD = "codes" + + // Empty symbols table. + private const val EMPTY_SYMBOLS_TABLE = "Sorry, but the currency table is empty." + + // Currency symbols + private val SYMBOLS: TreeMap<String, String> = TreeMap() + + // Decimal format + private val DECIMAL_FORMAT = DecimalFormat("0.00#") + + /** + * Converts from a currency to another. + */ + @JvmStatic + fun convertCurrency(apiKey: String?, query: String): Message { + if (apiKey.isNullOrEmpty()) { + throw ModuleException("${CURRENCY_CMD}($query)", "No Exchange Rate API key specified.") + } + + val cmds = query.split(" ") + return if (cmds.size == 4) { + if (cmds[3] == cmds[1] || "0" == cmds[0]) { + PublicMessage("You're kidding, right?") + } else { + val to = cmds[1].uppercase() + val from = cmds[3].uppercase() + if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) { + try { + val amt = cmds[0].replace(",", "") + val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/pair/$to/$from/$amt") + val body = url.reader().body + val json = JSONObject(body) + + if (json.getString("result") == "success") { + val result = DECIMAL_FORMAT.format(json.getDouble("conversion_result")) + PublicMessage( + "${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}" + ) + } else { + ErrorMessage("Sorry, an error occurred while converting the currencies.") + } + } catch (ignore: IOException) { + ErrorMessage("Sorry, an IO error occurred while converting the currencies.") + } + } else { + ErrorMessage("Sounds like monopoly money to me!") + } + } + } else { + ErrorMessage("Invalid query. Let's try again.") + } + } + + /** + * Loads the currency ISO symbols. + */ + @JvmStatic + @Throws(ModuleException::class) + fun loadSymbols(apiKey: String?) { + if (!apiKey.isNullOrEmpty()) { + try { + val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/codes") + val json = JSONObject(url.reader().body) + if (json.getString("result") == "success") { + val codes = json.getJSONArray("supported_codes") + for (i in 0 until codes.length()) { + val code = codes.getJSONArray(i) + SYMBOLS[code.getString(0)] = code.getString(1) + } + } + } catch (e: IOException) { + throw ModuleException( + "loadCodes(): IOE", + "An IO error has occurred while retrieving the currencies.", + e + ) + } + } + } + } + + init { + commands.add(CURRENCY_CMD) + initProperties(API_KEY_PROP) + loadSymbols(properties[ChatGpt.API_KEY_PROP]) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt b/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt new file mode 100644 index 0000000..8420fb1 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt @@ -0,0 +1,87 @@ +/* + * Dice.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent + +/** + * The Dice module. + */ +class Dice : AbstractModule() { + override val name = "Dice" + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + val arg = if (args.isBlank()) "2d6" else args.trim() + val match = Regex("^([1-9]|[12]\\d|3[0-2])[dD]([1-9]|[12]\\d|3[0-2])$").find(arg) + if (match != null) { + val (dice, sides) = match.destructured + event.respond("you rolled " + roll(dice.toInt(), sides.toInt())) + } else { + helpResponse(event) + } + } + + companion object { + // Dice command + private const val DICE_CMD = "dice" + + @JvmStatic + fun roll(dice: Int, sides: Int): String { + val result = StringBuilder() + var total = 0 + + repeat(dice) { + val roll = (1..sides).random() + total += roll + + if (result.isNotEmpty()) { + result.append(" + ") + } + + result.append(roll.bold()) + } + + if (dice != 1) { + result.append(" = ${total.bold()}") + } + + return result.toString() + } + } + + init { + commands.add(DICE_CMD) + help.add("To roll 2 dice with 6 sides:") + help.add(helpFormat("%c $DICE_CMD [2d6]")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt new file mode 100644 index 0000000..f426d1e --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -0,0 +1,162 @@ +/* + * GoogleSearch.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.ReleaseInfo +import net.thauvin.erik.mobibot.Utils.capitalise +import net.thauvin.erik.mobibot.Utils.colorize +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.Utils.unescapeXml +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.NoticeMessage +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 : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java) + + override val name = "GoogleSearch" + + /** + * Searches Google. + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val results = searchGoogle( + args, + properties[API_KEY_PROP], + properties[CSE_KEY_PROP], + event.user.nick + ) + for (msg in results) { + if (msg.isError) { + event.respond(msg.msg.colorize(msg.color)) + } else { + event.sendMessage(channel, msg) + } + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } else { + helpResponse(event) + } + } + + companion object { + // Google API Key property + const val API_KEY_PROP = "google-api-key" + + // Google Custom Search Engine ID property + const val CSE_KEY_PROP = "google-cse-cx" + + // Google command + private const val GOOGLE_CMD = "google" + + /** + * Performs a search on Google. + */ + @JvmStatic + @Throws(ModuleException::class) + fun searchGoogle( + query: String, + apiKey: String?, + cseKey: String?, + quotaUser: String = ReleaseInfo.PROJECT + ): List<Message> { + if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { + throw ModuleException( + "${GoogleSearch::class.java.name} is disabled.", + "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." + ) + } + val results = mutableListOf<Message>() + if (query.isNotBlank()) { + try { + val url = URL( + "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + + ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" + ) + val json = JSONObject(url.reader().body) + if (json.has("items")) { + val ja = json.getJSONArray("items") + for (i in 0 until ja.length()) { + val j = ja.getJSONObject(i) + results.add(NoticeMessage(j.getString("title").unescapeXml())) + results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) + } + } else if (json.has("error")) { + val error = json.getJSONObject("error") + val message = error.getString("message") + throw ModuleException("searchGoogle($query): ${error.getInt("code")} : $message", message) + } else { + results.add(ErrorMessage("No results found.", Colors.RED)) + } + } catch (e: IOException) { + throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) + } catch (e: JSONException) { + throw ModuleException( + "searchGoogle($query): JSON", + "A JSON error has occurred searching Google.", + e + ) + } + } else { + results.add(ErrorMessage("Invalid query. Please try again.")) + } + return results + } + } + + init { + commands.add(GOOGLE_CMD) + help.add("To search Google:") + help.add(helpFormat("%c $GOOGLE_CMD <query>")) + initProperties(API_KEY_PROP, CSE_KEY_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt b/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt new file mode 100644 index 0000000..2760fa7 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt @@ -0,0 +1,105 @@ +/* + * Joke.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.jokeapi.exceptions.HttpErrorException +import net.thauvin.erik.jokeapi.exceptions.JokeException +import net.thauvin.erik.jokeapi.joke +import net.thauvin.erik.jokeapi.models.Type +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.colorize +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import org.json.JSONException +import org.pircbotx.Colors +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException + +/** + * The Joke module. + */ +class Joke : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Joke::class.java) + + override val name = "Joke" + + /** + * Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/). + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + with(event.bot()) { + try { + randomJoke().forEach { + sendIRC().notice(channel, it.msg.colorize(it.color)) + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } + } + + companion object { + // Joke command + private const val JOKE_CMD = "joke" + + /** + * Retrieves a random joke. + */ + @JvmStatic + @Throws(ModuleException::class) + fun randomJoke(): List<Message> { + return try { + val joke = joke(safe = true, type = Type.SINGLE, splitNewLine = true) + joke.joke.map { PublicMessage(it, Colors.CYAN) } + } catch (e: JokeException) { + throw ModuleException("randomJoke(): ${e.additionalInfo}", e.message, e) + } catch (e: HttpErrorException) { + throw ModuleException("randomJoke(): HTTP: ${e.statusCode}", e.message, e) + } catch (e: IOException) { + throw ModuleException("randomJoke(): IOE", "An IO error has occurred retrieving a random joke.", e) + } catch (e: JSONException) { + throw ModuleException("randomJoke(): JSON", "A parsing error has occurred retrieving a random joke.", e) + } + } + } + + init { + commands.add(JOKE_CMD) + help.add("To display a random joke:") + help.add(helpFormat("%c $JOKE_CMD")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt b/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt new file mode 100644 index 0000000..9ab2ead --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -0,0 +1,171 @@ +/* + * Lookup.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Constants +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 + +/** + * The Lookup module. + */ +class Lookup : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java) + + override val name = "Lookup" + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.matches("(\\S.)+(\\S)+".toRegex())) { + try { + event.respondWith(nslookup(args).prependIndent()) + } catch (ignore: UnknownHostException) { + if (args.matches( + ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") + .toRegex() + ) + ) { + try { + val lines = whois(args) + if (lines.isNotEmpty()) { + var line: String + var hasData = false + for (rawLine in lines) { + line = rawLine.trim() + if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) { + if (!hasData) { + event.respondWith(line) + hasData = true + } else { + event.bot().sendIRC().notice(event.user.nick, line) + } + } + } + } else { + event.respond("Unknown host.") + } + } catch (ioe: IOException) { + if (logger.isWarnEnabled) { + logger.warn("Unable to perform whois IP lookup: $args", ioe) + } + event.respond("Unable to perform whois IP lookup: ${ioe.message}") + } + } else { + event.respond("Unknown host.") + } + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The whois default host. + */ + const val WHOIS_HOST = "whois.arin.net" + + // Lookup command + private const val LOOKUP_CMD = "lookup" + + /** + * Performs a DNS lookup on the specified query. + */ + @JvmStatic + @Throws(UnknownHostException::class) + fun nslookup(query: String): String { + val buffer = StringBuilder() + val results = InetAddress.getAllByName(query) + var hostInfo: String + for (result in results) { + if (result.hostAddress == query) { + hostInfo = result.hostName + if (hostInfo == query) { + throw UnknownHostException() + } + } else { + hostInfo = result.hostAddress + } + if (buffer.isNotEmpty()) { + buffer.append(", ") + } + buffer.append(hostInfo) + } + return buffer.toString() + } + + /** + * Performs a whois IP query. + */ + @Throws(IOException::class) + private fun whois(query: String): List<String> { + return whois(query, WHOIS_HOST) + } + + /** + * Performs a whois IP query. + */ + @JvmStatic + @Throws(IOException::class) + fun whois(query: String, host: String): List<String> { + val whoisClient = WhoisClient() + val lines: List<String> + with(whoisClient) { + try { + defaultTimeout = Constants.CONNECT_TIMEOUT + connect(host) + soTimeout = Constants.CONNECT_TIMEOUT + setSoLinger(false, 0) + lines = if (WHOIS_HOST == host) { + query("n - $query").split("\n") + } else { + query(query).split("\n") + } + } finally { + disconnect() + } + } + return lines + } + } + + init { + commands.add(LOOKUP_CMD) + help.add("To perform a DNS lookup query:") + help.add(helpFormat("%c $LOOKUP_CMD <ip address or hostname>")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt b/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt new file mode 100644 index 0000000..3be3a5f --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -0,0 +1,149 @@ +/* + * Mastodon.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.prefixIfMissing +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.social.SocialModule +import org.json.JSONException +import org.json.JSONObject +import org.json.JSONWriter +import java.io.IOException +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse + +class Mastodon : SocialModule() { + override val name = "Mastodon" + + override val handle: String? + get() = properties[HANDLE_PROP] + + override val isAutoPost: Boolean + get() = isEnabled && properties[AUTO_POST_PROP].toBoolean() + + override val isValidProperties: Boolean + get() = !(properties[INSTANCE_PROP].isNullOrBlank() || properties[ACCESS_TOKEN_PROP].isNullOrBlank()) + + /** + * Formats the entry for posting. + */ + override fun formatEntry(entry: EntryLink): String { + return "${entry.title} (via ${entry.nick} on ${entry.channel})${formatTags(entry)}\n\n${entry.link}" + } + + private fun formatTags(entry: EntryLink): String { + return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } + .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } + } + + /** + * Posts on Mastodon. + */ + @Throws(ModuleException::class) + override fun post(message: String, isDm: Boolean): String { + return toot( + apiKey = properties[ACCESS_TOKEN_PROP], + instance = properties[INSTANCE_PROP], + handle = handle, + message = message, + isDm = isDm + ) + } + + companion object { + // Property keys + const val ACCESS_TOKEN_PROP = "mastodon-access-token" + const val AUTO_POST_PROP = "mastodon-auto-post" + const val HANDLE_PROP = "mastodon-handle" + const val INSTANCE_PROP = "mastodon-instance" + + private const val MASTODON_CMD = "mastodon" + private const val TOOT_CMD = "toot" + + /** + * Post on Mastodon. + */ + @JvmStatic + @Throws(ModuleException::class) + fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { + val request = HttpRequest.newBuilder() + .uri(URI.create("https://$instance/api/v1/statuses")) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer $apiKey") + .POST( + HttpRequest.BodyPublishers.ofString( + JSONWriter.valueToString( + if (isDm) { + mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") + } else { + mapOf("status" to message) + } + ) + ) + ) + .build() + try { + val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) + if (response.statusCode() == 200) { + return try { + val jsonResponse = JSONObject(response.body()) + if (isDm) { + jsonResponse.getString("content") + } else { + "Your message was posted to ${jsonResponse.getString("url")}" + } + } catch (e: JSONException) { + throw ModuleException("mastodonPost($message)", "A JSON error has occurred: ${e.message}", e) + } + } else { + throw IOException("Status Code: " + response.statusCode()) + } + } catch (e: IOException) { + throw ModuleException("mastodonPost($message)", "An IO error has occurred: ${e.message}", e) + } catch (e: InterruptedException) { + throw ModuleException("mastodonPost($message)", "An error has occurred: ${e.message}", e) + } + } + } + + init { + commands.add(MASTODON_CMD) + commands.add(TOOT_CMD) + help.add("To toot on Mastodon:") + help.add(Utils.helpFormat("%c $TOOT_CMD <message>")) + properties[AUTO_POST_PROP] = "false" + initProperties(ACCESS_TOKEN_PROP, HANDLE_PROP, INSTANCE_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt b/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt new file mode 100644 index 0000000..a7416c2 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -0,0 +1,45 @@ +/* + * ModuleException.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +/** + * The `ModuleException` class. + */ +class ModuleException @JvmOverloads constructor( + val debugMessage: String, + message: String? = null, + cause: Throwable? = null +) : Exception(message, cause) { + companion object { + @Suppress("ConstPropertyName") + private const val serialVersionUID = 1L + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt b/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt new file mode 100644 index 0000000..944dbc1 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt @@ -0,0 +1,83 @@ +/* + * Ping.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils.bot +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent + +/** + * The Ping module. + */ +class Ping : AbstractModule() { + override val name = "Ping" + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + event.bot().sendIRC().action(channel, randomPing()) + } + + companion object { + /** + * The ping responses. + */ + @JvmField + val PINGS = listOf( + "is barely alive.", + "is trying to stay awake.", + "has gone fishing.", + "is somewhere over the rainbow.", + "has fallen and can't get up.", + "is running. You better go chase it.", + "has just spontaneously combusted.", + "is talking to itself... don't interrupt. That's rude.", + "is bartending at an AA meeting.", + "is hibernating.", + "is saving energy: apathetic mode activated.", + "is busy. Go away!" + ) + + @JvmStatic + fun randomPing(): String { + return PINGS[PINGS.indices.random()] + } + + /** + * The ping command. + */ + private const val PING_CMD = "ping" + } + + init { + commands.add(PING_CMD) + help.add("To ping the bot:") + help.add(helpFormat("%c $PING_CMD")) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt new file mode 100644 index 0000000..a299d8d --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -0,0 +1,114 @@ +/* + * RockPaperScissors.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent + + +/** + * Simple module example in Kotlin. + */ +class RockPaperScissors : AbstractModule() { + override val name = "RockPaperScissors" + + init { + with(commands) { + add(Hands.ROCK.name.lowercase()) + add(Hands.PAPER.name.lowercase()) + add(Hands.SCISSORS.name.lowercase()) + } + + with(help) { + add("To play Rock Paper Scissors:") + add( + helpFormat( + "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" + + " | ${Hands.SCISSORS.name.lowercase()}" + ) + ) + } + } + + enum class Hands(val action: String) { + ROCK("crushes") { + override fun beats(hand: Hands): Boolean { + return hand == SCISSORS + } + }, + PAPER("covers") { + override fun beats(hand: Hands): Boolean { + return hand == ROCK + } + }, + SCISSORS("cuts") { + override fun beats(hand: Hands): Boolean { + return hand == PAPER + } + }; + + abstract fun beats(hand: Hands): Boolean + } + + companion object { + // For testing. + fun winLoseOrDraw(player: String, bot: String): String { + val hand = Hands.valueOf(player.uppercase()) + val botHand = Hands.valueOf(bot.uppercase()) + + return when { + hand == botHand -> "draw" + hand.beats(botHand) -> "win" + else -> "lose" + } + } + } + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + val hand = Hands.valueOf(cmd.uppercase()) + val botHand = Hands.entries[(0..Hands.entries.size).random()] + when { + hand == botHand -> { + event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.") + } + + hand.beats(botHand) -> { + event.respond("${hand.name.bold()} ${hand.action} ${botHand.name} » You ${"win".bold()}!") + } + + else -> { + event.respond("${botHand.name.bold()} ${botHand.action} ${hand.name} » You ${"lose".bold()}!") + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt b/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt new file mode 100644 index 0000000..dcae5e7 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -0,0 +1,236 @@ +/* + * StockQuote.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +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.reader +import net.thauvin.erik.mobibot.Utils.sendMessage +import net.thauvin.erik.mobibot.Utils.unescapeXml +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.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 : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java) + + override val name = "StockQuote" + + /** + * Returns the specified stock quote from Alpha Vantage. + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val messages = getQuote(args, properties[API_KEY_PROP]) + for (msg in messages) { + event.sendMessage(channel, msg) + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The API property key. + */ + const val API_KEY_PROP = "alphavantage-api-key" + + /** + * The Invalid Symbol error string. + */ + const val INVALID_SYMBOL = "Invalid symbol." + + // API URL + private const val API_URL = "https://www.alphavantage.co/query?function=" + + // Quote command + private const val STOCK_CMD = "stock" + + @Throws(ModuleException::class) + private fun getJsonResponse(response: String, debugMessage: String): JSONObject { + return if (response.isNotBlank()) { + val json = JSONObject(response) + try { + val info = json.getString("Information") + if (info.isNotEmpty()) { + throw ModuleException(debugMessage, info.unescapeXml()) + } + } catch (ignore: JSONException) { + // Do nothing + } + try { + var error = json.getString("Note") + if (error.isNotEmpty()) { + throw ModuleException(debugMessage, error.unescapeXml()) + } + error = json.getString("Error Message") + if (error.isNotEmpty()) { + throw ModuleException(debugMessage, error.unescapeXml()) + } + } catch (ignore: JSONException) { + // Do nothing + } + json + } else { + throw ModuleException(debugMessage, "Empty Response.") + } + } + + /** + * Retrieves a stock quote. + */ + @JvmStatic + @Throws(ModuleException::class) + fun getQuote(symbol: String, apiKey: String?): List<Message> { + if (apiKey.isNullOrBlank()) { + throw ModuleException( + "${StockQuote::class.java.name} is disabled.", + "${STOCK_CMD.capitalise()} is disabled. The API key is missing." + ) + } + val messages = mutableListOf<Message>() + if (symbol.isNotBlank()) { + val debugMessage = "getQuote($symbol)" + var response: String + try { + with(messages) { + // Search for symbol/keywords + response = URL( + "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" + + apiKey.encodeUrl() + ).reader().body + var json = getJsonResponse(response, debugMessage) + val symbols = json.getJSONArray("bestMatches") + if (symbols.isEmpty) { + messages.add(ErrorMessage(INVALID_SYMBOL)) + } else { + val symbolInfo = symbols.getJSONObject(0) + + // Get quote for symbol + response = URL( + "${API_URL}GLOBAL_QUOTE&symbol=" + + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" + + apiKey.encodeUrl() + ).reader().body + json = getJsonResponse(response, debugMessage) + val quote = json.getJSONObject("Global Quote") + if (quote.isEmpty) { + add(ErrorMessage(INVALID_SYMBOL)) + } else { + + add( + PublicMessage( + "Symbol: " + quote.getString("01. symbol").unescapeXml() + + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' + ) + ) + + val pad = 10 + + add( + PublicMessage( + "Price:".padEnd(pad).prependIndent() + + quote.getString("05. price").unescapeXml() + ) + ) + add( + PublicMessage( + "Previous:".padEnd(pad).prependIndent() + + quote.getString("08. previous close").unescapeXml() + ) + ) + + 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() + + quote.getString(it.second).unescapeXml() + ) + ) + } + + add( + NoticeMessage( + "Change:".padEnd(pad).prependIndent() + + quote.getString("09. change").unescapeXml() + + " [" + quote.getString("10. change percent").unescapeXml() + ']' + ) + ) + } + } + } + } catch (e: IOException) { + throw ModuleException("$debugMessage: IOE", "An IO error has occurred retrieving a stock quote.", e) + } catch (e: NullPointerException) { + throw ModuleException("$debugMessage: NPE", "An error has occurred retrieving a stock quote.", e) + } + } else { + messages.add(ErrorMessage(INVALID_SYMBOL)) + } + return messages + } + } + + init { + commands.add(STOCK_CMD) + help.add("To retrieve a stock quote:") + help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>")) + initProperties(API_KEY_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/War.class b/bin/main/net/thauvin/erik/mobibot/modules/War.class new file mode 100644 index 0000000000000000000000000000000000000000..6c36e541b639c5b04b624194b3b2df6b3d11ba55 GIT binary patch literal 2452 zcmeHJUvm>T5MMb??6`r@5CVjMYDuZxQXf#BNMXjcP07@D7&{5m;i02DE4IR&<VrfX zdE`?u)6T$qzXso<)79MtNOEL4Q<&*PAFN&Jw7b7u?N9QbfBpUv0Pe%LB`6SBliYfC z%;Kq#9@pZT7b#!(%Ay>`AvfL=rb|#HaAuE9nHMrS@;3cF9#{gi&Cd4s0|IBCcHVv* zSS{peoj|#@(dcgWTDW2EQM=LKa!S9^Yi)K3Tuv<v4`MCszBh0R?=v}65!%iT9yeNT zG$^*(%^wLYKg|tMdeZ3s-0ZdpEaf3M)l|}0ChZOjV_t?O0yDodjqbk_QrHJLt=4uh zNK*}Y3C<H(e49sGYo*I@krZBUOU;c6r+i2Q6-`9QtWc6pwDLn9nR~P{zcuDmL=&Yg z#fsLmiq>64>sdwX+g5~z224t2sn6+w(|>E-@Qu|hu-)Xuasp7RD5G|N!YlKbK!jD! z$1&#NB*(ro|K1NL=M-@}Rzo`Cw#On~tx>g`z@XSvG>VO-YRa`1Lr#@;^}}*<X%Vc6 zCtN$kH1(0zDD#))(C21?2h6K!%iW|$<qurq0R&xc3>)$G6kUHP2)+u3S=<^GhQ@Np zcZI|b*~LsOc0&Rl^E`z7KWkQj6}YqjXW=q|bJgQyax_`&V1m6k^10q(*!hliDq!Ib z)56WO#iAVxL*Pc|pIj}-^-|Uz$nt9Kcw_>X`mwYk;u~@*jKDw*$Do9R80UIN>5;d` zt*@Do#!Kjxe)M|E_Tn&HFT)K2zno0t0~UY4o+WVYDD*9C=|k`rk2EuZQBL`fmnCP# z)nOuZfVcN=-dL$;#&b<*yc`~^y8~T2itaJf$WzPpI}poolaS{p*Y3>~_&-5MAL%+E zP#LIL2mGOM%q!!~Qg@FP(se@ycnu0;@qT+GxMn&S@0Z{<fzyYd1_BFRVGKUct_=;` z27CeW&p-)QVHW04TSYyU<xI{)C0YL@n=inrWPTbJ(fSPP1^5)b;J*M^#_=4k+)d^H zl|NwRr=<%oVD(k9?+T7{xCh{KxQgRKqF3M=P@EC?4A*DiI_hrM8p`9|zR2;`&GGgn zT9<(=L*j=(6>8}FD_p%;%$%no-W?CZ&EmEc{MR|*w%-%(Dil}H5OJ?yj!qO(fi-r? U%)mD|7N8CuN`O1~b69Tu4Ue7BiU0rr literal 0 HcmV?d00001 diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt b/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt new file mode 100644 index 0000000..80a06fa --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -0,0 +1,250 @@ +/* + * Weather2.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +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.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.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 : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java) + + override val name = "Weather" + + /** + * Fetches the weather data from a specific city. + */ + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val messages = getWeather(args, properties[API_KEY_PROP]) + if (messages[0].isError) { + helpResponse(event) + } else { + for (msg in messages) { + event.sendMessage(channel, msg) + } + } + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The OpenWeatherMap API Key property. + */ + const val API_KEY_PROP = "owm-api-key" + + // Weather command + private const val WEATHER_CMD = "weather" + + /** + * Converts and rounds temperature from °F to °C. + */ + fun ftoC(d: Double): Pair<Int, Int> { + val c = (d - 32) * 5 / 9 + return d.roundToInt() to c.roundToInt() + } + + /** + * Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found. + */ + fun getCountry(countryCode: String): Country { + for (c in Country.entries) { + if (c.value.equals(countryCode, ignoreCase = true)) { + return c + } + } + return Country.UNITED_STATES + } + + /** + * Retrieves the weather data. + */ + @JvmStatic + @Throws(ModuleException::class) + fun getWeather(query: String, apiKey: String?): List<Message> { + if (apiKey.isNullOrBlank()) { + throw ModuleException( + "${Weather2::class.java.name} is disabled.", + "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." + ) + } + val owm = OWM(apiKey) + val messages = mutableListOf<Message>() + owm.unit = OWM.Unit.IMPERIAL + if (query.isNotBlank()) { + val argv = query.split(",") + if (argv.size in 1..2) { + val city = argv[0].trim() + val code: String = if (argv.size > 1 && argv[1].isNotBlank()) { + argv[1].trim() + } else { + "US" + } + try { + val country = getCountry(code) + val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) { + owm.currentWeatherByZipCode(city.toInt(), country) + } else { + owm.currentWeatherByCityName(city, country) + } + if (cwd.hasCityName()) { + messages.add( + PublicMessage( + "City: ${cwd.cityName}, " + + country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" + ) + ) + cwd.mainData?.let { + with(it) { + if (hasTemp()) { + temp?.let { t -> + val (f, c) = ftoC(t) + messages.add(PublicMessage("Temperature: ${f}°F, ${c}°C")) + } + } + if (hasHumidity()) { + humidity?.let { h -> + messages.add(NoticeMessage("Humidity: ${h.roundToInt()}%")) + } + } + } + } + if (cwd.hasWindData()) { + cwd.windData?.let { + if (it.hasSpeed()) { + it.speed?.let { s -> + val w = mphToKmh(s) + messages.add(NoticeMessage("Wind: ${w.first} mph, ${w.second} km/h")) + } + } + } + } + if (cwd.hasWeatherList()) { + val condition = StringBuilder("Condition:") + cwd.weatherList?.let { + for (w in it) { + w?.let { + condition.append(' ') + .append(w.getDescription().capitalise()) + .append('.') + } + } + messages.add(NoticeMessage(condition.toString())) + } + } + if (cwd.hasCityId()) { + cwd.cityId?.let { + if (it > 0) { + messages.add( + NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) + ) + } else { + messages.add( + NoticeMessage( + "https://openweathermap.org/find?q=" + + "$city,${code.uppercase()}".encodeUrl(), + Colors.GREEN + ) + ) + } + } + } + } + } catch (e: APIException) { + if (e.code == 404) { + throw ModuleException( + "getWeather($query): API ${e.code}", + "The requested city was not found.", + e + ) + } else { + throw ModuleException("getWeather($query): API ${e.code}", e.message, e) + } + } catch (e: NullPointerException) { + throw ModuleException("getWeather($query): NPE", "Unable to perform weather lookup.", e) + } + } + } + if (messages.isEmpty()) { + messages.add(ErrorMessage("Invalid syntax.")) + } + return messages + } + + /** + * Converts and rounds temperature from mph to km/h. + */ + fun mphToKmh(w: Double): Pair<Int, Int> { + val kmh = w * 1.60934 + return w.roundToInt() to kmh.roundToInt() + } + } + + init { + commands.add(WEATHER_CMD) + with(help) { + add("To display weather information:") + add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) + add("For example:") + add(helpFormat("%c $WEATHER_CMD paris, fr")) + add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.") + } + initProperties(API_KEY_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt new file mode 100644 index 0000000..a72efab --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -0,0 +1,142 @@ +/* + * WolframAlpha.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.isHttpSuccess +import net.thauvin.erik.mobibot.Utils.reader +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 +import java.net.URL + +class WolframAlpha : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(WolframAlpha::class.java) + + override val name = "WolframAlpha" + + private fun getUnits(unit: String?): String { + return if (unit?.lowercase() == METRIC) { + METRIC + } else { + IMPERIAL + } + } + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + if (args.isNotBlank()) { + try { + val query = args.trim().split("units=", limit = 2, ignoreCase = true) + event.sendMessage( + queryWolfram( + query[0].trim(), + units = if (query.size == 2) { + getUnits(query[1].trim()) + } else { + getUnits(properties[UNITS_PROP]) + }, + appId = properties[APPID_KEY_PROP] + ) + ) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } else { + helpResponse(event) + } + } + + companion object { + /** + * The Wolfram Alpha API Key property. + */ + const val APPID_KEY_PROP = "wolfram-appid" + + /** + * The Wolfram units properties + */ + const val UNITS_PROP = "wolfram-units" + + const val METRIC = "metric" + const val IMPERIAL = "imperial" + + // Wolfram command + private const val WOLFRAM_CMD = "wolfram" + + // Wolfram Alpha API URL + private const val API_URL = "http://api.wolframalpha.com/v1/spoken?appid=" + + @JvmStatic + @Throws(ModuleException::class) + fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String { + if (!appId.isNullOrEmpty()) { + try { + val urlReader = URL("${API_URL}${appId}&units=${units}&i=" + query.encodeUrl()).reader() + if (urlReader.responseCode.isHttpSuccess()) { + return urlReader.body + } else { + throw ModuleException( + "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", + urlReader.body.ifEmpty { + "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" + } + ) + } + } catch (ioe: IOException) { + throw ModuleException( + "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe + ) + } + } else { + throw ModuleException("wolfram($query): No API Key", "No Wolfram Alpha API key specified.") + } + } + } + + init { + commands.add(WOLFRAM_CMD) + with(help) { + add("To get answers from Wolfram Alpha:") + add(Utils.helpFormat("%c $WOLFRAM_CMD <query> [units=(${METRIC}|${IMPERIAL})]")) + add("For example:") + add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas")) + add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric")) + } + initProperties(APPID_KEY_PROP, UNITS_PROP) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt b/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt new file mode 100644 index 0000000..18072bc --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -0,0 +1,390 @@ +/* + * WorldTime.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +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 +import java.time.temporal.ChronoField + +/** + * The WorldTime module. + */ +class WorldTime : AbstractModule() { + override val name = "WorldTime" + + companion object { + /** + * Beats (Internet Time) keyword + */ + const val BEATS_KEYWORD = ".beats" + + /** + * Supported countries + */ + val COUNTRIES_MAP = buildMap<String, String> { + put("AG", "America/Antigua") + put("AI", "America/Anguilla") + put("AE", "Asia/Dubai") + put("AD", "Europe/Andorra") + put("AKDT", "America/Anchorage") + put("AF", "Asia/Kabul") + put("AKST", "America/Anchorage") + put("AL", "Europe/Tirane") + put("AM", "Asia/Yerevan") + put("AO", "Africa/Luanda") + put("AQ", "Antarctica/South_Pole") + put("AR", "America/Argentina/Buenos_Aires") + put("AS", "Pacific/Pago_Pago") + put("AT", "Europe/Vienna") + put("AU", "Australia/Sydney") + put("AW", "America/Aruba") + put("AX", "Europe/Mariehamn") + put("AZ", "Asia/Baku") + put("BA", "Europe/Sarajevo") + put("BB", "America/Barbados") + put("BD", "Asia/Dhaka") + put("BE", "Europe/Brussels") + put("BEAT", BEATS_KEYWORD) + put("BF", "Africa/Ouagadougou") + put("BG", "Europe/Sofia") + put("BH", "Asia/Bahrain") + put("BI", "Africa/Bujumbura") + put("BJ", "Africa/Porto-Novo") + put("BL", "America/St_Barthelemy") + put("BM", "Atlantic/Bermuda") + put("BMT", BEATS_KEYWORD) + put("BN", "Asia/Brunei") + put("BO", "America/La_Paz") + put("BQ", "America/Kralendijk") + put("BR", "America/Sao_Paulo") + put("BS", "America/Nassau") + put("BT", "Asia/Thimphu") + put("BW", "Africa/Gaborone") + put("BY", "Europe/Minsk") + put("BZ", "America/Belize") + put("CA", "America/Montreal") + put("CC", "Indian/Cocos") + put("CD", "Africa/Kinshasa") + put("CDT", "America/Chicago") + put("CET", "CET") + put("CF", "Africa/Bangui") + put("CG", "Africa/Brazzaville") + put("CH", "Europe/Zurich") + put("CI", "Africa/Abidjan") + put("CK", "Pacific/Rarotonga") + put("CL", "America/Santiago") + put("CM", "Africa/Douala") + put("CN", "Asia/Shanghai") + put("CO", "America/Bogota") + put("CR", "America/Costa_Rica") + put("CST", "America/Chicago") + put("CU", "Cuba") + put("CV", "Atlantic/Cape_Verde") + put("CW", "America/Curacao") + put("CX", "Indian/Christmas") + put("CY", "Asia/Nicosia") + put("CZ", "Europe/Prague") + put("DE", "Europe/Berlin") + put("DJ", "Africa/Djibouti") + put("DK", "Europe/Copenhagen") + put("DM", "America/Dominica") + put("DO", "America/Santo_Domingo") + put("DZ", "Africa/Algiers") + put("EC", "Pacific/Galapagos") + put("EDT", "America/New_York") + put("EE", "Europe/Tallinn") + put("EG", "Africa/Cairo") + put("EH", "Africa/El_Aaiun") + put("ER", "Africa/Asmara") + put("ES", "Europe/Madrid") + put("EST", "America/New_York") + put("ET", "Africa/Addis_Ababa") + put("FI", "Europe/Helsinki") + put("FJ", "Pacific/Fiji") + put("FK", "Atlantic/Stanley") + put("FM", "Pacific/Yap") + put("FO", "Atlantic/Faroe") + put("FR", "Europe/Paris") + put("GA", "Africa/Libreville") + put("GB", "Europe/London") + put("GD", "America/Grenada") + put("GE", "Asia/Tbilisi") + put("GF", "America/Cayenne") + put("GG", "Europe/Guernsey") + put("GH", "Africa/Accra") + put("GI", "Europe/Gibraltar") + put("GL", "America/Thule") + put("GM", "Africa/Banjul") + put("GMT", "GMT") + put("GN", "Africa/Conakry") + put("GP", "America/Guadeloupe") + put("GQ", "Africa/Malabo") + put("GR", "Europe/Athens") + put("GS", "Atlantic/South_Georgia") + put("GT", "America/Guatemala") + put("GU", "Pacific/Guam") + put("GW", "Africa/Bissau") + put("GY", "America/Guyana") + put("HK", "Asia/Hong_Kong") + put("HN", "America/Tegucigalpa") + put("HR", "Europe/Zagreb") + put("HST", "Pacific/Honolulu") + put("HT", "America/Port-au-Prince") + put("HU", "Europe/Budapest") + put("ID", "Asia/Jakarta") + put("IE", "Europe/Dublin") + put("IL", "Asia/Tel_Aviv") + put("IM", "Europe/Isle_of_Man") + put("IN", "Asia/Kolkata") + put("IO", "Indian/Chagos") + put("IQ", "Asia/Baghdad") + put("IR", "Asia/Tehran") + put("IS", "Atlantic/Reykjavik") + put("IT", "Europe/Rome") + put("JE", "Europe/Jersey") + put("JM", "Jamaica") + put("JO", "Asia/Amman") + put("JP", "Asia/Tokyo") + put("KE", "Africa/Nairobi") + put("KG", "Asia/Bishkek") + put("KH", "Asia/Phnom_Penh") + put("KI", "Pacific/Tarawa") + put("KM", "Indian/Comoro") + put("KN", "America/St_Kitts") + put("KP", "Asia/Pyongyang") + put("KR", "Asia/Seoul") + put("KW", "Asia/Riyadh") + put("KY", "America/Cayman") + put("KZ", "Asia/Oral") + put("LA", "Asia/Vientiane") + put("LB", "Asia/Beirut") + put("LC", "America/St_Lucia") + put("LI", "Europe/Vaduz") + put("LK", "Asia/Colombo") + put("LR", "Africa/Monrovia") + put("LS", "Africa/Maseru") + put("LT", "Europe/Vilnius") + put("LU", "Europe/Luxembourg") + put("LV", "Europe/Riga") + put("LY", "Africa/Tripoli") + put("MA", "Africa/Casablanca") + put("MC", "Europe/Monaco") + put("MD", "Europe/Chisinau") + put("MDT", "America/Denver") + put("ME", "Europe/Podgorica") + put("MF", "America/Marigot") + put("MG", "Indian/Antananarivo") + put("MH", "Pacific/Majuro") + put("MK", "Europe/Skopje") + put("ML", "Africa/Timbuktu") + put("MM", "Asia/Yangon") + put("MN", "Asia/Ulaanbaatar") + put("MO", "Asia/Macau") + put("MP", "Pacific/Saipan") + put("MQ", "America/Martinique") + put("MR", "Africa/Nouakchott") + put("MS", "America/Montserrat") + put("MST", "America/Denver") + put("MT", "Europe/Malta") + put("MU", "Indian/Mauritius") + put("MV", "Indian/Maldives") + put("MW", "Africa/Blantyre") + put("MX", "America/Mexico_City") + put("MY", "Asia/Kuala_Lumpur") + put("MZ", "Africa/Maputo") + put("NA", "Africa/Windhoek") + put("NC", "Pacific/Noumea") + put("NE", "Africa/Niamey") + put("NF", "Pacific/Norfolk") + put("NG", "Africa/Lagos") + put("NI", "America/Managua") + put("NL", "Europe/Amsterdam") + put("NO", "Europe/Oslo") + put("NP", "Asia/Kathmandu") + put("NR", "Pacific/Nauru") + put("NU", "Pacific/Niue") + put("NZ", "Pacific/Auckland") + put("OM", "Asia/Muscat") + put("PA", "America/Panama") + put("PDT", "America/Los_Angeles") + put("PE", "America/Lima") + put("PF", "Pacific/Tahiti") + put("PG", "Pacific/Port_Moresby") + put("PH", "Asia/Manila") + put("PK", "Asia/Karachi") + put("PL", "Europe/Warsaw") + put("PM", "America/Miquelon") + put("PN", "Pacific/Pitcairn") + put("PR", "America/Puerto_Rico") + put("PS", "Asia/Gaza") + put("PST", "America/Los_Angeles") + put("PT", "Europe/Lisbon") + put("PW", "Pacific/Palau") + put("PY", "America/Asuncion") + put("QA", "Asia/Qatar") + put("RE", "Indian/Reunion") + put("RO", "Europe/Bucharest") + put("RS", "Europe/Belgrade") + put("RU", "Europe/Moscow") + put("RW", "Africa/Kigali") + put("SA", "Asia/Riyadh") + put("SB", "Pacific/Guadalcanal") + put("SC", "Indian/Mahe") + put("SD", "Africa/Khartoum") + put("SE", "Europe/Stockholm") + put("SG", "Asia/Singapore") + put("SH", "Atlantic/St_Helena") + put("SI", "Europe/Ljubljana") + put("SJ", "Atlantic/Jan_Mayen") + put("SK", "Europe/Bratislava") + put("SL", "Africa/Freetown") + put("SM", "Europe/San_Marino") + put("SN", "Africa/Dakar") + put("SO", "Africa/Mogadishu") + put("SR", "America/Paramaribo") + put("SS", "Africa/Juba") + put("ST", "Africa/Sao_Tome") + put("SV", "America/El_Salvador") + put("SX", "America/Lower_Princes") + put("SY", "Asia/Damascus") + put("SZ", "Africa/Mbabane") + put("TC", "America/Grand_Turk") + put("TD", "Africa/Ndjamena") + put("TF", "Indian/Kerguelen") + put("TG", "Africa/Lome") + put("TH", "Asia/Bangkok") + put("TJ", "Asia/Dushanbe") + put("TK", "Pacific/Fakaofo") + put("TL", "Asia/Dili") + put("TM", "Asia/Ashgabat") + put("TN", "Africa/Tunis") + put("TO", "Pacific/Tongatapu") + put("TR", "Europe/Istanbul") + put("TT", "America/Port_of_Spain") + put("TV", "Pacific/Funafuti") + put("TW", "Asia/Taipei") + put("TZ", "Africa/Dar_es_Salaam") + put("UA", "Europe/Kiev") + put("UG", "Africa/Kampala") + put("UK", "Europe/London") + put("UM", "Pacific/Wake") + put("US", "America/New_York") + put("UTC", "UTC") + put("UY", "America/Montevideo") + put("UZ", "Asia/Tashkent") + put("VA", "Europe/Vatican") + put("VC", "America/St_Vincent") + put("VE", "America/Caracas") + put("VG", "America/Tortola") + put("VI", "America/St_Thomas") + put("VN", "Asia/Ho_Chi_Minh") + put("VU", "Pacific/Efate") + put("WF", "Pacific/Wallis") + put("WS", "Pacific/Apia") + put("YE", "Asia/Aden") + put("YT", "Indian/Mayotte") + put("ZA", "Africa/Johannesburg") + put("ZM", "Africa/Lusaka") + put("ZULU", "Zulu") + put("ZW", "Africa/Harare") + ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } + .forEach { tz -> put(tz, tz) } + } + + // 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 ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") + + /** + * Returns the current Internet (beat) Time. + */ + 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 "%c%03d".format('@', beats) + } + + /** + * Returns the time for the given timezone/city. + */ + @JvmStatic + 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 ${internetTime().bold()} $BEATS_KEYWORD" + } else { + (ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf) + + tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ').bold()) + } + } else { + "Unsupported country/zone. Please try again." + } + } + } + + 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)) + } + } + + override val isPrivateMsgEnabled = true + + init { + with(help) { + add("To display a country's current date/time:") + add(helpFormat("%c $TIME_CMD [<country code or zone>]")) + add("For a listing of the supported countries/zones:") + add(helpFormat("%c $TIME_CMD $ZONES_ARGS")) + } + commands.add(TIME_CMD) + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt new file mode 100644 index 0000000..0607936 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -0,0 +1,37 @@ +/* + * ErrorMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.msg + +/** + * The `ErrorMessage` class. + */ +class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isError = true) diff --git a/bin/main/net/thauvin/erik/mobibot/msg/Message.kt b/bin/main/net/thauvin/erik/mobibot/msg/Message.kt new file mode 100644 index 0000000..23a33b9 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/Message.kt @@ -0,0 +1,65 @@ +/* + * Message.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.msg + +import net.thauvin.erik.semver.Constants + +/** + * The `Message` class. + */ +open class Message @JvmOverloads constructor( + var msg: String, + var color: String = DEFAULT_COLOR, + var isNotice: Boolean = false, + isError: Boolean = false, + var isPrivate: Boolean = false +) { + companion object { + var DEFAULT_COLOR = Constants.EMPTY + } + + init { + if (isError) { + isNotice = true + } + } + + /** Error flag. */ + var isError = isError + set(value) { + if (value) isNotice = true + field = value + } + + override fun toString(): String { + return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, msg='$msg')" + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt new file mode 100644 index 0000000..037d504 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -0,0 +1,38 @@ +/* + * NoticeMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.msg + +/** + * The `NoticeMessage` class. + */ +class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isNotice = true) + diff --git a/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt new file mode 100644 index 0000000..b424fdf --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -0,0 +1,37 @@ +/* + * PrivateMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.msg + +/** + * The `PrivateMessage` class. + */ +class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : + Message(msg, color, isPrivate = true) diff --git a/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt new file mode 100644 index 0000000..9c5e088 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -0,0 +1,36 @@ +/* + * PublicMessage.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.msg + +/** + * The `PublicMessage` class. + */ +class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color) diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt new file mode 100644 index 0000000..91f2dd9 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -0,0 +1,116 @@ +/* + * SocialManager.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.social + +import net.thauvin.erik.mobibot.Addons +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.util.* + +/** + * Social Manager. + */ +class SocialManager { + private val entries: MutableSet<Int> = HashSet() + private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) + private val modules = ArrayList<SocialModule>() + private val timer = Timer(true) + + /** + * Adds social modules. + */ + fun add(addons: Addons, vararg modules: SocialModule) { + modules.forEach { + if (addons.add(it)) { + this.modules.add(it) + } + } + } + + /** + * Returns the number of entries. + */ + fun entriesCount(): Int = entries.size + + /** + * Sends a social notification (dm, etc.) + */ + fun notification(msg: String) { + modules.forEach { + it.notification(msg) + } + } + + /** + * Posts to social media. + */ + fun postEntry(index: Int) { + if (entries.contains(index)) { + modules.forEach { + it.postEntry(index) + } + entries.remove(index) + } + } + + /** + * Queues an entry for posting to social media. + */ + fun queueEntry(index: Int) { + if (modules.isNotEmpty()) { + entries.add(index) + if (logger.isDebugEnabled) { + logger.debug("Scheduling {} for posting on social media.", index.toLinkLabel()) + } + timer.schedule(SocialTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L) + } + } + + /** + * Removes entries from queue. + */ + fun removeEntry(index: Int) { + entries.remove(index) + } + + /** + * Posts all entries on shutdown. + */ + fun shutdown() { + timer.cancel() + entries.forEach { + postEntry(it) + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt new file mode 100644 index 0000000..b594670 --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -0,0 +1,96 @@ +/* + * SocialModule.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.social + +import net.thauvin.erik.mobibot.commands.links.LinksManager +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import net.thauvin.erik.mobibot.entries.EntryLink +import net.thauvin.erik.mobibot.modules.AbstractModule +import net.thauvin.erik.mobibot.modules.ModuleException +import org.pircbotx.hooks.types.GenericMessageEvent +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +abstract class SocialModule : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) + + abstract val handle: String? + abstract val isAutoPost: Boolean + + abstract fun formatEntry(entry: EntryLink): String + + /** + * Sends a DM. + */ + fun notification(msg: String) { + if (isEnabled && !handle.isNullOrBlank()) { + try { + post(message = msg, isDm = true) + if (logger.isDebugEnabled) logger.debug("Notified $handle on $name: $msg") + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn("Failed to notify $handle on $name: $msg", e) + } + } + } + + abstract fun post(message: String, isDm: Boolean): String + + /** + * Post entry to social media. + */ + fun postEntry(index: Int) { + if (isAutoPost && LinksManager.entries.links.size >= index) { + try { + if (logger.isDebugEnabled) { + logger.debug("Posting {} to $name.", index.toLinkLabel()) + } + post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn( + "Failed to post entry ${index.toLinkLabel()} on $name.", + e + ) + } + } + } + + override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { + try { + event.respond(post("$args (by ${event.user.nick} on $channel)", false)) + } catch (e: ModuleException) { + if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) + e.message?.let { + event.respond(it) + } + } + } +} diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt new file mode 100644 index 0000000..3fd315e --- /dev/null +++ b/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -0,0 +1,40 @@ +/* + * SocialTimer.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.social + +import java.util.* + +class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() { + override fun run() { + socialManager.postEntry(index) + } +} diff --git a/bin/test/current.xml b/bin/test/current.xml new file mode 100644 index 0000000..535a400 --- /dev/null +++ b/bin/test/current.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ current.xml + ~ + ~ Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + ~ + ~ 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. + --> + +<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> + <channel> + <title>#mobibot IRC Links + https://www.mobitopia.org/mobibot/logs + Links from irc.example.com on #mobibot + en + Sun, 31 Oct 2021 21:45:11 GMT + 2021-10-31T21:45:11Z + en + + Example 2 + https://www.example.com/2 + Posted by <b>Skynx</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a> + tag2-1 + tag2-2 + Sun, 31 Oct 2021 21:45:11 GMT + https://www.foo.com + mobibot@irc.libera.chat (Skynx) + 2021-10-31T00:01:00Z + + + Example 1 + https://www.example.com/1 + 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. + + tag1-1 + tag1-2 + Sun, 31 Oct 2021 21:43:15 GMT + https://www.example.com/ + mobibot@irc.libera.chat (ErikT) + 2021-10-31T00:00:00Z + + + diff --git a/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt b/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt new file mode 100644 index 0000000..afb8ce6 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt @@ -0,0 +1,86 @@ +/* + * AddonsTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import assertk.assertThat +import assertk.assertions.containsExactly +import assertk.assertions.isEqualTo +import assertk.assertions.size +import net.thauvin.erik.mobibot.commands.ChannelFeed +import net.thauvin.erik.mobibot.commands.Cycle +import net.thauvin.erik.mobibot.commands.Die +import net.thauvin.erik.mobibot.commands.Ignore +import net.thauvin.erik.mobibot.commands.links.Comment +import net.thauvin.erik.mobibot.commands.links.View +import net.thauvin.erik.mobibot.modules.* +import kotlin.test.Test +import java.util.* + +class AddonsTest { + private val p = Properties().apply { + put("disabled-modules", "war,dice Lookup") + put("disabled-commands", "View | comment") + } + private val addons = Addons(p) + + @Test + fun addTest() { + // Modules + addons.add(Joke()) + addons.add(RockPaperScissors()) + addons.add(War()) + addons.add(Dice()) + addons.add(Lookup()) + assertThat(addons::modules).size().isEqualTo(2) + assertThat(addons.names.modules, "names.modules").containsExactly("Joke", "RockPaperScissors") + + // Commands + addons.add(View()) + addons.add(Comment()) + addons.add(Cycle()) + addons.add(Die()) // invisible + addons.add(ChannelFeed("channel")) // no properties, disabled + p[Ignore.IGNORE_PROP] = "nick" + addons.add(Ignore()) + assertThat(addons::commands).size().isEqualTo(3) + + assertThat(addons.names.ops, "names.ops").containsExactly("cycle") + + assertThat(addons.names.commands, "names.command").containsExactly( + "joke", + "rock", + "paper", + "scissors", + "ignore" + ) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt new file mode 100644 index 0000000..a3994ec --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -0,0 +1,60 @@ +/* + * ExceptionSanitizer.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import net.thauvin.erik.mobibot.Utils.obfuscate +import net.thauvin.erik.mobibot.Utils.replaceEach +import net.thauvin.erik.mobibot.modules.ModuleException + +object ExceptionSanitizer { + /** + * Returns a sanitized exception to avoid displaying api keys, etc. in CI logs. + */ + 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(this) { + if (!cause?.message.isNullOrBlank()) { + return ModuleException( + debugMessage, + cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), + this + ) + } else if (!message.isNullOrBlank()) { + return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) + } + } + } + return this + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt b/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt new file mode 100644 index 0000000..6e5fa8f --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -0,0 +1,78 @@ +/* + * FeedReaderTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.* +import com.rometools.rome.io.FeedException +import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed +import net.thauvin.erik.mobibot.msg.Message +import kotlin.test.Test +import java.io.IOException +import java.net.MalformedURLException +import java.net.UnknownHostException + +/** + * The `FeedReader Test` class. + */ +class FeedReaderTest { + @Test + fun readFeedTest() { + var messages = readFeed("https://feeds.thauvin.net/ethauvin") + assertThat(messages, "messages").all { + size().isEqualTo(10) + index(1).prop(Message::msg).contains("erik.thauvin.net") + } + + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") + assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing") + + messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42) + assertThat(messages, "messages").size().isEqualTo(84) + messages.forEachIndexed { i, m -> + if (i % 2 == 0) { + assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum") + } else { + assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/") + } + } + + assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) + + assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java) + + assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) + + assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt b/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt new file mode 100644 index 0000000..1384a72 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt @@ -0,0 +1,85 @@ +/* + * LocalProperties.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import org.testng.annotations.BeforeSuite +import java.io.IOException +import java.net.InetAddress +import java.net.UnknownHostException +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* + +/** + * Access to `local.properties`. + */ +open class LocalProperties { + @BeforeSuite(alwaysRun = true) + fun loadProperties() { + val localPath = Paths.get("local.properties") + if (Files.exists(localPath)) { + try { + Files.newInputStream(localPath).use { stream -> localProps.load(stream) } + } catch (ignore: IOException) { + // Do nothing + } + } + } + + companion object { + private val localProps = Properties() + + fun getHostName(): String { + val ciName = System.getenv("CI_NAME") + return ciName ?: try { + InetAddress.getLocalHost().hostName + } catch (ignore: UnknownHostException) { + "Unknown Host" + } + } + + fun getProperty(key: String): String { + return if (localProps.containsKey(key)) { + localProps.getProperty(key) + } else { + val env = System.getenv(keyToEnv(key)) + env?.let { + localProps.setProperty(key, env) + } + env + } + } + + private fun keyToEnv(key: String): String { + return key.replace('-', '_').uppercase() + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt b/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt new file mode 100644 index 0000000..b527fc5 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt @@ -0,0 +1,81 @@ +/* + * PinboardTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.entries.EntryLink +import org.testng.Assert.assertFalse +import org.testng.Assert.assertTrue +import kotlin.test.Test +import java.net.URL + +class PinboardTest : LocalProperties() { + private val pinboard = Pinboard() + + @Test + fun testPinboard() { + val apiToken = getProperty("pinboard-api-token") + val url = "https://www.example.com/${(1000..5000).random()}" + val ircServer = "irc.test.com" + val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) + + pinboard.setApiToken(apiToken) + + pinboard.addPin(ircServer, entry) + assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin") + + entry.link = "https://www.example.com/${(5001..9999).random()}" + pinboard.updatePin(ircServer, url, entry) + assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin") + + entry.title = "Foo Title" + pinboard.updatePin(ircServer, entry.link, entry) + assertTrue(validatePin(apiToken, url = entry.link, entry.title), "updatePin(${entry.title}") + + pinboard.deletePin(entry) + assertFalse(validatePin(apiToken, url = entry.link), "deletePin") + } + + private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { + val response = + URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body + + matches.forEach { + if (!response.contains(it)) { + return false + } + } + + return response.contains(url) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt b/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt new file mode 100644 index 0000000..7b7ba8c --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt @@ -0,0 +1,278 @@ +/* + * UtilsTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.length +import net.thauvin.erik.mobibot.Utils.appendIfMissing +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.colorize +import net.thauvin.erik.mobibot.Utils.cyan +import net.thauvin.erik.mobibot.Utils.encodeUrl +import net.thauvin.erik.mobibot.Utils.getIntProperty +import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpCmdSyntax +import net.thauvin.erik.mobibot.Utils.helpFormat +import net.thauvin.erik.mobibot.Utils.lastOrEmpty +import net.thauvin.erik.mobibot.Utils.obfuscate +import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.reader +import net.thauvin.erik.mobibot.Utils.red +import net.thauvin.erik.mobibot.Utils.replaceEach +import net.thauvin.erik.mobibot.Utils.reverseColor +import net.thauvin.erik.mobibot.Utils.toIntOrDefault +import net.thauvin.erik.mobibot.Utils.toIsoLocalDate +import net.thauvin.erik.mobibot.Utils.toUtcDateTime +import net.thauvin.erik.mobibot.Utils.today +import net.thauvin.erik.mobibot.Utils.underline +import net.thauvin.erik.mobibot.Utils.unescapeXml +import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR +import org.pircbotx.Colors +import org.testng.annotations.BeforeClass +import kotlin.test.Test +import java.io.File +import java.io.IOException +import java.net.URL +import java.time.LocalDateTime +import java.util.* + +/** + * The `Utils Test` class. + */ +class UtilsTest { + private val ascii = + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + private val cal = Calendar.getInstance() + private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) + private val test = "This is a test." + + @BeforeClass + fun setUp() { + cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 + } + + @Test + fun testAppendIfMissing() { + val dir = "dir" + val sep = '/' + val url = "https://erik.thauvin.net" + assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") + .isEqualTo(dir + File.separatorChar) + assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") + assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") + } + + @Test + fun testBold() { + assertThat(1.bold(), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + assertThat(2L.bold(), "bold(2L)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) + assertThat(ascii.bold(), "ascii.bold()").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) + } + + + @Test + fun testCapitalise() { + assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test") + assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test") + assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test) + assertThat("".capitalise(), "capitalize()").isEqualTo("") + } + + @Test + fun textCapitaliseWords() { + assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") + assertThat("Already Capitalized".capitalizeWords(), "already capitalized") + .isEqualTo("Already Capitalized") + assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") + } + + @Test + fun testColorize() { + assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( + Colors.REVERSE + ascii + Colors.REVERSE + ) + assertThat(ascii.colorize(Colors.RED), "red.colorize()") + .isEqualTo(Colors.RED + ascii + Colors.NORMAL) + assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") + .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) + assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") + assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") + assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) + assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") + .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) + } + + @Test + fun testCyan() { + assertThat(ascii.cyan()).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) + } + + @Test + fun testEncodeUrl() { + assertThat("Hello Günter".encodeUrl()).isEqualTo("Hello%20G%C3%BCnter") + } + + @Test + fun testGetIntProperty() { + val p = Properties() + p["one"] = "1" + p["two"] = "two" + assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1) + assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2) + assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3) + } + + @Test + fun testGreen() { + assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) + } + + @Test + fun testHelpCmdSyntax() { + val bot = "mobibot" + assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") + .isEqualTo("$bot: $test $bot $test") + assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + } + + @Test + fun testHelpFormat() { + assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") + .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") + assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") + .isEqualTo(test.prependIndent()) + assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") + .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) + + } + + @Test + fun testIsoLocalDate() { + assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17") + assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17") + } + + @Test + fun testLastOrEmpty() { + val two = listOf("1", "2") + assertThat(two.lastOrEmpty(), "lastOrEmpty(1,2)").isEqualTo("2") + val one = listOf("1") + assertThat(one.lastOrEmpty(), "lastOrEmpty(1)").isEqualTo("") + } + + @Test + fun testObfuscate() { + assertThat(ascii.obfuscate(), "obfuscate()").all { + length().isEqualTo(ascii.length) + isEqualTo(("x".repeat(ascii.length))) + } + assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ") + } + + @Test + fun testPlural() { + val week = "week" + val weeks = "weeks" + + for (i in -1..3) { + assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week) + } + } + + @Test + fun testReplaceEach() { + val search = arrayOf("one", "two", "three") + val replace = arrayOf("1", "2", "3") + assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") + .isEqualTo(replace.joinToString(",")) + + assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) + + assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") + .isEqualTo(test.replace("t", "").replace("e", "E")) + + assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") + .isEqualTo(test) + } + + @Test + fun testRed() { + assertThat(ascii.red()).isEqualTo(ascii.colorize(Colors.RED)) + } + + @Test + fun testReverseColor() { + assertThat(ascii.reverseColor()).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) + } + + @Test + fun testToday() { + assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate()) + } + + @Test + fun testToIntOrDefault() { + assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10) + assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2) + } + + @Test + fun testUnderline() { + assertThat(ascii.underline()).isEqualTo(ascii.colorize(Colors.UNDERLINE)) + } + + @Test + fun testUnescapeXml() { + assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( + "" + ) + } + + @Test + @Throws(IOException::class) + fun testUrlReader() { + val reader = URL("https://postman-echo.com/status/200").reader() + assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}") + assertThat(reader.responseCode).isEqualTo(200) + } + + @Test + fun testUtcDateTime() { + assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30") + assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt new file mode 100644 index 0000000..99ba951 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -0,0 +1,58 @@ +/* + * InfoTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 assertk.assertThat +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime +import kotlin.test.Test + +class InfoTest { + @Test + fun testToUptime() { + assertThat( + 547800300076L.toUptime(), + "upTime(full)" + ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") + assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") + assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") + assertThat( + 1320300000L.toUptime(), + "upTime(weeks days hours minutes)" + ).isEqualTo("2 weeks 1 day 6 hours 45 minutes") + assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") + assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") + assertThat(59000L.toUptime(), "upTime(59 seconds)").isEqualTo("59 seconds") + assertThat(0L.toUptime(), "upTime(0 second)").isEqualTo("0 second") + + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt new file mode 100644 index 0000000..b6fbbff --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -0,0 +1,60 @@ +/* + * RecapTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.matches +import assertk.assertions.prop +import assertk.assertions.size +import kotlin.test.Test + +class RecapTest { + @Test + fun storeRecapTest() { + for (i in 1..20) { + Recap.storeRecap("sender$i", "test $i", false) + } + assertThat(Recap.recaps, "Recap.recaps").all { + size().isEqualTo(Recap.MAX_RECAPS) + prop(MutableList::first) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) + prop(MutableList::last) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) + } + + Recap.storeRecap("sender", "test action", true) + assertThat(Recap.recaps.last()) + .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt new file mode 100644 index 0000000..fa2bb70 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -0,0 +1,77 @@ +/* + * LinksManagerTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.links + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.isTrue +import assertk.assertions.size +import net.thauvin.erik.mobibot.Constants +import kotlin.test.Test + +class LinksManagerTest { + private val linksManager = LinksManager() + + @Test + fun fetchTitle() { + assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") + assertThat( + linksManager.fetchTitle("https://www.google.com/foo"), + "fetchTitle(Foo)" + ).isEqualTo(Constants.NO_TITLE) + } + + @Test + fun testMatches() { + assertThat(linksManager.matches("https://www.example.com/"), "matches(url)").isTrue() + assertThat(linksManager.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "matches(HTTP)").isTrue() + } + + @Test + fun matchTagKeywordsTest() { + linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3") + val tags = mutableListOf() + + linksManager.matchTagKeywords("Test title with key2", tags) + assertThat(tags, "tags").contains("key2") + tags.clear() + + linksManager.matchTagKeywords("Test key3 title with key1", tags) + assertThat(tags, "tags(key1, key3)").all { + contains("key1") + contains("key3") + size().isEqualTo(2) + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt new file mode 100644 index 0000000..e315891 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -0,0 +1,111 @@ +/* + * ViewTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.links + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.prop +import net.thauvin.erik.mobibot.entries.EntryLink +import kotlin.test.Test + +class ViewTest { + @Test + fun testParseArgs() { + val view = View() + + for (i in 1..10) { + LinksManager.entries.links.add( + EntryLink( + "https://www.example.com/$i", + "Example $i", + "nick$i", + "login$i", + "#channel", + emptyList() + ) + ) + } + + assertThat(view.parseArgs("1"), "parseArgs(1)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + + assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all { + prop(Pair::first).isEqualTo(1) + prop(Pair::second).isEqualTo("foo") + } + + assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all { + prop(Pair::first).isEqualTo(2) + prop(Pair::second).isEqualTo("foo") + } + + assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all { + prop(Pair::first).isEqualTo(3) + prop(Pair::second).isEqualTo("foo bar") + } + + assertThat(view.parseArgs("foo bar"), "parseArgs(foo bar)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("foo bar") + } + + assertThat(view.parseArgs("${Int.MAX_VALUE}1"), "parseArgs(overflow)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("${Int.MAX_VALUE}1") + } + + assertThat(view.parseArgs("1a"), "parseArgs(1a)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("1a") + } + + assertThat(view.parseArgs("20"), "parseArgs(20)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + + assertThat(view.parseArgs(""), "parseArgs()").all { + prop(Pair::first).isEqualTo(LinksManager.entries.links.size - View.MAX_ENTRIES) + prop(Pair::second).isEqualTo("") + } + + LinksManager.entries.links.clear() + + assertThat(view.parseArgs("4"), "parseArgs(4)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt new file mode 100644 index 0000000..37b884b --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -0,0 +1,85 @@ +/* + * SeenTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.seen + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import org.testng.annotations.AfterClass +import org.testng.annotations.BeforeClass +import kotlin.test.Test +import kotlin.io.path.deleteIfExists +import kotlin.io.path.fileSize + +class SeenTest { + private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") + private val seen = Seen(tmpFile.toAbsolutePath().toString()) + private val nick = "ErikT" + + @BeforeClass + fun saveTest() { + seen.add("ErikT") + assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) + } + + @AfterClass(alwaysRun = true) + fun afterClass() { + tmpFile.deleteIfExists() + } + + @Test(priority = 1, groups = ["commands"]) + fun loadTest() { + seen.clear() + assertThat(seen::seenNicks).isEmpty() + seen.load() + assertThat(seen::seenNicks).key(nick).isNotNull() + } + + @Test + fun addTest() { + val last = seen.seenNicks[nick]?.lastSeen + seen.add(nick.lowercase()) + assertThat(seen).all { + prop(Seen::seenNicks).size().isEqualTo(1) + prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last) + prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(nick.lowercase()) + } + } + + @Test(priority = 10, groups = ["commands"]) + fun clearTest() { + seen.clear() + seen.save() + seen.load() + assertThat(seen::seenNicks).size().isEqualTo(0) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt new file mode 100644 index 0000000..cc09237 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -0,0 +1,72 @@ +/* + * TellMessageTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.tell + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import assertk.assertions.prop +import kotlin.test.Test +import java.time.Duration +import java.time.LocalDateTime +import java.time.temporal.Temporal + +/** + * The `TellMessageTest` class. + */ +class TellMessageTest { + private fun isValidDate(date: Temporal): Boolean { + return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 + } + + @Test + fun testTellMessage() { + val message = "Test message." + val recipient = "recipient" + val sender = "sender" + val tellMessage = TellMessage(sender, recipient, message) + assertThat(tellMessage).all { + prop(TellMessage::sender).isEqualTo(sender) + prop(TellMessage::recipient).isEqualTo(recipient) + prop(TellMessage::message).isEqualTo(message) + } + assertThat(isValidDate(tellMessage.queued), "isValidDate()").isTrue() + assertThat(tellMessage.isMatch(sender), "isMatch(sender)").isTrue() + assertThat(tellMessage.isMatch(recipient), "isMatch(recipient)").isTrue() + assertThat(tellMessage.isMatch("foo"), "isMatch(foo)").isFalse() + tellMessage.isReceived = false + assertThat(tellMessage.receptionDate, "receptionDate").isEqualTo(LocalDateTime.MIN) + tellMessage.isReceived = true + assertThat(isValidDate(tellMessage.receptionDate), "isValidDate(creationDate)").isTrue() + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt new file mode 100644 index 0000000..5acba78 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -0,0 +1,87 @@ +/* + * TellMessagesMgrTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.tell + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import org.testng.annotations.AfterClass +import org.testng.annotations.BeforeClass +import kotlin.test.Test +import java.time.LocalDateTime +import kotlin.io.path.createTempFile +import kotlin.io.path.deleteIfExists +import kotlin.io.path.fileSize + +class TellMessagesMgrTest { + private val testFile = createTempFile(suffix = ".ser") + private val maxDays = 10L + private val testMessages = mutableListOf().apply { + for (i in 0..5) { + this.add(i, TellMessage("sender$i", "recipient$i", "message $i")) + } + } + + @BeforeClass + fun saveTest() { + TellManager.save(testFile.toAbsolutePath().toString(), testMessages) + assertThat(testFile.fileSize()).isGreaterThan(0) + } + + @AfterClass + fun afterClass() { + testFile.deleteIfExists() + } + + @Test + fun cleanTest() { + testMessages.add(TellMessage("sender", "recipient", "message").apply { + queued = LocalDateTime.now().minusDays(maxDays) + }) + val size = testMessages.size + assertThat(TellManager.clean(testMessages, maxDays + 2), "clean(maxDays=${maxDays + 2})").isFalse() + assertThat(TellManager.clean(testMessages, maxDays), "clean(maxDays=$maxDays)").isTrue() + assertThat(testMessages, "testMessages").size().isEqualTo(size - 1) + } + + @Test + fun loadTest() { + val messages = TellManager.load(testFile.toAbsolutePath().toString()) + for (i in messages.indices) { + assertThat(messages).index(i).all { + prop(TellMessage::sender).isEqualTo(testMessages[i].sender) + prop(TellMessage::recipient).isEqualTo(testMessages[i].recipient) + prop(TellMessage::message).isEqualTo(testMessages[i].message) + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt new file mode 100644 index 0000000..0ad26b5 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -0,0 +1,91 @@ +/* + * EntriesUtilsTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.Constants +import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment +import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags +import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel +import kotlin.test.Test + +class EntriesUtilsTest { + private val comment = EntryComment("comment", "nick") + private val links = buildList { + for (i in 0..5) { + add( + EntryLink( + "https://www.mobitopia.org/$i", + "Mobitopia$i", + "Skynx$i", + "JimH$i", + "#mobitopia$i", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) + ) + } + } + + @Test + fun printCommentTest() { + assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") + } + + @Test + fun printLinkTest() { + for (i in links.indices) { + assertThat( + printLink(i - 1, links[i]), "link $i" + ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") + } + + assertThat(links.first().addComment(comment), "addComment()").isEqualTo(0) + assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]") + } + + @Test + fun printTagsTest() { + for (i in links.indices) { + assertThat( + printTags(i - 1, links[i]), "tag $i" + ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") + } + } + + @Test + fun toLinkLabelTest() { + assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt new file mode 100644 index 0000000..f421099 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -0,0 +1,133 @@ +/* + * EntryLinkTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.* +import com.rometools.rome.feed.synd.SyndCategory +import com.rometools.rome.feed.synd.SyndCategoryImpl +import kotlin.test.Test +import java.security.SecureRandom +import java.util.* + +/** + * The `EntryUtilsTest` class. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @created 2019-04-19 + * @since 1.0 + */ +class EntryLinkTest { + private val entryLink = EntryLink( + "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", + listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") + ) + + @Test + fun testAddDeleteComment() { + var i = 0 + while (i < 5) { + entryLink.addComment("c$i", "u$i") + i++ + } + assertThat(entryLink.comments, "comments").size().isEqualTo(i) + i = 0 + for (comment in entryLink.comments) { + 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, "hasComments()").isEmpty() + entryLink.addComment("nothing", "nobody") + entryLink.setComment(0, "something", "somebody") + val comment = entryLink.getComment(0) + assertThat(comment, "comment[first]").all { + prop(EntryComment::nick).isEqualTo("somebody") + prop(EntryComment::comment).isEqualTo("something") + } + assertThat(entryLink.deleteComment(comment), "deleteComment").isTrue() + assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse() + } + + @Test + fun testConstructor() { + val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" }) + val link = EntryLink("link", "title", "nick", "channel", Date(), tags) + assertThat(link, "link").all { + prop(EntryLink::tags).size().isEqualTo(tags.size) + prop(EntryLink::tags).index(0).prop(SyndCategory::getName).isEqualTo("tag1") + } + } + + @Test + fun testMatches() { + assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue() + assertThat(entryLink.matches("skynx"), "match(nick)").isTrue() + assertThat(entryLink.matches("www.mobitopia.org"), "matches(url)").isTrue() + assertThat(entryLink.matches("foo"), "matches(foo)").isFalse() + assertThat(entryLink.matches(""), "matches(empty)").isFalse() + assertThat(entryLink.matches(null), "matches(null)").isFalse() + } + + + @Test + fun testTags() { + val tags: List = entryLink.tags + for ((i, tag) in tags.withIndex()) { + assertThat(tag.name, "tag.name($i)").isEqualTo("tag${i + 1}") + } + assertThat(entryLink::tags).size().isEqualTo(5) + entryLink.setTags("-tag5, tag4") + entryLink.setTags("+mobitopia") + entryLink.setTags("-mobitopia") + assertThat( + entryLink.formatTags(","), + "formatTags(',')" + ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") + entryLink.setTags("-tag4 tag5") + assertThat( + entryLink.formatTags(" ", ","), "formatTag(' ',',')" + ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") + val size = entryLink.tags.size + entryLink.setTags("") + assertThat(entryLink.tags, "setTags('')").size().isEqualTo(size) + entryLink.setTags(" ") + assertThat(entryLink.tags, "setTags(' ')").size().isEqualTo(size) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt new file mode 100644 index 0000000..1611009 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -0,0 +1,115 @@ +/* + * FeedMgrTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.* +import net.thauvin.erik.mobibot.Utils.today +import org.testng.annotations.BeforeSuite +import kotlin.test.Test +import java.nio.file.Paths +import java.util.* +import kotlin.io.path.deleteIfExists +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(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31") + + assertThat(entries.links, "entries.links").size().isEqualTo(2) + entries.links.forEachIndexed { i, entryLink -> + assertThat(entryLink, "entryLink[${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, "nick[first]").isEqualTo("ErikT") + assertThat(date, "date[first]").isEqualTo(Date(1635638400000L)) + assertThat(comments.first(), "comments[first]").all { + prop(EntryComment::comment).endsWith("comment 1.") + prop(EntryComment::nick).isEqualTo("ErikT") + } + assertThat(comments.last(), "comments[last]").all { + prop(EntryComment::comment).endsWith("comment 2.") + prop(EntryComment::nick).isEqualTo("Skynx") + } + } + + assertThat(entries.links, "links").index(1).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 + FeedsManager.saveFeed(entries, currentFile.name) + + assertThat(currentFile, "currentFile").exists() + assertThat(backlogFile, "backlogFile").exists() + + assertThat(currentFile.fileSize(), "currentFile == backlogFile").isEqualTo(backlogFile.fileSize()) + + // Load the test feed + entries.links.clear() + FeedsManager.loadFeed(entries, currentFile.name) + + entries.links.forEachIndexed { i, entryLink -> + assertThat(entryLink.title, "entryLink.title[${i + 1}]").isEqualTo("Example ${i + 1}") + } + + assertThat(currentFile.deleteIfExists(), "currentFile.deleteIfExists()").isTrue() + assertThat(backlogFile.deleteIfExists(), "backlogFile.deleteIfExists()").isTrue() + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt new file mode 100644 index 0000000..3bd4c0f --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -0,0 +1,53 @@ +/* + * CalcTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.isEqualTo +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 kotlin.test.Test + +/** + * The `CalcTest` class. + */ +class CalcTest { + @Test + fun testCalculate() { + assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") + assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}") + assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}") + assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt new file mode 100644 index 0000000..085f228 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -0,0 +1,62 @@ +/* + * ChatGptTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.hasNoCause +import assertk.assertions.isInstanceOf +import net.thauvin.erik.mobibot.LocalProperties +import kotlin.test.Test + +class ChatGptTest : LocalProperties() { + @Test + fun testApiKey() { + assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } + .isInstanceOf(ModuleException::class.java) + .hasNoCause() + } + + @Test(groups = ["modules", "no-ci"]) + fun testChat() { + val apiKey = getProperty(ChatGpt.API_KEY_PROP) + assertThat( + ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) + ).contains("XMLHttpRequest") + assertThat( + ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) + ).contains("URLEncoder") + + assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } + .isInstanceOf(ModuleException::class.java) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt new file mode 100644 index 0000000..d0ecbc9 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -0,0 +1,78 @@ +/* + * CryptoPricesTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThan +import assertk.assertions.prop +import net.thauvin.erik.crypto.CryptoPrice +import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice +import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName +import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies +import org.testng.annotations.BeforeClass +import kotlin.test.Test + +/** + * The `CryptoPricesTest` class. + */ +class CryptoPricesTest { + @BeforeClass + @Throws(ModuleException::class) + fun before() { + loadCurrencies() + } + + @Test + @Throws(ModuleException::class) + fun testMarketPrice() { + var price = currentPrice(listOf("BTC")) + assertThat(price, "currentPrice(BTC)").all { + prop(CryptoPrice::base).isEqualTo("BTC") + prop(CryptoPrice::currency).isEqualTo("USD") + prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) + } + + price = currentPrice(listOf("ETH", "EUR")) + assertThat(price, "currentPrice(ETH, EUR)").all { + prop(CryptoPrice::base).isEqualTo("ETH") + prop(CryptoPrice::currency).isEqualTo("EUR") + prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) + } + } + + @Test + fun testGetCurrencyName() { + assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") + assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt new file mode 100644 index 0000000..8d8c997 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -0,0 +1,85 @@ +/* + * CurrencyConverterTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isInstanceOf +import assertk.assertions.matches +import assertk.assertions.prop +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency +import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols +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 kotlin.test.Test + + +/** + * The `CurrencyConvertTest` class. + */ +class CurrencyConverterTest : LocalProperties() { + + @BeforeClass + @Throws(ModuleException::class) + fun before() { + val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) + loadSymbols(apiKey) + } + + @Test + fun testConvertCurrency() { + val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) + assertThat( + convertCurrency(apiKey, "100 USD to EUR").msg, + "convertCurrency(100 USD to EUR)" + ).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex()) + assertThat( + convertCurrency(apiKey, "1 USD to GBP").msg, + "convertCurrency(1 USD to BGP)" + ).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex()) + assertThat( + convertCurrency(apiKey, "100,000.00 CAD to USD").msg, + "convertCurrency(100,000.00 GBP to USD)" + ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex()) + assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all { + prop(Message::msg).contains("You're kidding, right?") + isInstanceOf(PublicMessage::class.java) + } + assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all { + prop(Message::msg).contains("Invalid query.") + isInstanceOf(ErrorMessage::class.java) + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt new file mode 100644 index 0000000..8bafede --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -0,0 +1,53 @@ +/* + * DiceTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.matches +import kotlin.test.Test + +class DiceTest { + @Test + fun testRoll() { + assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") + assertThat(Dice.roll(2, 1), "roll(2d1)") + .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") + assertThat(Dice.roll(5, 1), "roll(5d1)") + .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") + assertThat(Dice.roll(2, 6), "roll(2d6)") + .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) + assertThat(Dice.roll(3, 7), "roll(3d7)") + .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt new file mode 100644 index 0000000..85c990c --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -0,0 +1,95 @@ +/* + * GoogleSearchTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import kotlin.test.Test + +/** + * The `GoogleSearchTest` class. + */ +class GoogleSearchTest : LocalProperties() { + @Test + fun testAPIKeys() { + assertThat( + searchGoogle("", "apikey", "cssKey").first(), + "searchGoogle(empty)" + ).isInstanceOf(ErrorMessage::class.java) + + assertFailure { searchGoogle("test", "", "apiKey") } + .isInstanceOf(ModuleException::class.java).hasNoCause() + + assertFailure { searchGoogle("test", "apiKey", "") } + .isInstanceOf(ModuleException::class.java).hasNoCause() + + assertFailure { searchGoogle("test", "apiKey", "cssKey") } + .isInstanceOf(ModuleException::class.java) + .hasMessage("API key not valid. Please pass a valid API key.") + } + + @Test\n@DisableOnCi + @Throws(ModuleException::class) + fun testSearchGoogle() { + val apiKey = getProperty(GoogleSearch.API_KEY_PROP) + val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP) + + try { + var query = "mobibot" + var messages = searchGoogle(query, apiKey, cseKey) + assertThat(messages, "searchGoogle($query)").all { + isNotEmpty() + index(0).prop(Message::msg).contains(query, true) + } + + query = "adadflkjl" + messages = searchGoogle(query, apiKey, cseKey) + assertThat(messages, "searchGoogle($query)").index(0).all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo("No results found.") + } + } catch (e: ModuleException) { + // Avoid displaying api keys in CI logs + if ("true" == System.getenv("CI")) { + throw e.sanitize(apiKey, cseKey) + } else { + throw e + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt new file mode 100644 index 0000000..0af8e75 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -0,0 +1,57 @@ +/* + * JokeTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke +import net.thauvin.erik.mobibot.msg.Message +import net.thauvin.erik.mobibot.msg.PublicMessage +import kotlin.test.Test + +/** + * The `JokeTest` class. + */ +class JokeTest { + @Test + @Throws(ModuleException::class) + fun testRandomJoke() { + val joke = randomJoke() + assertThat(joke, "randomJoke()").all { + size().isGreaterThan(0) + each { + it.isInstanceOf(PublicMessage::class.java) + it.prop(Message::msg).doesNotContain("\n") + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt new file mode 100644 index 0000000..a3fc226 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -0,0 +1,60 @@ +/* + * LookupTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.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 kotlin.test.Test + +/** + * The `Lookup Test` class. + */ +class LookupTest { + @Test + @Throws(Exception::class) + fun testLookup() { + var result = nslookup("apple.com") + assertThat(result, "lookup(apple.com)").contains("17.253.144.10") + + result = nslookup("204.122.16.136") + assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") + } + + @Test + @Throws(Exception::class) + fun testWhois() { + val result = whois("17.178.96.59", Lookup.WHOIS_HOST) + assertThat(result, "whois(17.178.96.59").any { it.contains("Apple Inc.") } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt new file mode 100644 index 0000000..f4b5e99 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -0,0 +1,54 @@ +/* + * MastodonTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.assertThat +import assertk.assertions.contains +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot +import kotlin.test.Test + +class MastodonTest : LocalProperties() { + @Test + @Throws(ModuleException::class) + fun testToot() { + val msg = "Testing Mastodon API from ${getHostName()}" + assertThat( + toot( + getProperty(Mastodon.ACCESS_TOKEN_PROP), + getProperty(Mastodon.INSTANCE_PROP), + getProperty(Mastodon.HANDLE_PROP), + msg, + true + ) + ).contains(msg) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt new file mode 100644 index 0000000..1416eec --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -0,0 +1,105 @@ +/* + * ModuleExceptionTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.all +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize +import org.testng.annotations.DataProvider +import kotlin.test.Test +import java.io.IOException +import java.lang.reflect.Method + +/** + * The `ModuleExceptionTest` class. + */ +class ModuleExceptionTest { + companion object { + const val DEBUG_MESSAGE = "debugMessage" + const val MESSAGE = "message" + } + + @DataProvider(name = "dp") + fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array> { + return arrayOf( + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))), + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))), + arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE)) + ) + } + + @Test(dataProvider = "dp") + fun testGetDebugMessage(e: ModuleException) { + assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE) + } + + @Test(dataProvider = "dp") + fun testGetMessage(e: ModuleException) { + assertThat(e).hasMessage(MESSAGE) + } + + @Test + fun testSanitizeMessage() { + val apiKey = "1234567890" + var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) + assertThat( + e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" + ).isNotNull().all { + contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") + doesNotContain(apiKey, "me") + } + + e = ModuleException(DEBUG_MESSAGE, MESSAGE, null) + assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE) + + e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException()) + assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE) + + e = ModuleException(DEBUG_MESSAGE, apiKey) + assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() + .doesNotContain(apiKey) + + val msg: String? = null + e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg)) + assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull() + + e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey")) + assertThat( + e.sanitize(" ", apiKey, "foo").message, + "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" + ).isNotNull().all { + doesNotContain(apiKey) + endsWith("xxx is xxxxxxxxxx") + } + assertThat(e.sanitize(), "exception should be unchanged").isEqualTo(e) + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt new file mode 100644 index 0000000..d1399e0 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -0,0 +1,54 @@ +/* + * PingTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isNotEmpty +import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing +import kotlin.test.Test + +/** + * The `PingTest` class. + */ +class PingTest { + @Test + fun testPingsArray() { + assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty() + } + + @Test + fun testRandomPing() { + for (i in 0..9) { + assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing()) + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt new file mode 100644 index 0000000..f836b0e --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -0,0 +1,50 @@ +/* + * RockPaperScissorsTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.assertThat +import assertk.assertions.isEqualTo +import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw +import kotlin.test.Test + +class RockPaperScissorsTest { + @Test + fun testWinLoseOrDraw() { + assertThat(winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win") + assertThat(winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win") + assertThat(winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win") + assertThat(winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose") + assertThat(winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose") + assertThat(winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose") + assertThat(winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw") + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt new file mode 100644 index 0000000..d96812b --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -0,0 +1,85 @@ +/* + * StockQuoteTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.* +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote +import net.thauvin.erik.mobibot.msg.ErrorMessage +import net.thauvin.erik.mobibot.msg.Message +import kotlin.test.Test + +/** + * The `StockQuoteTest` class. + */ +class StockQuoteTest : LocalProperties() { + private fun buildMatch(label: String): String { + return "${label}:[ ]+[0-9.]+".prependIndent() + } + + @Test + @Throws(ModuleException::class) + fun testGetQuote() { + val apiKey = getProperty(StockQuote.API_KEY_PROP) + try { + var symbol = "apple inc" + val messages = getQuote(symbol, apiKey) + assertThat(messages, "response not empty").isNotEmpty() + assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) + assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) + assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) + .matches(buildMatch("Previous").toRegex()) + assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) + + symbol = "blahfoo" + assertThat(getQuote(symbol, apiKey).first(), "getQuote($symbol)").all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) + } + assertThat(getQuote("", "apikey").first(), "getQuote(empty)").all { + isInstanceOf(ErrorMessage::class.java) + prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) + } + assertFailure { getQuote("test", "") }.isInstanceOf(ModuleException::class.java).hasNoCause() + } catch (e: ModuleException) { + // Avoid displaying api keys in CI logs + if ("true" == System.getenv("CI")) { + throw e.sanitize(apiKey) + } else { + throw e + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt new file mode 100644 index 0000000..66f4d22 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -0,0 +1,117 @@ +/* + * Weather2Test.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.* +import net.aksingh.owmjapis.api.APIException +import net.aksingh.owmjapis.core.OWM +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.Weather2.Companion.API_KEY_PROP +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 net.thauvin.erik.mobibot.msg.Message +import kotlin.test.Test + +/** + * The `Weather2Test` class. + */ +class Weather2Test : LocalProperties() { + @Test + fun testFtoC() { + val t = ftoC(32.0) + assertThat(t.second, "32 °F is 0 °C").isEqualTo(0) + } + + @Test + fun testGetCountry() { + assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) + assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) + + val country = OWM.Country.entries.toTypedArray() + repeat(3) { + val rand = country[(country.indices).random()] + assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) + } + } + + @Test + fun testMphToKmh() { + val w = mphToKmh(0.62) + assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1) + } + + @Test + @Throws(ModuleException::class) + fun testWeather() { + var query = "98204" + var messages = getWeather(query, getProperty(API_KEY_PROP)) + assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { + contains("Everett, United States") + contains("US") + } + assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS") + + query = "San Francisco" + messages = getWeather(query, getProperty(API_KEY_PROP)) + assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { + contains("San Francisco") + contains("US") + } + assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959") + + query = "London, GB" + messages = getWeather(query, getProperty(API_KEY_PROP)) + assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { + contains("London, United Kingdom") + contains("GB") + } + assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("2643743") + + try { + query = "Foo, US" + getWeather(query, getProperty(API_KEY_PROP)) + } catch (e: ModuleException) { + assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java) + } + + query = "test" + assertFailure { getWeather(query, "") }.isInstanceOf(ModuleException::class.java).hasNoCause() + assertFailure { getWeather(query, null) }.isInstanceOf(ModuleException::class.java).hasNoCause() + + messages = getWeather("", "apikey") + assertThat(messages, "getWeather(empty)").index(0).prop(Message::isError).isTrue() + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt new file mode 100644 index 0000000..855522c --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -0,0 +1,77 @@ +/* + * WolframAlphaTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.hasMessage +import assertk.assertions.isInstanceOf +import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize +import net.thauvin.erik.mobibot.LocalProperties +import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram +import kotlin.test.Test + +class WolframAlphaTest : LocalProperties() { + @Test + fun testAppId() { + assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } + .isInstanceOf(ModuleException::class.java) + .hasMessage("Error 1: Invalid appid") + + assertFailure { queryWolfram("1 gallon to liter", appId = "") } + .isInstanceOf(ModuleException::class.java) + } + + @Test(groups = ["modules", "no-ci"]) + @Throws(ModuleException::class) + fun queryWolframTest() { + val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) + try { + var query = "SFO to SEA" + assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles") + + query = "SFO to LAX" + assertThat( + queryWolfram(query, WolframAlpha.METRIC, apiKey), + "queryWolfram($query)" + ).contains("kilometers") + } catch (e: ModuleException) { + // Avoid displaying api key in CI logs + if ("true" == System.getenv("CI")) { + throw e.sanitize(apiKey) + } else { + throw e + } + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt new file mode 100644 index 0000000..7931f33 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -0,0 +1,70 @@ +/* + * WordTimeTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import assertk.assertThat +import assertk.assertions.endsWith +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.pircbotx.Colors +import kotlin.test.Test +import java.time.ZoneId + +/** + * The `WordTimeTest` class. + */ +class WordTimeTest { + @Test + fun testTime() { + assertThat(time(), "time()").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(""), "time()").endsWith("Los Angeles".bold()) + assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) + assertThat(time("GB"), "time(GB)").endsWith("London".bold()) + assertThat(time("FR"), "time(FR)").endsWith("Paris".bold()) + assertThat(time("BLAH"), "time(BLAH)").startsWith("Unsupported") + assertThat(time("BEAT"), "time($BEATS_KEYWORD)").matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex()) + } + + @Test + fun testZones() { + COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { + assertThat(ZoneId.of(it.value)) + } + } +} diff --git a/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt b/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt new file mode 100644 index 0000000..32a0495 --- /dev/null +++ b/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -0,0 +1,109 @@ +/* + * MessageTest.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.msg + +import assertk.all +import assertk.assertThat +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import assertk.assertions.prop +import kotlin.test.Test + +class MessageTest { + @Test + fun testConstructor() { + var msg = Message("foo") + + msg.isError = true + assertThat(msg.isNotice, "message is notice").isTrue() + + msg = Message("foo", isError = true) + assertThat(msg.isNotice, "message is notice too").isTrue() + } + + @Test + fun testErrorMessage() { + val msg = ErrorMessage("foo") + assertThat(msg).all { + prop(Message::isError).isTrue() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + } + + @Test + fun testIsError() { + val msg = Message("foo") + msg.isError = true + assertThat(msg).all { + prop(Message::isError).isTrue() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + msg.isError = false + assertThat(msg).all { + prop(Message::isError).isFalse() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + } + + @Test + fun testNoticeMessage() { + val msg = NoticeMessage("food") + assertThat(msg).all { + prop(Message::isError).isFalse() + prop(Message::isNotice).isTrue() + prop(Message::isPrivate).isFalse() + } + } + + @Test + fun testPrivateMessage() { + val msg = PrivateMessage("foo") + assertThat(msg).all { + prop(Message::isPrivate).isTrue() + prop(Message::isError).isFalse() + prop(Message::isNotice).isFalse() + } + } + + @Test + fun testPublicMessage() { + val msg = PublicMessage("foo") + assertThat(msg).all { + prop(Message::isError).isFalse() + prop(Message::isNotice).isFalse() + prop(Message::isPrivate).isFalse() + } + } +} diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 623b3c3..7c85194 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,9 +1,10 @@ -image: maven:3-eclipse-temurin-17 +image: openjdk:17 pipelines: default: - step: - caches: - - gradle + name: Test with bld script: - - bash ./gradlew check + - ./bld download + - ./bld compile + - ./bld test diff --git a/bld b/bld new file mode 100755 index 0000000..77721d6 --- /dev/null +++ b/bld @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +java -jar "$(dirname "$0")/lib/bld/bld-wrapper.jar" "$0" --build net.thauvin.erik.MobibotBuild "$@" \ No newline at end of file diff --git a/bld.bat b/bld.bat new file mode 100644 index 0000000..12ffa36 --- /dev/null +++ b/bld.bat @@ -0,0 +1,4 @@ +@echo off +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +java -jar "%DIRNAME%/lib/bld/bld-wrapper.jar" "%0" --build net.thauvin.erik.MobibotBuild %* \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 6507e20..0000000 --- a/build.gradle +++ /dev/null @@ -1,247 +0,0 @@ -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import io.gitlab.arturbosch.detekt.Detekt -import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask - -plugins { - id 'application' - id 'com.github.ben-manes.versions' version '0.49.0' - id 'idea' - id 'io.gitlab.arturbosch.detekt' version '1.23.3' - id 'java' - id 'net.thauvin.erik.gradle.semver' version '1.0.4' - id 'org.jetbrains.kotlin.jvm' version '1.9.20' - id 'org.jetbrains.kotlin.kapt' version '1.9.20' - id 'org.jetbrains.kotlinx.kover' version '0.7.4' - id 'org.sonarqube' version '4.4.1.3373' - id 'pmd' -} - -defaultTasks 'deploy' - -final def packageName = 'net.thauvin.erik.mobibot' -final def deployDir = 'deploy' -final def semverProcessor = "net.thauvin.erik:semver:1.2.1" - -final def isCI = (System.getenv('CI') != null) - -def isNonStable = { String version -> - def stableKeyword = ['RELEASE', 'FINAL', 'GA', 'JRE'].any { it -> version.toUpperCase().contains(it) } - def regex = /^[0-9,.v-]+(-r)?$/ - return !stableKeyword && !(version ==~ regex) -} - -mainClassName = packageName + '.Mobibot' - -ext.versions = [ - log4j: '2.21.1', - pmd : '6.55.0', -] - -repositories { - mavenLocal() - mavenCentral() - maven { url 'https://jitpack.io' } - maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } -} - -dependencies { - kapt(semverProcessor) - compileOnly(semverProcessor) - - // PircBotX - implementation 'com.github.pircbotx:pircbotx:2.3.1' - // implementation fileTree(dir: 'lib', include: '*.jar') - - // Commons (mostly for PircBotX) - implementation 'org.apache.commons:commons-lang3:3.13.0' - implementation 'org.apache.commons:commons-text:1.11.0' - implementation 'commons-codec:commons-codec:1.16.0' - implementation 'commons-net:commons-net:3.10.0' - - // Google - implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.1.3-jre' - - // Kotlin - implementation platform('org.jetbrains.kotlin:kotlin-bom') - implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' - implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6' - - // Logging - implementation 'org.slf4j:slf4j-api:2.0.9' - implementation "org.apache.logging.log4j:log4j-api:$versions.log4j" - implementation "org.apache.logging.log4j:log4j-core:$versions.log4j" - implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$versions.log4j" - - implementation 'com.rometools:rome:2.1.0' - implementation 'com.squareup.okhttp3:okhttp:4.12.0' - implementation 'net.aksingh:owm-japis:2.5.3.0' - implementation 'net.objecthunter:exp4j:0.4.8' - implementation 'org.json:json:20231013' - implementation 'org.jsoup:jsoup:1.16.2' - - // Thauvin - implementation 'net.thauvin.erik:cryptoprice:1.0.1' - implementation 'net.thauvin.erik:jokeapi:0.9.0' - implementation 'net.thauvin.erik:pinboard-poster:1.1.0' - implementation 'net.thauvin.erik.urlencoder:urlencoder-lib:1.4.0' - - testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.27.0' -// testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' -// testImplementation "org.mockito:mockito-core:4.0.0" - testImplementation 'org.testng:testng:7.8.0' -} - -test { - useTestNG() { - // excludeGroups.add('twitter') - if (isCI) { - excludeGroups.add('no-ci') - } - if (!excludeGroups.isEmpty()) { - println "Excluded test groups: ${excludeGroups}" - } - } -} - -tasks.withType(Test).configureEach { - testLogging { - exceptionFormat = 'full' - events('skipped', 'failed') - } -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -kotlin { - jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} - -kapt { - includeCompileClasspath = false - arguments { - arg('semver.project.dir', projectDir) - } -} - -tasks.withType(JavaCompile).configureEach { - options.encoding = 'UTF-8' -} - - -compileJava { - dependsOn 'incrementBuildMeta' - options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation'] -} - -tasks.named("dependencyUpdates").configure { - rejectVersionIf { - isNonStable(it.candidate.version) - } -} - -pmd { - toolVersion = versions.pmd - ignoreFailures = true - ruleSets = [] - ruleSetFiles = files("${projectDir}/config/pmd.xml") - consoleOutput = true -} - -detekt { - //toolVersion = "main-SNAPSHOT" - baseline = file("${projectDir}/config/detekt/baseline.xml") -} - -tasks.withType(Detekt).configureEach { - jvmTarget = java.targetCompatibility.toString() -} - -tasks.withType(DetektCreateBaselineTask).configureEach { - jvmTarget = java.targetCompatibility.toString() -} - -jar { - manifest.attributes('Main-Class': mainClassName, - 'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/')) - archiveVersion.set("") - exclude('log4j2.xml') -} - -clean { - doFirst { - project.delete(fileTree(deployDir)) - } -} - -run { - args('-h') -} - -incrementBuildMeta { - doFirst { - if (isCI) { - println 'No increment with CI.' - } else { - buildMeta = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) - } - } -} - -koverReport { - defaults { - xml { - onCheck = true - } - html { - onCheck = true - } - } -} - -sonarqube { - properties { - property('sonar.organization', 'ethauvin-github') - property('sonar.projectKey', 'ethauvin_mobibot') - property('sonar.host.url', 'https://sonarcloud.io') - property('sonar.coverage.jacoco.xmlReportPaths', "${layout.buildDirectory.get()}/reports/kover/report.xml") - } -} - -tasks.register('copyToDeploy', Copy) { - from('properties', jar) - into deployDir -} - -tasks.register('copyToDeployLib', Copy) { - from(configurations.runtimeClasspath) { - exclude 'annotations-*.jar' - } - into(deployDir + '/lib') -} - -tasks.register('deploy') { - description = "Copies all needed files to the ${deployDir} directory." - group = 'Publishing' - dependsOn(assemble, jar) - outputs.dir deployDir - inputs.files(copyToDeploy, copyToDeployLib) - doLast { - file(deployDir + '/logs').mkdir() - } - mustRunAfter(clean) -} - -tasks.register('release') { - group = 'Publishing' - description = 'Releases new version.' - dependsOn(clean, check, deploy) - mustRunAfter clean -} diff --git a/deploy.sh b/deploy.sh index ce3fde5..58dd32d 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,6 @@ #!/bin/bash -./gradlew release +./bld jar deploy [ $? -eq 0 ] && sftp nix3.thauvin.us <nW$ZR+`W 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 3fa8f86..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index 1aa94a4..0000000 --- a/gradlew +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 93e3f59..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d17ad8982355ae5516129c6b07841609641eb492 GIT binary patch literal 27321 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 Kus>-qu>S(PSAA^& literal 0 HcmV?d00001 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties new file mode 100644 index 0000000..8375666 --- /dev/null +++ b/lib/bld/bld-wrapper.properties @@ -0,0 +1,10 @@ +bld.downloadExtensionJavadoc=false +bld.downloadExtensionSources=true +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.1 +bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.3-SNAPSHOT +bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0-SNAPSHOT +bld.extensions-pmd=com.uwyn.rife2:bld-testng:0.9.2 +bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES +bld.downloadLocation= +bld.sourceDirectories= +bld.version=1.7.5 diff --git a/release_info.txt b/release_info.txt new file mode 100644 index 0000000..f5efde2 --- /dev/null +++ b/release_info.txt @@ -0,0 +1,27 @@ +/* + * This file is automatically generated + * Do not modify! -- ALL CHANGES WILL BE ERASED! + */ + +package {{v packageName/}} + +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId + +/** + * Provides release information. + */ +object {{v className/}} { + const val PROJECT = "{{v project/}}" + const val VERSION = "{{v version/}}" + + @JvmField + val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( + Instant.ofEpochMilli({{v epoch/}}L), ZoneId.systemDefault() + ) + + const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val AUTHOR = "Erik C. Thauvin" + const val AUTHOR_URL = "https://erik.thauvin.net/" +} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 53bc4db..0000000 --- a/settings.gradle +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id "com.gradle.enterprise" version "3.6.3" -} - -gradleEnterprise { - buildScan { - link("GitHub", "https://github.com/ethauvin/pinboard-poster/tree/master") - if (System.getenv("CI")) { - uploadInBackground = false - publishOnFailure() - tag "CI" - } - termsOfServiceUrl = "https://gradle.com/terms-of-service" - termsOfServiceAgree = "yes" - } -} - -rootProject.name = 'mobibot' diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..85d8fce --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.organization=ethauvin-github +sonar.projectKey=ethauvin_mobibot +sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml +sonar.sources=src/main/kotlin/ +sonar.tests=src/test/kotlin/ +sonar.java.binaries=build/main,build/test +sonar.java.libraries=lib/compile/*.jar diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java new file mode 100644 index 0000000..9b6b32a --- /dev/null +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -0,0 +1,170 @@ +/* + * MobibotBuild.java + * + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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; + +import rife.bld.BuildCommand; +import rife.bld.Project; +import rife.bld.dependencies.Repository; +import rife.bld.extension.CompileKotlinOperation; +import rife.bld.extension.CompileKotlinOptions; +import rife.bld.extension.GeneratedVersionOperation; +import rife.bld.extension.JacocoReportOperation; +import rife.tools.FileUtils; +import rife.tools.exceptions.FileUtilsErrorException; + +import java.io.File; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; + +import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; +import static rife.bld.dependencies.Repository.MAVEN_LOCAL; +import static rife.bld.dependencies.Scope.compile; +import static rife.bld.dependencies.Scope.test; + +public class MobibotBuild extends Project { + public MobibotBuild() { + pkg = "net.thauvin.erik.mobibot"; + name = "mobibot"; + version = version(0, 8, 0, "rc+" + + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); + + mainClass = pkg + ".Mobibot"; + + javaRelease = 17; + downloadSources = true; + autoDownloadPurge = true; + repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io")); + + var log4j = version(2, 21, 1); + scope(compile) + // PircBotX + .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) + // Commons (mostly for PircBotX) + .include(dependency("org.apache.commons", "commons-lang3", "3.13.0")) + .include(dependency("org.apache.commons", "commons-text", "1.11.0")) + .include(dependency("commons-codec", "commons-codec", "1.16.0")) + .include(dependency("commons-net", "commons-net", "3.10.0")) + // Google + .include(dependency("com.google.code.gson", "gson", "2.10.1")) + .include(dependency("com.google.guava", "guava", "32.1.3-jre")) + // Kotlin + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", version(1, 9, 20))) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) + // Logging + .include(dependency("org.slf4j", "slf4j-api", "2.0.9")) + .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) + .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) + .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) + .include(dependency("com.rometools", "rome", "2.1.0")) + .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) + .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) + .include(dependency("net.objecthunter", "exp4j", "0.4.8")) + .include(dependency("org.json", "json", "20231013")) + .include(dependency("org.jsoup", "jsoup", "1.16.2")) + // Thauvin + .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.1")) + .include(dependency("net.thauvin.erik", "jokeapi", "0.9.0")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.0")) + .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0")); + scope(test) + .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 27, 0))) + .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 20))) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))); + + List jars = new ArrayList<>(); + compileClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); + jarOperation() + .manifestAttribute(Attributes.Name.MAIN_CLASS, mainClass()) + .manifestAttribute(Attributes.Name.CLASS_PATH, ". " + String.join(" ", jars)); + + jarSourcesOperation().sourceDirectories(new File(srcMainDirectory(), "kotlin")); + } + + public static void main(String[] args) { + new MobibotBuild().start(args); + } + + @Override + public void clean() throws Exception { + FileUtils.deleteDirectory(new File("deploy")); + super.clean(); + } + + @BuildCommand(summary = "Compiles the Kotlin project") + @Override + public void compile() throws Exception { + releaseInfo(); + new CompileKotlinOperation() + .fromProject(this) + .compileOptions( + new CompileKotlinOptions() + .jdkRelease(javaRelease) + .verbose(true) + ) + .execute(); + } + + @BuildCommand(summary = "Copies all needed files to the deploy directory") + public void deploy() throws FileUtilsErrorException { + var deploy = new File("deploy"); + var lib = new File(deploy, "lib"); + var ignore = lib.mkdirs(); + FileUtils.copyDirectory(new File("properties"), deploy); + FileUtils.copyDirectory(libCompileDirectory(), lib); + FileUtils.copy(new File(buildDistDirectory(), jarFileName()), new File(deploy, "mobibot.jar")); + } + + @BuildCommand(summary = "Generates JaCoCo Reports") + public void jacoco() throws IOException { + new JacocoReportOperation() + .fromProject(this) + .execute(); + } + + @BuildCommand(value = "release-info", summary = "Generates the ReleaseInfo class") + public void releaseInfo() { + new GeneratedVersionOperation() + .fromProject(this) + .classTemplate(new File(workDirectory(), "release-info.txt")) + .className("ReleaseInfo") + .packageName(pkg) + .directory(new File(srcMainDirectory(), "kotlin")) + .extension(".kt") + .execute(); + } +} diff --git a/src/main/java/net/thauvin/erik/mobibot/modules/War.java b/src/main/java/net/thauvin/erik/mobibot/modules/War.java deleted file mode 100644 index 4bbbd9b..0000000 --- a/src/main/java/net/thauvin/erik/mobibot/modules/War.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * War.java - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules; - -import net.thauvin.erik.mobibot.Utils; -import org.jetbrains.annotations.NotNull; -import org.pircbotx.hooks.types.GenericMessageEvent; - -import java.security.SecureRandom; - -import static net.thauvin.erik.mobibot.Utils.bold; - -/** - * The War module. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public final class War extends AbstractModule { - private static final String[] CLUBS = - {"🃑", "🃞", "🃝", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒"}; - private static final String[] DIAMONDS = - {"🃁", "🃎", "🃍", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂"}; - private static final String[] HEARTS = - {"🂱", "🂾", "🂽", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲"}; - // Random - private static final SecureRandom RANDOM = new SecureRandom(); - private static final String[] SPADES = - {"🂡", "🂮", "🂭", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢"}; - private static final String[][] DECK = {HEARTS, SPADES, DIAMONDS, CLUBS}; - // War command - private static final String WAR_CMD = "war"; - - /** - * The default constructor. - */ - public War() { - super(); - - commands.add(WAR_CMD); - - help.add("To play war:"); - help.add(Utils.helpFormat("%c " + WAR_CMD)); - } - - @NotNull - @Override - public String getName() { - return "War"; - } - - /** - * {@inheritDoc} - */ - @Override - public void commandResponse(@NotNull final String channel, @NotNull final String cmd, @NotNull final String args, - @NotNull final GenericMessageEvent event) { - int i; - int y; - - do { - i = RANDOM.nextInt(HEARTS.length); - y = RANDOM.nextInt(HEARTS.length); - - final String result; - if (i < y) { - result = bold("win"); - } else if (i > y) { - result = bold("lose"); - } else { - result = bold("tie") + ". This means " + bold("WAR"); - } - - event.respond(DECK[RANDOM.nextInt(DECK.length)][i] + " " + DECK[RANDOM.nextInt(DECK.length)][y] + - " » You " + result + '!'); - - } while (i == y); - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 2c5f05d..c6f16cb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 98ef74a..ea89f4c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index d82f011..371b523 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index f91c457..47dd7c2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -51,7 +51,6 @@ import net.thauvin.erik.mobibot.commands.links.* import net.thauvin.erik.mobibot.commands.seen.Seen import net.thauvin.erik.mobibot.commands.tell.Tell import net.thauvin.erik.mobibot.modules.* -import net.thauvin.erik.semver.Version import org.pircbotx.Configuration import org.pircbotx.PircBotX import org.pircbotx.hooks.ListenerAdapter @@ -66,7 +65,6 @@ import java.util.* import java.util.regex.Pattern import kotlin.system.exitProcess -@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { // The bot configuration. private val config: Configuration @@ -257,7 +255,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro // Output the version println( "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" + " (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})" ) println(ReleaseInfo.WEBSITE) } else { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 7cb5aed..ac01b0a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt new file mode 100644 index 0000000..88634ae --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -0,0 +1,27 @@ +/* + * This file is automatically generated + * Do not modify! -- ALL CHANGES WILL BE ERASED! + */ + +package net.thauvin.erik.mobibot + +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId + +/** + * Provides release information. + */ +object ReleaseInfo { + const val PROJECT = "mobibot" + const val VERSION = "0.8.0-rc+20231110234054" + + @JvmField + val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( + Instant.ofEpochMilli(1699688454937L), ZoneId.systemDefault() + ) + + const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val AUTHOR = "Erik C. Thauvin" + const val AUTHOR_URL = "https://erik.thauvin.net/" +} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index e4760d2..d69c0c5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 5f79472..e0b091a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 038e378..9084660 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 9608ca8..18a8b1d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index f271bfa..c1708a9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index d083c10..8d2b154 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index ed0b6ef..8ee8c4f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index ec7823b..f2a13ba 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index b2293b0..1aaefd7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 20a6635..0f492c9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 85a03ab..41b2c4b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 77154c7..0159017 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 7f76d35..1f89982 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 33d6fef..4a881a7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 896c569..a8300f0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -40,7 +40,7 @@ import org.pircbotx.hooks.types.GenericMessageEvent class Versions : AbstractCommand() { private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", + "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})", "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + ", JVM ${System.getProperty("java.runtime.version")}", "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 1443d44..90d8bb3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index fba6b99..2d7add6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,7 +1,7 @@ /* * LinksManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index ff4278d..dca99cf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1662857..c59cad9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 825e374..1626a58 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index cfd2c27..2a3856b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,7 +1,7 @@ /* * NickComparator.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index c9ee0f3..83083fd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,7 +1,7 @@ /* * Seen.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 7924977..1445f9f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,7 +1,7 @@ /* * SeenNick.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 061ca6a..e9d18ae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index b65a4da..229bc5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,7 +1,7 @@ /* * TellManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index d17fbb5..cc48d59 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index e8676ec..15506e0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 9c09626..6e58fd9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index e18d692..c0cc2a8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 4a69446..8098bbf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index f786cb2..e7017ab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,7 +1,7 @@ /* * FeedsManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 8c8e736..8dcf6d8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index b7aae28..9003783 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index bd92332..09791a8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -1,7 +1,7 @@ /* * ChatGpt.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index d14056e..4464732 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -120,7 +120,7 @@ class CryptoPrices : AbstractModule() { } /** - * Loads the Fiat currencies.. + * Loads the Fiat currencies. */ @JvmStatic @Throws(ModuleException::class) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index da0efd8..954f4d1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 8420fb1..84f2280 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index f426d1e..a9b15a5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 2760fa7..91b9ad2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 9ab2ead..eecc55e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 3be3a5f..1141c0e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,7 +1,7 @@ /* * Mastodon.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index a7416c2..9a0e641 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 944dbc1..86b311a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index a299d8d..d224568 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index dcae5e7..6b591cd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt new file mode 100644 index 0000000..0c64465 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt @@ -0,0 +1,89 @@ +/* + * War.kt + * + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules + +import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.helpFormat +import org.pircbotx.hooks.types.GenericMessageEvent +import java.security.SecureRandom + +/** + * The War module. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @since 1.0 + */ +class War : AbstractModule() { + override val name = "War" + + override fun commandResponse( + channel: String, cmd: String, args: String, + event: GenericMessageEvent + ) { + var i: Int + var y: Int + do { + i = RANDOM.nextInt(HEARTS.size) + y = RANDOM.nextInt(HEARTS.size) + val result: String = if (i < y) { + "win".bold() + } else if (i > y) { + "lose".bold() + } else { + "tie".bold() + ". This means " + "WAR".bold() + } + event.respond( + DECK[RANDOM.nextInt(DECK.size)][i] + " " + DECK[RANDOM.nextInt(DECK.size)][y] + + " » You " + result + '!' + ) + } while (i == y) + } + + companion object { + private val CLUBS = arrayOf("🃑", "🃞", "🃝", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒") + private val DIAMONDS = arrayOf("🃁", "🃎", "🃍", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂") + private val HEARTS = arrayOf("🂱", "🂾", "🂽", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲") + + // Random + private val RANDOM = SecureRandom() + private val SPADES = arrayOf("🂡", "🂮", "🂭", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢") + private val DECK = arrayOf(HEARTS, SPADES, DIAMONDS, CLUBS) + + // War command + private const val WAR_CMD = "war" + } + + init { + commands.add(WAR_CMD) + help.add("To play war:") + help.add(helpFormat("%c $WAR_CMD")) + } +} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 80a06fa..83eba95 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index a72efab..8d9c4e6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,7 +1,7 @@ /* * WolframAlpha.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 18072bc..0c379b3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 0607936..b9f5212 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 23a33b9..84e3a1e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -30,8 +30,6 @@ */ package net.thauvin.erik.mobibot.msg -import net.thauvin.erik.semver.Constants - /** * The `Message` class. */ @@ -43,7 +41,7 @@ open class Message @JvmOverloads constructor( var isPrivate: Boolean = false ) { companion object { - var DEFAULT_COLOR = Constants.EMPTY + var DEFAULT_COLOR = "" } init { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 037d504..2be52aa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index b424fdf..8d34d28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 9c5e088..9e67ecb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index 91f2dd9..3fb3874 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,7 +1,7 @@ /* * SocialManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index b594670..47a33d9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,7 +1,7 @@ /* * SocialModule.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index 3fd315e..e779a3c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,7 +1,7 @@ /* * SocialTimer.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..16644cc --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 3241bf0..7f65548 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -42,8 +42,8 @@ import net.thauvin.erik.mobibot.commands.Ignore import net.thauvin.erik.mobibot.commands.links.Comment import net.thauvin.erik.mobibot.commands.links.View import net.thauvin.erik.mobibot.modules.* -import org.testng.annotations.Test import java.util.* +import kotlin.test.Test class AddonsTest { private val p = Properties().apply { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt new file mode 100644 index 0000000..01d98ca --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -0,0 +1,44 @@ +/* + * DisableOnCi.kt + * + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import org.junit.jupiter.api.extension.ExtendWith + +/** + * Disables tests on CI annotation. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @since 1.0 + */ +@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@ExtendWith(DisableOnCiCondition::class) +annotation class DisabledOnCi diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt new file mode 100644 index 0000000..24f1ca0 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt @@ -0,0 +1,51 @@ +/* + * DisableOnCiCondition.kt + * + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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 + +import org.junit.jupiter.api.extension.ConditionEvaluationResult +import org.junit.jupiter.api.extension.ExecutionCondition +import org.junit.jupiter.api.extension.ExtensionContext + +/** + * Disables tests on CI condition. + * + * @author [Erik C. Thauvin](https://erik.thauvin.net/) + * @since 1.0 + */ +class DisableOnCiCondition : ExecutionCondition { + override fun evaluateExecutionCondition(context: ExtensionContext): ConditionEvaluationResult { + return if (System.getenv("CI") != null) { + ConditionEvaluationResult.disabled("Test disabled on CI") + } else { + ConditionEvaluationResult.enabled("Test enabled") + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index a3994ec..b02188e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 78f5b18..cfc3d4e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,10 +37,10 @@ import assertk.assertions.* import com.rometools.rome.io.FeedException import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed import net.thauvin.erik.mobibot.msg.Message -import org.testng.annotations.Test import java.io.IOException import java.net.MalformedURLException import java.net.UnknownHostException +import kotlin.test.Test /** * The `FeedReader Test` class. diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index 1384a72..95b6c08 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -30,7 +30,6 @@ */ package net.thauvin.erik.mobibot -import org.testng.annotations.BeforeSuite import java.io.IOException import java.net.InetAddress import java.net.UnknownHostException @@ -42,8 +41,7 @@ import java.util.* * Access to `local.properties`. */ open class LocalProperties { - @BeforeSuite(alwaysRun = true) - fun loadProperties() { + init { val localPath = Paths.get("local.properties") if (Files.exists(localPath)) { try { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 87617e8..cf9d59a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.reader import net.thauvin.erik.mobibot.entries.EntryLink -import org.testng.Assert.assertFalse -import org.testng.Assert.assertTrue -import org.testng.annotations.Test import java.net.URL +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue class PinboardTest : LocalProperties() { private val pinboard = Pinboard() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 7a3f5d2..a024cff 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -59,14 +59,14 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.underline import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR +import org.junit.jupiter.api.BeforeEach import org.pircbotx.Colors -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test import java.io.File import java.io.IOException import java.net.URL import java.time.LocalDateTime import java.util.* +import kotlin.test.Test /** * The `Utils Test` class. @@ -78,7 +78,7 @@ class UtilsTest { private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) private val test = "This is a test." - @BeforeClass + @BeforeEach fun setUp() { cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 265009b..33a411f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,7 +1,7 @@ /* * InfoTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.commands import assertk.assertThat import assertk.assertions.isEqualTo import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime -import org.testng.annotations.Test +import kotlin.test.Test class InfoTest { - @Test(groups = ["commands"]) + @Test fun testToUptime() { assertThat( 547800300076L.toUptime(), diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index f1fbe11..34dce0e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,10 +37,10 @@ import assertk.assertions.isEqualTo import assertk.assertions.matches import assertk.assertions.prop import assertk.assertions.size -import org.testng.annotations.Test +import kotlin.test.Test class RecapTest { - @Test(groups = ["commands"]) + @Test fun storeRecapTest() { for (i in 1..20) { Recap.storeRecap("sender$i", "test $i", false) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index 8e49b5e..eea09db 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,7 +1,7 @@ /* * LinksManagerTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -38,12 +38,12 @@ import assertk.assertions.isEqualTo import assertk.assertions.isTrue import assertk.assertions.size import net.thauvin.erik.mobibot.Constants -import org.testng.annotations.Test +import kotlin.test.Test class LinksManagerTest { private val linksManager = LinksManager() - @Test(groups = ["commands", "links"]) + @Test fun fetchTitle() { assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") assertThat( @@ -52,13 +52,13 @@ class LinksManagerTest { ).isEqualTo(Constants.NO_TITLE) } - @Test(groups = ["commands", "links"]) + @Test fun testMatches() { assertThat(linksManager.matches("https://www.example.com/"), "matches(url)").isTrue() assertThat(linksManager.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "matches(HTTP)").isTrue() } - @Test(groups = ["commands", "links"]) + @Test fun matchTagKeywordsTest() { linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3") val tags = mutableListOf() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index c28090d..b5028e3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,10 +36,10 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.prop import net.thauvin.erik.mobibot.entries.EntryLink -import org.testng.annotations.Test +import kotlin.test.Test class ViewTest { - @Test(groups = ["commands", "links"]) + @Test fun testParseArgs() { val view = View() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 52a21cc..331f97c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,7 +1,7 @@ /* * SeenTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,29 +34,16 @@ package net.thauvin.erik.mobibot.commands.seen import assertk.all import assertk.assertThat import assertk.assertions.* -import org.testng.annotations.AfterClass -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.jupiter.api.Order import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize +import kotlin.test.Test class SeenTest { - private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") - private val seen = Seen(tmpFile.toAbsolutePath().toString()) - private val nick = "ErikT" - - @BeforeClass - fun saveTest() { - seen.add("ErikT") - assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) - } - - @AfterClass(alwaysRun = true) - fun afterClass() { - tmpFile.deleteIfExists() - } - - @Test(priority = 1, groups = ["commands"]) + @Test + @Order(1) fun loadTest() { seen.clear() assertThat(seen::seenNicks).isEmpty() @@ -64,7 +51,8 @@ class SeenTest { assertThat(seen::seenNicks).key(nick).isNotNull() } - @Test(groups = ["commands"]) + @Test + @Order(2) fun addTest() { val last = seen.seenNicks[nick]?.lastSeen seen.add(nick.lowercase()) @@ -75,11 +63,31 @@ class SeenTest { } } - @Test(priority = 10, groups = ["commands"]) + @Test + @Order(3) fun clearTest() { seen.clear() seen.save() seen.load() assertThat(seen::seenNicks).size().isEqualTo(0) } + + companion object { + private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") + private val seen = Seen(tmpFile.toAbsolutePath().toString()) + private const val nick = "ErikT" + + @JvmStatic + @BeforeClass + fun beforeClass() { + seen.add(nick) + assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) + } + + @JvmStatic + @AfterClass + fun afterClass() { + tmpFile.deleteIfExists() + } + } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index f7239e0..f2c6f35 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,10 +36,10 @@ 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 import java.time.temporal.Temporal +import kotlin.test.Test /** * The `TellMessageTest` class. @@ -49,7 +49,7 @@ class TellMessageTest { return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 } - @Test(groups = ["commands", "tell"]) + @Test fun testTellMessage() { val message = "Test message." val recipient = "recipient" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index 115e9fb..9ab1bce 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,16 +34,14 @@ package net.thauvin.erik.mobibot.commands.tell import assertk.all import assertk.assertThat import assertk.assertions.* -import org.testng.annotations.AfterClass -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test +import org.junit.AfterClass import java.time.LocalDateTime import kotlin.io.path.createTempFile import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize +import kotlin.test.Test class TellMessagesMgrTest { - private val testFile = createTempFile(suffix = ".ser") private val maxDays = 10L private val testMessages = mutableListOf().apply { for (i in 0..5) { @@ -51,18 +49,12 @@ class TellMessagesMgrTest { } } - @BeforeClass - fun saveTest() { + init { TellManager.save(testFile.toAbsolutePath().toString(), testMessages) assertThat(testFile.fileSize()).isGreaterThan(0) } - @AfterClass - fun afterClass() { - testFile.deleteIfExists() - } - - @Test(groups = ["commands", "tell"]) + @Test fun cleanTest() { testMessages.add(TellMessage("sender", "recipient", "message").apply { queued = LocalDateTime.now().minusDays(maxDays) @@ -73,7 +65,7 @@ class TellMessagesMgrTest { assertThat(testMessages, "testMessages").size().isEqualTo(size - 1) } - @Test(groups = ["commands", "tell"]) + @Test fun loadTest() { val messages = TellManager.load(testFile.toAbsolutePath().toString()) for (i in messages.indices) { @@ -84,4 +76,14 @@ class TellMessagesMgrTest { } } } + + companion object { + private val testFile = createTempFile(suffix = ".ser") + + @JvmStatic + @AfterClass + fun afterClass() { + testFile.deleteIfExists() + } + } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 6eef16e..8e9bd6f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -39,7 +39,7 @@ import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import org.testng.annotations.Test +import kotlin.test.Test class EntriesUtilsTest { private val comment = EntryComment("comment", "nick") @@ -58,12 +58,12 @@ class EntriesUtilsTest { } } - @Test(groups = ["entries"]) + @Test fun printCommentTest() { assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") } - @Test(groups = ["entries"]) + @Test fun printLinkTest() { for (i in links.indices) { assertThat( @@ -75,7 +75,7 @@ class EntriesUtilsTest { assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]") } - @Test(groups = ["entries"]) + @Test fun printTagsTest() { for (i in links.indices) { assertThat( @@ -84,7 +84,7 @@ class EntriesUtilsTest { } } - @Test(groups = ["entries"]) + @Test fun toLinkLabelTest() { assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index ab3feee..495ed15 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,9 +35,9 @@ import assertk.assertThat import assertk.assertions.* import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl -import org.testng.annotations.Test import java.security.SecureRandom import java.util.* +import kotlin.test.Test /** * The `EntryUtilsTest` class. @@ -52,7 +52,7 @@ class EntryLinkTest { listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") ) - @Test(groups = ["entries"]) + @Test fun testAddDeleteComment() { var i = 0 while (i < 5) { @@ -85,7 +85,7 @@ class EntryLinkTest { assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse() } - @Test(groups = ["entries"]) + @Test fun testConstructor() { val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" }) val link = EntryLink("link", "title", "nick", "channel", Date(), tags) @@ -95,7 +95,7 @@ class EntryLinkTest { } } - @Test(groups = ["entries"]) + @Test fun testMatches() { assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue() assertThat(entryLink.matches("skynx"), "match(nick)").isTrue() @@ -106,7 +106,7 @@ class EntryLinkTest { } - @Test(groups = ["entries"]) + @Test fun testTags() { val tags: List = entryLink.tags for ((i, tag) in tags.withIndex()) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index 4223d9d..de6075d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,27 +35,25 @@ import assertk.all import assertk.assertThat import assertk.assertions.* 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.* import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize import kotlin.io.path.name +import kotlin.test.Test class FeedMgrTest { private val entries = Entries() private val channel = "mobibot" - @BeforeSuite(alwaysRun = true) - fun beforeSuite() { + init { entries.logsDir = "src/test/resources/" entries.ircServer = "irc.example.com" entries.channel = channel entries.backlogs = "https://www.mobitopia.org/mobibot/logs" } - @Test(groups = ["entries"]) + @Test fun testFeedMgr() { // Load the feed assertThat(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 2b1d3f9..6df5616 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,13 +37,13 @@ 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.testng.annotations.Test +import kotlin.test.Test /** * The `CalcTest` class. */ class CalcTest { - @Test(groups = ["modules"]) + @Test fun testCalculate() { assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 66fb98d..00fa43f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -1,7 +1,7 @@ /* * ChatGptTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,18 +35,20 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause import assertk.assertions.isInstanceOf +import net.thauvin.erik.mobibot.DisabledOnCi import net.thauvin.erik.mobibot.LocalProperties -import org.testng.annotations.Test +import kotlin.test.Test class ChatGptTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test fun testApiKey() { assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } .isInstanceOf(ModuleException::class.java) .hasNoCause() } - @Test(groups = ["modules", "no-ci"]) + @Test + @DisabledOnCi fun testChat() { val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 0547c95..9847080 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -39,20 +39,17 @@ import net.thauvin.erik.crypto.CryptoPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies -import org.testng.annotations.BeforeClass -import org.testng.annotations.Test +import kotlin.test.Test /** * The `CryptoPricesTest` class. */ class CryptoPricesTest { - @BeforeClass - @Throws(ModuleException::class) - fun before() { + init { loadCurrencies() } - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testMarketPrice() { var price = currentPrice(listOf("BTC")) @@ -70,7 +67,7 @@ class CryptoPricesTest { } } - @Test(groups = ["modules"]) + @Test fun testGetCurrencyName() { assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 5375784..57affe5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -42,23 +42,19 @@ import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols 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 +import kotlin.test.Test /** * The `CurrencyConvertTest` class. */ class CurrencyConverterTest : LocalProperties() { - - @BeforeClass - @Throws(ModuleException::class) - fun before() { + init { val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) loadSymbols(apiKey) } - @Test(groups = ["modules"]) + @Test fun testConvertCurrency() { val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) assertThat( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index cdc04f0..0aaacb9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,10 +35,10 @@ package net.thauvin.erik.mobibot.modules import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.matches -import org.testng.annotations.Test +import kotlin.test.Test class DiceTest { - @Test(groups = ["modules"]) + @Test fun testRoll() { assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") assertThat(Dice.roll(2, 1), "roll(2d1)") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index fa50f8c..b0c154a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,18 +34,19 @@ import assertk.all import assertk.assertFailure import assertk.assertThat import assertk.assertions.* +import net.thauvin.erik.mobibot.DisabledOnCi import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message -import org.testng.annotations.Test +import kotlin.test.Test /** * The `GoogleSearchTest` class. */ class GoogleSearchTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test fun testAPIKeys() { assertThat( searchGoogle("", "apikey", "cssKey").first(), @@ -63,7 +64,8 @@ class GoogleSearchTest : LocalProperties() { .hasMessage("API key not valid. Please pass a valid API key.") } - @Test(groups = ["no-ci", "modules"]) + @Test + @DisabledOnCi @Throws(ModuleException::class) fun testSearchGoogle() { val apiKey = getProperty(GoogleSearch.API_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 55a7b8f..9b8dcb3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,13 +36,13 @@ import assertk.assertions.* import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage -import org.testng.annotations.Test +import kotlin.test.Test /** * The `JokeTest` class. */ class JokeTest { - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testRandomJoke() { val joke = randomJoke() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 9c21f7c..e67c8ad 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,13 +35,13 @@ 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.testng.annotations.Test +import kotlin.test.Test /** * The `Lookup Test` class. */ class LookupTest { - @Test(groups = ["modules"]) + @Test @Throws(Exception::class) fun testLookup() { var result = nslookup("apple.com") @@ -51,7 +51,7 @@ class LookupTest { assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") } - @Test(groups = ["modules"]) + @Test @Throws(Exception::class) fun testWhois() { val result = whois("17.178.96.59", Lookup.WHOIS_HOST) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 34f778a..d189d75 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,7 +1,7 @@ /* * MastodonTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ import assertk.assertThat import assertk.assertions.contains import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot -import org.testng.annotations.Test +import kotlin.test.Test class MastodonTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testToot() { val msg = "Testing Mastodon API from ${getHostName()}" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index db68280..9dda5b3 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ import assertk.all import assertk.assertThat import assertk.assertions.* import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import org.testng.annotations.DataProvider -import org.testng.annotations.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource import java.io.IOException -import java.lang.reflect.Method +import kotlin.test.Test /** * The `ModuleExceptionTest` class. @@ -46,28 +46,30 @@ class ModuleExceptionTest { companion object { const val DEBUG_MESSAGE = "debugMessage" const val MESSAGE = "message" + + @JvmStatic + fun dataProviders(): List { + return listOf( + ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com")), + ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?")), + ModuleException(DEBUG_MESSAGE, MESSAGE) + ) + } } - @DataProvider(name = "dp") - fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array> { - return arrayOf( - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))), - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE)) - ) - } - - @Test(dataProvider = "dp") + @ParameterizedTest + @MethodSource("dataProviders") fun testGetDebugMessage(e: ModuleException) { assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE) } - @Test(dataProvider = "dp") + @ParameterizedTest + @MethodSource("dataProviders") fun testGetMessage(e: ModuleException) { assertThat(e).hasMessage(MESSAGE) } - @Test(groups = ["modules"]) + @Test fun testSanitizeMessage() { val apiKey = "1234567890" var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index e1e79f3..a6ff707 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,18 +34,18 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isNotEmpty import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing -import org.testng.annotations.Test +import kotlin.test.Test /** * The `PingTest` class. */ class PingTest { - @Test(groups = ["modules"]) + @Test fun testPingsArray() { assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty() } - @Test(groups = ["modules"]) + @Test fun testRandomPing() { for (i in 0..9) { assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing()) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 8dc90ba..fb8865b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,10 +34,10 @@ package net.thauvin.erik.mobibot.modules import assertk.assertThat import assertk.assertions.isEqualTo import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw -import org.testng.annotations.Test +import kotlin.test.Test class RockPaperScissorsTest { - @Test(groups = ["modules"]) + @Test fun testWinLoseOrDraw() { assertThat(winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win") assertThat(winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win") diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index aff4188..ae8cff2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -39,7 +39,7 @@ import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message -import org.testng.annotations.Test +import kotlin.test.Test /** * The `StockQuoteTest` class. @@ -49,7 +49,7 @@ class StockQuoteTest : LocalProperties() { return "${label}:[ ]+[0-9.]+".prependIndent() } - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testGetQuote() { val apiKey = getProperty(StockQuote.API_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index d7d65de..e135866 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -43,19 +43,19 @@ 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 net.thauvin.erik.mobibot.msg.Message -import org.testng.annotations.Test +import kotlin.test.Test /** * The `Weather2Test` class. */ class Weather2Test : LocalProperties() { - @Test(groups = ["modules"]) + @Test fun testFtoC() { val t = ftoC(32.0) assertThat(t.second, "32 °F is 0 °C").isEqualTo(0) } - @Test(groups = ["modules"]) + @Test fun testGetCountry() { assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) @@ -67,13 +67,13 @@ class Weather2Test : LocalProperties() { } } - @Test(groups = ["modules"]) + @Test fun testMphToKmh() { val w = mphToKmh(0.62) assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1) } - @Test(groups = ["modules"]) + @Test @Throws(ModuleException::class) fun testWeather() { var query = "98204" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index ae1722d..417f9fc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,7 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,13 +36,14 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasMessage import assertk.assertions.isInstanceOf +import net.thauvin.erik.mobibot.DisabledOnCi import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram -import org.testng.annotations.Test +import kotlin.test.Test class WolframAlphaTest : LocalProperties() { - @Test(groups = ["modules"]) + @Test fun testAppId() { assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } .isInstanceOf(ModuleException::class.java) @@ -52,7 +53,8 @@ class WolframAlphaTest : LocalProperties() { .isInstanceOf(ModuleException::class.java) } - @Test(groups = ["modules", "no-ci"]) + @Test + @DisabledOnCi @Throws(ModuleException::class) fun queryWolframTest() { val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 3602a27..5c8cc84 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -39,14 +39,14 @@ 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.pircbotx.Colors -import org.testng.annotations.Test import java.time.ZoneId +import kotlin.test.Test /** * The `WordTimeTest` class. */ class WordTimeTest { - @Test(groups = ["modules"]) + @Test fun testTime() { assertThat(time(), "time()").matches( ("The time is ${Colors.BOLD}\\d{1,2}:\\d{2}${Colors.BOLD} " + @@ -61,7 +61,7 @@ class WordTimeTest { assertThat(time("BEAT"), "time($BEATS_KEYWORD)").matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex()) } - @Test(groups = ["modules"]) + @Test fun testZones() { COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { assertThat(ZoneId.of(it.value)) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index e856112..477c1a4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,7 +1,7 @@ /* * MessageTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,7 +36,7 @@ import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.assertions.prop -import org.testng.annotations.Test +import kotlin.test.Test class MessageTest { @Test diff --git a/src/test/resources/current.xml b/src/test/resources/current.xml index 535a400..8552a9a 100644 --- a/src/test/resources/current.xml +++ b/src/test/resources/current.xml @@ -1,35 +1,4 @@ - - #mobibot IRC Links diff --git a/version.mustache b/version.mustache deleted file mode 100644 index b2672e4..0000000 --- a/version.mustache +++ /dev/null @@ -1,32 +0,0 @@ -/* -* This file is automatically generated. -* Do not modify! -- ALL CHANGES WILL BE ERASED! -*/ -package {{packageName}} - -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.Instant - -/** -* Provides semantic version information. -* -* @author [Semantic Version Annotation Processor](https://github.com/ethauvin/semver) -*/ -object ReleaseInfo{ - const val PROJECT = "{{project}}" - const val VERSION = "{{version}}" - - @JvmField - val BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault()) - - const val MAJOR = {{major}} - const val MINOR = {{minor}} - const val PATCH = {{patch}} - const val BUILDMETA = "{{buildMeta}}" - const val PRERELEASE = "{{preRelease}}" - - const val WEBSITE = "https://www.mobitopia.org/mobibot/" - const val AUTHOR = "Erik C. Thauvin" - const val AUTHOR_URL = "https://erik.thauvin.net/" -} diff --git a/version.properties b/version.properties deleted file mode 100644 index 9c446af..0000000 --- a/version.properties +++ /dev/null @@ -1,9 +0,0 @@ -#Generated by the Semver Plugin for Gradle -#Wed Nov 01 22:09:32 PDT 2023 -version.buildmeta=20231101220932 -version.major=0 -version.minor=8 -version.patch=0 -version.prerelease=rc -version.project=mobibot -version.semver=0.8.0-rc+20231101220932 From f8de100dde2e02996f1b0e10c9d6183c6f213b63 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 11 Nov 2023 00:34:54 -0800 Subject: [PATCH 754/858] Added missing env variables --- .github/workflows/gradle.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ae6c66f..19081a4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -35,6 +35,16 @@ jobs: run: ./bld compile - name: Run tests with bld + env: + CI_NAME: "GitHub CI" + ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} + CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} + OWM_API_KEY: ${{ secrets.OWM_API_KEY }} + PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} + MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} + MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} + MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} + EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} run: ./bld jacoco - name: SonarCloud Scan From 208f3a82d99f1c056c424948f7ad107d482ea481 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 11 Nov 2023 13:13:35 -0800 Subject: [PATCH 755/858] Ignored bin directory --- .gitignore | 5 +- bin/main/log4j2.xml | 69 --- bin/main/net/thauvin/erik/mobibot/Addons.kt | 190 -------- .../net/thauvin/erik/mobibot/Constants.kt | 102 ---- .../net/thauvin/erik/mobibot/FeedReader.kt | 92 ---- bin/main/net/thauvin/erik/mobibot/Mobibot.kt | 421 ----------------- bin/main/net/thauvin/erik/mobibot/Pinboard.kt | 113 ----- bin/main/net/thauvin/erik/mobibot/Utils.kt | 439 ------------------ .../erik/mobibot/commands/AbstractCommand.kt | 79 ---- .../erik/mobibot/commands/ChannelFeed.kt | 62 --- .../thauvin/erik/mobibot/commands/Cycle.kt | 66 --- .../net/thauvin/erik/mobibot/commands/Die.kt | 62 --- .../thauvin/erik/mobibot/commands/Ignore.kt | 147 ------ .../net/thauvin/erik/mobibot/commands/Info.kt | 124 ----- .../net/thauvin/erik/mobibot/commands/Me.kt | 51 -- .../thauvin/erik/mobibot/commands/Modules.kt | 63 --- .../net/thauvin/erik/mobibot/commands/Msg.kt | 60 --- .../net/thauvin/erik/mobibot/commands/Nick.kt | 51 -- .../thauvin/erik/mobibot/commands/Recap.kt | 81 ---- .../net/thauvin/erik/mobibot/commands/Say.kt | 51 -- .../thauvin/erik/mobibot/commands/Users.kt | 50 -- .../thauvin/erik/mobibot/commands/Versions.kt | 59 --- .../erik/mobibot/commands/links/Comment.kt | 151 ------ .../mobibot/commands/links/LinksManager.kt | 207 --------- .../erik/mobibot/commands/links/Posting.kt | 164 ------- .../erik/mobibot/commands/links/Tags.kt | 87 ---- .../erik/mobibot/commands/links/View.kt | 120 ----- .../mobibot/commands/seen/NickComparator.kt | 45 -- .../erik/mobibot/commands/seen/Seen.kt | 150 ------ .../erik/mobibot/commands/seen/SeenNick.kt | 41 -- .../erik/mobibot/commands/tell/Tell.kt | 306 ------------ .../erik/mobibot/commands/tell/TellManager.kt | 74 --- .../erik/mobibot/commands/tell/TellMessage.kt | 104 ----- .../thauvin/erik/mobibot/entries/Entries.kt | 54 --- .../erik/mobibot/entries/EntriesUtils.kt | 83 ---- .../erik/mobibot/entries/EntryComment.kt | 52 --- .../thauvin/erik/mobibot/entries/EntryLink.kt | 213 --------- .../erik/mobibot/entries/FeedsManager.kt | 187 -------- .../erik/mobibot/modules/AbstractModule.kt | 131 ------ .../net/thauvin/erik/mobibot/modules/Calc.kt | 87 ---- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 176 ------- .../erik/mobibot/modules/CryptoPrices.kt | 159 ------- .../erik/mobibot/modules/CurrencyConverter.kt | 222 --------- .../net/thauvin/erik/mobibot/modules/Dice.kt | 87 ---- .../erik/mobibot/modules/GoogleSearch.kt | 162 ------- .../net/thauvin/erik/mobibot/modules/Joke.kt | 105 ----- .../thauvin/erik/mobibot/modules/Lookup.kt | 171 ------- .../thauvin/erik/mobibot/modules/Mastodon.kt | 149 ------ .../erik/mobibot/modules/ModuleException.kt | 45 -- .../net/thauvin/erik/mobibot/modules/Ping.kt | 83 ---- .../erik/mobibot/modules/RockPaperScissors.kt | 114 ----- .../erik/mobibot/modules/StockQuote.kt | 236 ---------- .../thauvin/erik/mobibot/modules/War.class | Bin 2452 -> 0 bytes .../thauvin/erik/mobibot/modules/Weather2.kt | 250 ---------- .../erik/mobibot/modules/WolframAlpha.kt | 142 ------ .../thauvin/erik/mobibot/modules/WorldTime.kt | 390 ---------------- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 37 -- .../net/thauvin/erik/mobibot/msg/Message.kt | 65 --- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 38 -- .../erik/mobibot/msg/PrivateMessage.kt | 37 -- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 36 -- .../erik/mobibot/social/SocialManager.kt | 116 ----- .../erik/mobibot/social/SocialModule.kt | 96 ---- .../erik/mobibot/social/SocialTimer.kt | 40 -- bin/test/current.xml | 67 --- .../net/thauvin/erik/mobibot/AddonsTest.kt | 86 ---- .../erik/mobibot/ExceptionSanitizer.kt | 60 --- .../thauvin/erik/mobibot/FeedReaderTest.kt | 78 ---- .../thauvin/erik/mobibot/LocalProperties.kt | 85 ---- .../net/thauvin/erik/mobibot/PinboardTest.kt | 81 ---- .../net/thauvin/erik/mobibot/UtilsTest.kt | 278 ----------- .../thauvin/erik/mobibot/commands/InfoTest.kt | 58 --- .../erik/mobibot/commands/RecapTest.kt | 60 --- .../commands/links/LinksManagerTest.kt | 77 --- .../erik/mobibot/commands/links/ViewTest.kt | 111 ----- .../erik/mobibot/commands/seen/SeenTest.kt | 85 ---- .../mobibot/commands/tell/TellMessageTest.kt | 72 --- .../commands/tell/TellMessagesMgrTest.kt | 87 ---- .../erik/mobibot/entries/EntriesUtilsTest.kt | 91 ---- .../erik/mobibot/entries/EntryLinkTest.kt | 133 ------ .../erik/mobibot/entries/FeedMgrTest.kt | 115 ----- .../thauvin/erik/mobibot/modules/CalcTest.kt | 53 --- .../erik/mobibot/modules/ChatGptTest.kt | 62 --- .../erik/mobibot/modules/CryptoPricesTest.kt | 78 ---- .../mobibot/modules/CurrencyConverterTest.kt | 85 ---- .../thauvin/erik/mobibot/modules/DiceTest.kt | 53 --- .../erik/mobibot/modules/GoogleSearchTest.kt | 95 ---- .../thauvin/erik/mobibot/modules/JokeTest.kt | 57 --- .../erik/mobibot/modules/LookupTest.kt | 60 --- .../erik/mobibot/modules/MastodonTest.kt | 54 --- .../mobibot/modules/ModuleExceptionTest.kt | 105 ----- .../thauvin/erik/mobibot/modules/PingTest.kt | 54 --- .../mobibot/modules/RockPaperScissorsTest.kt | 50 -- .../erik/mobibot/modules/StockQuoteTest.kt | 85 ---- .../erik/mobibot/modules/Weather2Test.kt | 117 ----- .../erik/mobibot/modules/WolframAlphaTest.kt | 77 --- .../erik/mobibot/modules/WordTimeTest.kt | 70 --- .../thauvin/erik/mobibot/msg/MessageTest.kt | 109 ----- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 +- src/main/resources/log4j2.xml | 38 -- 100 files changed, 5 insertions(+), 10574 deletions(-) delete mode 100644 bin/main/log4j2.xml delete mode 100644 bin/main/net/thauvin/erik/mobibot/Addons.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/Constants.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/FeedReader.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/Mobibot.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/Pinboard.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/Utils.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Die.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Info.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Me.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Modules.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Msg.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Nick.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Recap.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Say.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Users.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/Versions.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/links/View.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/Entries.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Calc.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Dice.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Joke.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Ping.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/War.class delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/Message.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt delete mode 100644 bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt delete mode 100644 bin/test/current.xml delete mode 100644 bin/test/net/thauvin/erik/mobibot/AddonsTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/LocalProperties.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/PinboardTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/UtilsTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt delete mode 100644 bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt delete mode 100644 src/main/resources/log4j2.xml diff --git a/.gitignore b/.gitignore index fa550e5..6c5fc87 100644 --- a/.gitignore +++ b/.gitignore @@ -54,8 +54,9 @@ atlassian-ide-plugin.xml # Editor-based Rest Client .idea/httpRequests -local.properties - +bin deploy +local.properties logs mobibot.properties +out diff --git a/bin/main/log4j2.xml b/bin/main/log4j2.xml deleted file mode 100644 index 265c88f..0000000 --- a/bin/main/log4j2.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bin/main/net/thauvin/erik/mobibot/Addons.kt b/bin/main/net/thauvin/erik/mobibot/Addons.kt deleted file mode 100644 index 2c5f05d..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Addons.kt +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Addons.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import net.thauvin.erik.mobibot.Utils.notContains -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.links.LinksManager -import net.thauvin.erik.mobibot.modules.AbstractModule -import org.pircbotx.hooks.events.PrivateMessageEvent -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.util.* - -/** - * Modules and Commands addons. - */ -class Addons(private val props: Properties) { - private val logger: Logger = LoggerFactory.getLogger(Addons::class.java) - private val disabledModules = props.getProperty("disabled-modules", "").split(LinksManager.TAG_MATCH) - private val disableCommands = props.getProperty("disabled-commands", "").split(LinksManager.TAG_MATCH) - - val commands: MutableList = mutableListOf() - val modules: MutableList = mutableListOf() - val names = Names - - /** - * Add a module with properties. - */ - fun add(module: AbstractModule): Boolean { - var enabled = false - with(module) { - if (disabledModules.notContains(name, true)) { - if (hasProperties()) { - propertyKeys.forEach { - setProperty(it, props.getProperty(it, "")) - } - } - - if (isEnabled) { - modules.add(this) - names.modules.add(name) - names.commands.addAll(commands) - enabled = true - } else { - if (logger.isDebugEnabled) { - logger.debug("Module $name is disabled.") - } - names.disabledModules.add(name) - } - } else { - names.disabledModules.add(name) - } - } - return enabled - } - - /** - * Add a command with properties. - */ - fun add(command: AbstractCommand): Boolean { - var enabled = false - with(command) { - if (disableCommands.notContains(name, true)) { - if (properties.isNotEmpty()) { - properties.keys.forEach { - setProperty(it, props.getProperty(it, "")) - } - } - if (isEnabled()) { - commands.add(this) - if (isVisible) { - if (isOpOnly) { - names.ops.add(name) - } else { - names.commands.add(name) - } - } - enabled = true - } else { - if (logger.isDebugEnabled) { - logger.debug("Command $name is disabled.") - } - names.disabledCommands.add(name) - } - } else { - names.disabledCommands.add(name) - } - } - return enabled - } - - /** - * Execute a command or module. - */ - 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(channel, args, event) - return true - } - } - val mods = if (event is PrivateMessageEvent) modules.filter { it.isPrivateMsgEnabled } else modules - for (module in mods) { - if (module.commands.contains(cmd)) { - module.commandResponse(channel, cmd, args, event) - return true - } - } - return false - } - - /** - * Match a command. - */ - fun match(channel: String, event: GenericMessageEvent): Boolean { - for (command in commands) { - if (command.matches(event.message)) { - command.commandResponse(channel, event.message, event) - return true - } - } - return false - } - - /** - * Commands and Modules help. - */ - fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean { - for (command in commands) { - if (command.isVisible && command.name.startsWith(topic)) { - return command.helpResponse(channel, topic, event) - } - } - for (module in modules) { - if (module.commands.contains(topic)) { - return module.helpResponse(event) - } - } - return false - } - - /** - * Holds commands and modules names. - */ - object Names { - val modules: MutableList = mutableListOf() - val disabledModules: MutableList = mutableListOf() - val commands: MutableList = mutableListOf() - val disabledCommands: MutableList = mutableListOf() - val ops: MutableList = mutableListOf() - - fun sort() { - modules.sort() - disabledModules.sort() - commands.sort() - disabledCommands.sort() - ops.sort() - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/Constants.kt b/bin/main/net/thauvin/erik/mobibot/Constants.kt deleted file mode 100644 index 98ef74a..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Constants.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Constants.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -/** - * The `Constants`. - */ -object Constants { - /** - * The connect/read timeout in ms. - */ - const val CONNECT_TIMEOUT = 5000 - - /** - * Debug command line argument. - */ - const val DEBUG_ARG = "debug" - - /** - * Default IRC Port. - */ - const val DEFAULT_PORT = 6667 - - /** - * Default IRC Server. - */ - const val DEFAULT_SERVER = "irc.libera.chat" - - /** - * CLI command for usage. - */ - const val CLI_CMD = "java -jar ${ReleaseInfo.PROJECT}.jar" - - /** - * User-Agent - */ - const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" - - /** - * The help command. - */ - const val HELP_CMD = "help" - - /** - * The link command. - */ - const val LINK_CMD = "L" - - /** - * The empty title string. - */ - const val NO_TITLE = "No Title" - - /** - * Properties command line argument. - */ - const val PROPS_ARG = "properties" - - /** - * The tag command - */ - const val TAG_CMD = "T" - - /** - * The timer delay in minutes. - */ - const val TIMER_DELAY = 10L - - /** - * Properties version line argument. - */ - const val VERSION_ARG = "version" -} diff --git a/bin/main/net/thauvin/erik/mobibot/FeedReader.kt b/bin/main/net/thauvin/erik/mobibot/FeedReader.kt deleted file mode 100644 index d82f011..0000000 --- a/bin/main/net/thauvin/erik/mobibot/FeedReader.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * FeedReader.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import com.rometools.rome.io.FeedException -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.FeedsManager -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 java.io.IOException -import java.net.URL - -/** - * Reads an RSS feed. - */ -class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable { - private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) - - /** - * Fetches the Feed's items. - */ - override fun run() { - try { - readFeed(url).forEach { - event.sendMessage("", it) - } - } catch (e: FeedException) { - if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e) - event.sendMessage("An error has occurred while parsing the feed: ${e.message}") - } catch (e: IOException) { - if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e) - event.sendMessage("An IO error has occurred while fetching the feed: ${e.message}") - } - } - - companion object { - @JvmStatic - @Throws(FeedException::class, IOException::class) - fun readFeed(url: String, maxItems: Int = 5): List { - val messages = mutableListOf() - val input = SyndFeedInput() - XmlReader(URL(url).openStream()).use { reader -> - val feed = input.build(reader) - val items = feed.entries - if (items.isEmpty()) { - messages.add(NoticeMessage("There is currently nothing to view.")) - } else { - items.take(maxItems).forEach { - messages.add(NoticeMessage(it.title)) - messages.add(NoticeMessage(helpFormat(it.link.green(), false))) - } - } - } - return messages - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/Mobibot.kt b/bin/main/net/thauvin/erik/mobibot/Mobibot.kt deleted file mode 100644 index f91c457..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Mobibot.kt +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Mobibot.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import kotlinx.cli.ArgParser -import kotlinx.cli.ArgType -import kotlinx.cli.default -import net.thauvin.erik.mobibot.Utils.appendIfMissing -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.getIntProperty -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.lastOrEmpty -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.commands.* -import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap -import net.thauvin.erik.mobibot.commands.links.* -import net.thauvin.erik.mobibot.commands.seen.Seen -import net.thauvin.erik.mobibot.commands.tell.Tell -import net.thauvin.erik.mobibot.modules.* -import net.thauvin.erik.semver.Version -import org.pircbotx.Configuration -import org.pircbotx.PircBotX -import org.pircbotx.hooks.ListenerAdapter -import org.pircbotx.hooks.events.* -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.* -import java.nio.file.Files -import java.nio.file.Paths -import java.util.* -import java.util.regex.Pattern -import kotlin.system.exitProcess - -@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt") -class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() { - // The bot configuration. - private val config: Configuration - - // Commands and Modules - private val addons: Addons - - // Seen command - private val seen: Seen - - // Tell command - private val tell: Tell - - /** Logger. */ - val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java) - - /** - * Connects to the server and joins the channel. - */ - fun connect() { - PircBotX(config).startBot() - } - - /** - * Responds with the default help. - */ - private fun helpDefault(event: GenericMessageEvent) { - event.sendMessage("Type a URL on $channel to post it.") - event.sendMessage("For more information on a specific command, type:") - event.sendMessage( - helpFormat( - helpCmdSyntax("%c ${Constants.HELP_CMD} ", event.bot().nick, event is PrivateMessageEvent) - ) - ) - event.sendMessage("The commands are:") - event.sendList(addons.names.commands, 8, isBold = true, isIndent = true) - if (event.isChannelOp(channel)) { - if (addons.names.disabledCommands.isNotEmpty()) { - event.sendMessage("The disabled commands are:") - event.sendList(addons.names.disabledCommands, 8, isBold = false, isIndent = true) - } - event.sendMessage("The op commands are:") - event.sendList(addons.names.ops, 8, isBold = true, isIndent = true) - } - } - - /** - * Responds with the default, commands or modules help. - */ - private fun helpResponse(event: GenericMessageEvent, topic: String) { - if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) { - helpDefault(event) - } - } - - override fun onAction(event: ActionEvent?) { - event?.channel?.let { - if (channel == it.name) { - event.user?.let { user -> - storeRecap(user.nick, event.action, true) - } - } - } - } - - override fun onDisconnect(event: DisconnectEvent?) { - event?.let { - with(event.getBot()) { - LinksManager.socialManager.notification("$nick disconnected from $serverHostname") - seen.add(userChannelDao.getChannel(channel).users) - } - } - LinksManager.socialManager.shutdown() - } - - override fun onPrivateMessage(event: PrivateMessageEvent?) { - event?.user?.let { user -> - if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}") - val cmds = event.message.trim().split(" ".toRegex(), 2) - val cmd = cmds[0].lowercase() - val args = cmds.lastOrEmpty().trim() - if (cmd.startsWith(Constants.HELP_CMD)) { // help - helpResponse(event, args) - } else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module - helpDefault(event) - } - } - } - - override fun onJoin(event: JoinEvent?) { - event?.user?.let { user -> - with(event.getBot()) { - if (user.nick == nick) { - LinksManager.socialManager.notification( - "$nick has joined ${event.channel.name} on $serverHostname" - ) - seen.add(userChannelDao.getChannel(channel).users) - } else { - tell.send(event) - seen.add(user.nick) - } - } - } - } - - override fun onMessage(event: MessageEvent?) { - event?.user?.let { user -> - tell.send(event) - if (event.message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: - if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") - val cmds = event.message.substring(event.bot().nick.length + 1).trim().split(" ".toRegex(), 2) - val cmd = cmds[0].lowercase() - val args = cmds.lastOrEmpty().trim() - if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help - helpResponse(event, args) - } else { - // Execute module or command - addons.exec(channel, cmd, args, event) - } - } else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc. - if (logger.isTraceEnabled) logger.trace(">>> ${user.nick}: ${event.message}") - } - storeRecap(user.nick, event.message, false) - seen.add(user.nick) - } - } - - override fun onNickChange(event: NickChangeEvent?) { - event?.let { - tell.send(event) - if (!it.oldNick.equals(it.newNick, true)) { - seen.add(it.oldNick) - } - seen.add(it.newNick) - } - } - - override fun onPart(event: PartEvent?) { - event?.user?.let { user -> - with(event.getBot()) { - if (user.nick == nick) { - LinksManager.socialManager.notification( - "$nick has left ${event.channel.name} on $serverHostname" - ) - seen.add(userChannelDao.getChannel(channel).users) - } else { - seen.add(user.nick) - } - } - } - } - - override fun onQuit(event: QuitEvent?) { - event?.user?.let { user -> - seen.add(user.nick) - } - } - - companion object { - @JvmStatic - @Throws(Exception::class) - fun main(args: Array) { - // Set up the command line options - val parser = ArgParser(Constants.CLI_CMD) - val debug by parser.option( - ArgType.Boolean, - Constants.DEBUG_ARG, - Constants.DEBUG_ARG.substring(0, 1), - "Print debug & logging data directly to the console" - ).default(false) - val property by parser.option( - ArgType.String, - Constants.PROPS_ARG, - Constants.PROPS_ARG.substring(0, 1), - "Use alternate properties file" - ).default("./${ReleaseInfo.PROJECT}.properties") - val version by parser.option( - ArgType.Boolean, - Constants.VERSION_ARG, - Constants.VERSION_ARG.substring(0, 1), - "Print version info" - ).default(false) - - // Parse the command line - parser.parse(args) - - if (version) { - // Output the version - println( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION}" + - " (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})" - ) - println(ReleaseInfo.WEBSITE) - } else { - // Load the properties - val p = Properties() - try { - Files.newInputStream( - Paths.get(property) - ).use { fis -> - p.load(fis) - } - } catch (ignore: FileNotFoundException) { - System.err.println("Unable to find properties file.") - exitProcess(1) - } catch (ignore: IOException) { - System.err.println("Unable to open properties file.") - exitProcess(1) - } - val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) - val channel = p.getProperty("channel") - val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar) - - // Redirect stdout and stderr - if (!debug) { - try { - val stdout = PrintStream( - BufferedOutputStream( - FileOutputStream( - logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true - ) - ), true - ) - System.setOut(stdout) - } catch (ignore: IOException) { - System.err.println("Unable to open output (stdout) log file.") - exitProcess(1) - } - try { - val stderr = PrintStream( - BufferedOutputStream( - FileOutputStream("$logsDir$nickname.err", true) - ), true - ) - System.setErr(stderr) - } catch (ignore: IOException) { - System.err.println("Unable to open error (stderr) log file.") - exitProcess(1) - } - } - - // Start the bot - Mobibot(nickname, channel, logsDir, p).connect() - } - } - } - - /** - * Initialize the bot. - */ - init { - 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() - - // Load the current entries - with(LinksManager) { - entries.channel = channel - entries.ircServer = ircServer - entries.logsDir = logsDirPath - entries.backlogs = p.getProperty("backlogs", "") - entries.load() - - // Set up pinboard - pinboard.setApiToken(p.getProperty("pinboard-api-token", "")) - } - - addons = Addons(p) - - // Load the commands - addons.add(ChannelFeed(channel.removePrefix("#"))) - addons.add(Comment()) - addons.add(Cycle()) - addons.add(Die()) - addons.add(Ignore()) - addons.add(LinksManager()) - addons.add(Me()) - addons.add(Modules(addons.names.modules, addons.names.disabledModules)) - addons.add(Msg()) - addons.add(Nick()) - addons.add(Posting()) - addons.add(Recap()) - addons.add(Say()) - - // Seen command - seen = Seen("${logsDirPath}${nickname}-seen.ser") - addons.add(seen) - - addons.add(Tags()) - - // Tell command - tell = Tell("${logsDirPath}${nickname}.ser") - addons.add(tell) - - addons.add(Users()) - addons.add(Versions()) - addons.add(View()) - - // Load social modules - LinksManager.socialManager.add(addons, Mastodon()) - - // Load the modules - addons.add(Calc()) - addons.add(ChatGpt()) - addons.add(CryptoPrices()) - addons.add(CurrencyConverter()) - addons.add(Dice()) - addons.add(GoogleSearch()) - addons.add(Info(tell, seen)) - addons.add(Joke()) - addons.add(Lookup()) - addons.add(Ping()) - addons.add(RockPaperScissors()) - addons.add(StockQuote()) - addons.add(War()) - addons.add(Weather2()) - addons.add(WolframAlpha()) - addons.add(WorldTime()) - - // Sort the addons - addons.names.sort() - } -} - diff --git a/bin/main/net/thauvin/erik/mobibot/Pinboard.kt b/bin/main/net/thauvin/erik/mobibot/Pinboard.kt deleted file mode 100644 index 7cb5aed..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Pinboard.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Pinboard.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.pinboard.PinboardPoster -import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.time.temporal.ChronoUnit -import java.util.* - -/** - * Handles posts to pinboard.in. - */ -class Pinboard { - private val poster = PinboardPoster() - - /** - * Adds a pin. - */ - fun addPin(ircServer: String, entry: EntryLink) { - if (poster.apiToken.isNotBlank()) { - with(entry) { - poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) - } - } - } - - /** - * Sets the pinboard API token. - */ - fun setApiToken(apiToken: String) { - poster.apiToken = apiToken - } - - /** - * Deletes a pin. - */ - fun deletePin(entry: EntryLink) { - if (poster.apiToken.isNotBlank()) { - poster.deletePin(entry.link) - } - - } - - /** - * Updates a pin. - */ - fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) { - if (poster.apiToken.isNotBlank()) { - with(entry) { - if (oldUrl != link) { - poster.deletePin(oldUrl) - } - poster.addPin(link, title, postedBy(ircServer), formatTags(), date.toTimestamp()) - } - } - } - - /** - * Formats a date to a UTC timestamp. - */ - private fun Date.toTimestamp(): String { - return ZonedDateTime.ofInstant( - toInstant().truncatedTo(ChronoUnit.SECONDS), ZoneId.systemDefault() - ).format(DateTimeFormatter.ISO_INSTANT) - } - - /** - * Formats the tags for pinboard. - */ - private fun EntryLink.formatTags(): String { - return nick + formatTags(",", ",") - } - - /** - * Returns the pinboard.in extended attribution line. - */ - private fun EntryLink.postedBy(ircServer: String): String { - return "Posted by $nick on $channel ( $ircServer )" - } -} - diff --git a/bin/main/net/thauvin/erik/mobibot/Utils.kt b/bin/main/net/thauvin/erik/mobibot/Utils.kt deleted file mode 100644 index e4760d2..0000000 --- a/bin/main/net/thauvin/erik/mobibot/Utils.kt +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Utils.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR -import net.thauvin.erik.urlencoder.UrlEncoderUtil -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 org.slf4j.Logger -import java.io.* -import java.net.HttpURLConnection -import java.net.URL -import java.nio.file.Files -import java.nio.file.Paths -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import java.util.* -import kotlin.io.path.exists -import kotlin.io.path.fileSize - -/** - * Miscellaneous utilities. - */ -@Suppress("TooManyFunctions") -object Utils { - private val searchFlags = arrayOf("%c", "%n") - - /** - * Prepends a prefix if not present. - */ - @JvmStatic - fun String.prefixIfMissing(prefix: Char): String { - return if (first() != prefix) { - "$prefix${this}" - } else { - this - } - } - - /** - * Appends a suffix to the end of the String if not present. - */ - @JvmStatic - fun String.appendIfMissing(suffix: Char): String { - return if (last() != suffix) { - "$this${suffix}" - } else { - this - } - } - - /** - * Makes the given int bold. - */ - @JvmStatic - fun Int.bold(): String = toString().bold() - - /** - * Makes the given long bold. - */ - @JvmStatic - fun Long.bold(): String = toString().bold() - - /** - * Makes the given string bold. - */ - @JvmStatic - fun String?.bold(): String = colorize(Colors.BOLD) - - /** - * Returns the [PircBotX] instance. - */ - fun GenericMessageEvent.bot(): PircBotX { - return getBot() as PircBotX - } - - /** - * Capitalize a string. - */ - @JvmStatic - fun String.capitalise(): String = lowercase().replaceFirstChar { it.uppercase() } - - /** - * Capitalize words - */ - @JvmStatic - fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() } - - /** - * Colorize a string. - */ - @JvmStatic - fun String?.colorize(color: String): String { - return when { - isNullOrEmpty() -> { - "" - } - - color == DEFAULT_COLOR -> { - this - } - - Colors.BOLD == color || Colors.REVERSE == color -> { - color + this + color - } - - else -> { - color + this + Colors.NORMAL - } - } - } - - /** - * Makes the given string cyan. - */ - @JvmStatic - fun String?.cyan(): String = colorize(Colors.CYAN) - - /** - * URL encodes the given string. - */ - @JvmStatic - fun String.encodeUrl(): String = UrlEncoderUtil.encode(this) - - /** - * Returns a property as an int. - */ - @JvmStatic - fun Properties.getIntProperty(key: String, defaultValue: Int): Int { - return getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue - } - - /** - * Makes the given string green. - */ - @JvmStatic - fun String?.green(): String = colorize(Colors.DARK_GREEN) - - /** - * Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's - * nick. - */ - @JvmStatic - fun helpCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { - val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) - return text.replaceEach(searchFlags, replace) - } - - /** - * Returns a formatted help string. - */ - @JvmStatic - @JvmOverloads - fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { - val s = if (isBold) help.bold() else help - return if (isIndent) s.prependIndent() else s - } - - /** - * Returns `true` if the specified user is an operator on the [channel]. - */ - @JvmStatic - fun GenericMessageEvent.isChannelOp(channel: String): Boolean { - return this.bot().userChannelDao.getChannel(channel).isOp(this.user) - } - - /** - * Returns `true` if a HTTP status code indicates a successful response. - */ - @JvmStatic - fun Int.isHttpSuccess() = this in 200..399 - - /** - * Returns the last item of a list of strings or empty if none. - */ - @JvmStatic - fun List.lastOrEmpty(): String { - return if (this.size >= 2) { - this.last() - } else - "" - } - - /** - * Load serial data from file. - */ - @JvmStatic - fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any { - val serialFile = Paths.get(file) - if (serialFile.exists() && serialFile.fileSize() > 0) { - try { - ObjectInputStream( - BufferedInputStream(Files.newInputStream(serialFile)) - ).use { input -> - if (logger.isDebugEnabled) logger.debug("Loading the ${description}.") - return input.readObject() - } - } catch (e: IOException) { - logger.error("An IO error occurred loading the ${description}.", e) - } catch (e: ClassNotFoundException) { - logger.error("An error occurred loading the ${description}.", e) - } - } - return default - } - - /** - * Returns `true` if the list does not contain the given string. - */ - @JvmStatic - fun List.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) } - - /** - * Obfuscates the given string. - */ - @JvmStatic - fun String.obfuscate(): String { - return if (isNotBlank()) { - "x".repeat(length) - } else this - } - - /** - * Returns the plural form of a word, if count > 1. - */ - @JvmStatic - fun String.plural(count: Long): String { - return if (count > 1) "${this}s" else this - } - - /** - * Makes the given string red. - */ - @JvmStatic - fun String?.red(): String = colorize(Colors.RED) - - /** - * Replaces all occurrences of Strings within another String. - */ - @JvmStatic - fun String.replaceEach(search: Array, replace: Array): String { - var result = this - if (search.size == replace.size) { - search.forEachIndexed { i, s -> - result = result.replace(s, replace[i]) - } - } - return result - } - - /** - * Makes the given string reverse color. - */ - @JvmStatic - fun String?.reverseColor(): String = colorize(Colors.REVERSE) - - /** - * Save data - */ - @JvmStatic - fun saveSerialData(file: String, data: Any, logger: Logger, description: String) { - try { - BufferedOutputStream(Files.newOutputStream(Paths.get(file))).use { bos -> - ObjectOutputStream(bos).use { output -> - if (logger.isDebugEnabled) logger.debug("Saving the ${description}.") - output.writeObject(data) - } - } - } catch (e: IOException) { - logger.error("Unable to save the ${description}.", e) - } - } - - /** - * Send a formatted commands/modules, etc. list. - */ - @JvmStatic - @JvmOverloads - fun GenericMessageEvent.sendList( - list: List, - 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, message.msg.colorize(message.color)) - } else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) { - respondPrivateMessage(message.msg.colorize(message.color)) - } else { - bot().sendIRC().message(channel, message.msg.colorize(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. - */ - @JvmStatic - fun today(): String = LocalDateTime.now().toIsoLocalDate() - - /** - * Converts a string to an int. - */ - @JvmStatic - fun String.toIntOrDefault(defaultValue: Int): Int { - return try { - toInt() - } catch (e: NumberFormatException) { - defaultValue - } - } - - /** - * Returns the specified date as an ISO local date string. - */ - @JvmStatic - fun Date.toIsoLocalDate(): String { - return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toIsoLocalDate() - } - - /** - * Returns the specified date as an ISO local date string. - */ - @JvmStatic - fun LocalDateTime.toIsoLocalDate(): String = format(DateTimeFormatter.ISO_LOCAL_DATE) - - /** - * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. - */ - @JvmStatic - fun Date.toUtcDateTime(): String { - return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toUtcDateTime() - } - - /** - * Returns the specified date formatted as `yyyy-MM-dd HH:mm`. - */ - @JvmStatic - fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) - - /** - * Makes the given string bold. - */ - @JvmStatic - fun String?.underline(): String = colorize(Colors.UNDERLINE) - - - /** - * Converts XML/XHTML entities to plain text. - */ - @JvmStatic - fun String.unescapeXml(): String = Jsoup.parse(this).text() - - /** - * Reads contents of a URL. - */ - @JvmStatic - @Throws(IOException::class) - fun URL.reader(): UrlReaderResponse { - val connection = this.openConnection() as HttpURLConnection - connection.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" - ) - return if (connection.responseCode.isHttpSuccess()) { - UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) - } else { - UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() }) - } - } - - /** - * Holds the [URL.reader] response code and body text. - */ - data class UrlReaderResponse(val responseCode: Int, val body: String) -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt deleted file mode 100644 index 5f79472..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * AbstractCommand.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -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 - -abstract class AbstractCommand { - abstract val name: String - abstract val help: List - abstract val isOpOnly: Boolean - abstract val isPublic: Boolean - abstract val isVisible: Boolean - - val properties: MutableMap = mutableMapOf() - - abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent) - - open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { - if (!isOpOnly || isOpOnly == event.isChannelOp(channel)) { - for (h in help) { - event.sendMessage(helpCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic)) - } - return true - } - return false - } - - open fun initProperties(vararg keys: String) { - keys.forEach { - properties[it] = "" - } - } - - open fun isEnabled(): Boolean { - return true - } - - open fun matches(message: String): Boolean { - return false - } - - open fun setProperty(key: String, value: String) { - properties[key] = value - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt deleted file mode 100644 index 038e378..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * ChannelFeed.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.FeedReader -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent - -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 isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val FEED_PROP = "feed" - } - - init { - initProperties(FEED_PROP) - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (isEnabled()) { - properties[FEED_PROP]?.let { FeedReader(it, event).run() } - } - } - - override fun isEnabled(): Boolean { - return !properties[FEED_PROP].isNullOrBlank() - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt b/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt deleted file mode 100644 index 9608ca8..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Cycle.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Cycle.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 kotlinx.coroutines.delay -import kotlinx.coroutines.launch -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 : 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 isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - with(event.bot()) { - if (event.isChannelOp(channel)) { - runBlocking { - launch { - 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 { - helpResponse(channel, args, event) - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Die.kt b/bin/main/net/thauvin/erik/mobibot/commands/Die.kt deleted file mode 100644 index f271bfa..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Die.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Die.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.bot -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Die : AbstractCommand() { - override val name = "die" - override val help = emptyList() - override val isOpOnly = true - override val isPublic = false - override val isVisible = false - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - with(event.bot()) { - if (event.isChannelOp(channel) && (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 { - const val DIE_PROP = "die" - } - - init { - initProperties(DIE_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt b/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt deleted file mode 100644 index d083c10..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Ignore.kt +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Ignore.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.bold -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -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.LinksManager -import org.pircbotx.hooks.types.GenericMessageEvent - -class Ignore : AbstractCommand() { - private val me = "me" - - init { - initProperties(IGNORE_PROP) - } - - override val name = IGNORE_CMD - override val help = listOf( - "To ignore a link posted to the channel:", - helpFormat("https://www.foo.bar %n"), - "To check your ignore status:", - helpFormat("%c $name"), - "To toggle your ignore status:", - helpFormat("%c $name $me") - ) - private val helpOp = help.plus( - arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name [ ...]")) - ) - - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val IGNORE_CMD = "ignore" - const val IGNORE_PROP = IGNORE_CMD - private val ignored = mutableSetOf() - - @JvmStatic - fun isNotIgnored(nick: String): Boolean { - return !ignored.contains(nick.lowercase()) - } - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val isMe = args.trim().equals(me, true) - if (isMe || !event.isChannelOp(channel)) { - val nick = event.user.nick.lowercase() - ignoreNick(nick, isMe, event) - } else { - ignoreOp(args, event) - } - } - - override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { - return if (event.isChannelOp(channel)) { - for (h in helpOp) { - event.sendMessage(helpCmdSyntax(h, event.bot().nick, true)) - } - true - } else { - super.helpResponse(channel, topic, event) - } - } - - private fun ignoreNick(sender: String, isMe: Boolean, event: GenericMessageEvent) { - if (isMe) { - if (ignored.remove(sender)) { - event.sendMessage("You are no longer ignored.") - } else { - ignored.add(sender) - event.sendMessage("You are now ignored.") - } - } else { - if (ignored.contains(sender)) { - event.sendMessage("You are currently ignored.") - } else { - event.sendMessage("You are not currently ignored.") - } - } - } - - private fun ignoreOp(args: String, event: GenericMessageEvent) { - if (args.isNotEmpty()) { - val nicks = args.lowercase().split(" ") - for (nick in nicks) { - val ignore = if (me == nick) { - nick.lowercase() - } else { - nick - } - if (!ignored.remove(ignore)) { - ignored.add(ignore) - } - } - } - - if (ignored.isNotEmpty()) { - event.sendMessage("The following nicks are ignored:") - event.sendList(ignored.sorted(), 8, isIndent = true) - } else { - event.sendMessage("No one is currently ${"ignored".bold()}.") - } - } - - override fun setProperty(key: String, value: String) { - super.setProperty(key, value) - if (IGNORE_PROP == key) { - ignored.addAll(value.split(LinksManager.TAG_MATCH)) - } - } - -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Info.kt b/bin/main/net/thauvin/erik/mobibot/commands/Info.kt deleted file mode 100644 index ed0b6ef..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Info.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Info.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.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.plural -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.links.LinksManager -import net.thauvin.erik.mobibot.commands.seen.Seen -import net.thauvin.erik.mobibot.commands.tell.Tell -import org.pircbotx.hooks.types.GenericMessageEvent -import java.lang.management.ManagementFactory -import kotlin.time.DurationUnit -import kotlin.time.toDuration - -class Info(private val tell: Tell, private val seen: Seen) : AbstractCommand() { - private val allVersions = listOf( - "${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})", - "Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})" - ) - override val name = "info" - override val help = listOf("To view information about the bot:", helpFormat("%c $name")) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - /** - * Converts milliseconds to year month week day hour and minutes. - */ - @JvmStatic - fun Long.toUptime(): String { - this.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, seconds, _ -> - val years = wholeDays / 365 - var days = wholeDays % 365 - val months = days / 30 - days %= 30 - val weeks = days / 7 - days %= 7 - - with(StringBuffer()) { - if (years > 0) { - append(years).append(" year".plural(years)).append(' ') - } - if (months > 0) { - append(months).append(" month".plural(months)).append(' ') - } - if (weeks > 0) { - append(weeks).append(" week".plural(weeks)).append(' ') - } - if (days > 0) { - append(days).append(" day".plural(days)).append(' ') - } - if (hours > 0) { - append(hours).append(" hour".plural(hours.toLong())).append(' ') - } - - if (minutes > 0) { - append(minutes).append(" minute".plural(minutes.toLong())) - } else { - append(seconds).append(" second".plural(seconds.toLong())) - } - - return toString() - } - } - } - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - event.sendList(allVersions, 1) - val info = StringBuilder() - info.append("Uptime: ") - .append(ManagementFactory.getRuntimeMXBean().uptime.toUptime()) - .append(" [Entries: ") - .append(LinksManager.entries.links.size) - if (seen.isEnabled()) { - info.append(", Seen: ").append(seen.count()) - } - if (event.isChannelOp(channel)) { - if (tell.isEnabled()) { - info.append(", Messages: ").append(tell.size()) - } - if (LinksManager.socialManager.entriesCount() > 0) { - info.append(", Social: ").append(LinksManager.socialManager.entriesCount()) - } - } - info.append(", Recap: ").append(Recap.recaps.size).append(']') - event.sendMessage(info.toString()) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Me.kt b/bin/main/net/thauvin/erik/mobibot/commands/Me.kt deleted file mode 100644 index ec7823b..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Me.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Me.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Me : AbstractCommand() { - override val name = "me" - override val help = listOf("To have the bot perform an action:", helpFormat("%c $name ")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - event.bot().sendIRC().action(channel, args) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt b/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt deleted file mode 100644 index b2293b0..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Modules.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Modules.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import net.thauvin.erik.mobibot.Utils.sendList -import org.pircbotx.hooks.types.GenericMessageEvent - -class Modules(private val modules: List, private val disabledModules: List) : AbstractCommand() { - override val name = "modules" - override val help = listOf("To view a list of enabled/disabled modules:", helpFormat("%c $name")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - if (modules.isEmpty()) { - event.respondPrivateMessage("There are no enabled modules.") - } else { - event.respondPrivateMessage("The enabled modules are: ") - event.sendList(modules, 7, isIndent = true) - } - if (disabledModules.isNotEmpty()) { - event.respondPrivateMessage("The disabled modules are: ") - event.sendList(disabledModules, 7, isIndent = true) - } - } else { - helpResponse(channel, args, event) - } - } -} - diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt b/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt deleted file mode 100644 index 20a6635..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Msg.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Msg.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Msg : AbstractCommand() { - override val name = "msg" - override val help = listOf( - "To have the bot send a private message to someone:", - helpFormat("%c $name ") - ) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - val msg = args.split(" ", limit = 2) - if (args.length > 2) { - event.bot().sendIRC().message(msg[0], msg[1]) - event.respondPrivateMessage("A message was sent to ${msg[0]}") - } else { - helpResponse(channel, args, event) - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt b/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt deleted file mode 100644 index 85a03ab..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Nick.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Nick.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Nick : AbstractCommand() { - override val name = "nick" - override val help = listOf("To change the bot's nickname:", helpFormat("%c $name ")) - override val isOpOnly = true - override val isPublic = true - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - event.bot().sendIRC().changeNick(args) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt b/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt deleted file mode 100644 index 77154c7..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Recap.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Recap.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.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 : AbstractCommand() { - override val name = "recap" - override val help = listOf( - "To list the last 10 public channel messages:", - helpFormat("%c $name") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val MAX_RECAPS = 10 - - @JvmField - val recaps = mutableListOf() - - /** - * Stores the last 10 public messages and actions. - */ - @JvmStatic - fun storeRecap(sender: String, message: String, isAction: Boolean) { - recaps.add( - LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() - + " - $sender" + (if (isAction) " " else ": ") + message - ) - if (recaps.size > MAX_RECAPS) { - recaps.removeFirst() - } - } - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (recaps.isNotEmpty()) { - for (r in recaps) { - event.sendMessage(r) - } - } else { - event.sendMessage("Sorry, nothing to recap.") - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Say.kt b/bin/main/net/thauvin/erik/mobibot/commands/Say.kt deleted file mode 100644 index 7f76d35..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Say.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Say.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.isChannelOp -import org.pircbotx.hooks.types.GenericMessageEvent - -class Say : AbstractCommand() { - override val name = "say" - override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name ")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - event.bot().sendIRC().message(channel, args) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Users.kt b/bin/main/net/thauvin/erik/mobibot/commands/Users.kt deleted file mode 100644 index 33d6fef..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Users.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Users.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendList -import org.pircbotx.hooks.types.GenericMessageEvent - -class Users : AbstractCommand() { - override val name = "users" - override val help = listOf("To list the users present on the channel:", helpFormat("%c $name")) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val ch = event.bot().userChannelDao.getChannel(channel) - event.sendList(ch.users.map { if (it.channelsOpIn.contains(ch)) "@${it.nick}" else it.nick }, 8) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt b/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt deleted file mode 100644 index 896c569..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/Versions.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Versions.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.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.PircBotX -import org.pircbotx.hooks.types.GenericMessageEvent - -class Versions : AbstractCommand() { - private val allVersions = listOf( - "Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})", - "${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" + - ", JVM ${System.getProperty("java.runtime.version")}", - "Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}" - ) - override val name = "versions" - override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name")) - override val isOpOnly = true - override val isPublic = false - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - event.sendList(allVersions, 1) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt deleted file mode 100644 index 1443d44..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Comment.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.links - -import net.thauvin.erik.mobibot.Constants -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.printComment -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import net.thauvin.erik.mobibot.entries.EntryLink -import org.pircbotx.hooks.types.GenericMessageEvent - -class Comment : AbstractCommand() { - override val name = COMMAND - override val help = listOf( - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, use its label: ", - helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"), - "To delete a comment, use its label and a minus sign: ", - helpFormat("${Constants.LINK_CMD}1.1:-") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val COMMAND = "comment" - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val cmds = args.substring(1).split("[.:]".toRegex(), 3) - val entryIndex = cmds[0].toInt() - 1 - - if (entryIndex < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) { - val entry: EntryLink = LinksManager.entries.links[entryIndex] - val commentIndex = cmds[1].toInt() - 1 - if (commentIndex < entry.comments.size) { - when (val cmd = cmds[2].trim()) { - "" -> showComment(entry, entryIndex, commentIndex, event) // L1.1: - "-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:- - else -> { - if (cmd.startsWith('?')) { // L1.1:? - changeAuthor(channel, cmd, entry, entryIndex, commentIndex, event) - } else { // L1.1: - setComment(cmd, entry, entryIndex, commentIndex, event) - } - } - } - } - } - } - - override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { - if (super.helpResponse(channel, topic, event)) { - if (event.isChannelOp(channel)) { - event.sendMessage("To change a comment's author:") - event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?")) - } - return true - } - return false - } - - override fun matches(message: String): Boolean { - return message.matches("^${Constants.LINK_CMD}\\d+\\.\\d+:.*".toRegex()) - } - - private fun changeAuthor( - channel: String, - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent - ) { - if (event.isChannelOp(channel) && cmd.length > 1) { - val comment = entry.getComment(commentIndex) - comment.nick = cmd.substring(1) - event.sendMessage(printComment(entryIndex, commentIndex, comment)) - LinksManager.entries.save() - } else { - event.sendMessage("Please ask a channel op to change the author of this comment for you.") - } - } - - private fun deleteComment( - channel: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent - ) { - if (event.isChannelOp(channel) || event.user.nick == entry.getComment(commentIndex).nick) { - entry.deleteComment(commentIndex) - event.sendMessage("Comment ${entryIndex.toLinkLabel()}.${commentIndex + 1} removed.") - LinksManager.entries.save() - } else { - event.sendMessage("Please ask a channel op to delete this comment for you.") - } - } - - private fun setComment( - cmd: String, - entry: EntryLink, - entryIndex: Int, - commentIndex: Int, - event: GenericMessageEvent - ) { - entry.setComment(commentIndex, cmd, event.user.nick) - event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) - LinksManager.entries.save() - } - - private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) { - event.sendMessage(printComment(entryIndex, commentIndex, entry.getComment(commentIndex))) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt deleted file mode 100644 index fba6b99..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ /dev/null @@ -1,207 +0,0 @@ -/* - * LinksManager.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.links - -import net.thauvin.erik.mobibot.Constants -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.Companion.isNotIgnored -import net.thauvin.erik.mobibot.entries.Entries -import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.social.SocialManager -import org.jsoup.Jsoup -import org.pircbotx.hooks.types.GenericMessageEvent -import java.io.IOException - -class LinksManager : AbstractCommand() { - private val defaultTags: MutableList = mutableListOf() - private val keywords: MutableList = mutableListOf() - - override val name = Constants.LINK_CMD - override val help = emptyList() - override val isOpOnly = false - override val isPublic = false - override val isVisible = false - - init { - initProperties(TAGS_PROP, KEYWORDS_PROP) - } - - companion object { - val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*".toRegex() - const val KEYWORDS_PROP = "tags-keywords" - const val TAGS_PROP = "tags" - val TAG_MATCH = ", *| +".toRegex() - - /** - * Entries array - */ - @JvmField - val entries = Entries() - - /** - * Pinboard handler. - */ - @JvmField - val pinboard = Pinboard() - - /** - * Social Manager handler. - */ - @JvmField - val socialManager = SocialManager() - - /** - * Let the user know if the entries are too old to be modified. - */ - @JvmStatic - fun isUpToDate(event: GenericMessageEvent): Boolean { - if (entries.lastPubDate != today()) { - event.sendMessage("The links are too old to be updated.") - return false - } - return true - } - } - - 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 (isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(botNick))) { - val link = cmds[0].trim() - if (!isDupEntry(link, event)) { - var title = "" - val tags = ArrayList(defaultTags) - if (cmds.size == 2) { - val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2) - title = data[0].trim() - if (data.size > 1) { - tags.addAll(data[1].split(TAG_MATCH)) - } - } - - if (title.isBlank()) { - title = fetchTitle(link) - } - - if (title != Constants.NO_TITLE) { - matchTagKeywords(title, tags) - } - - // Links are old, clear them - if (entries.lastPubDate != today()) { - entries.links.clear() - } - - val entry = EntryLink(link, title, sender, login, channel, tags) - entries.links.add(entry) - val index = entries.links.lastIndexOf(entry) - event.sendMessage(printLink(index, entry)) - - pinboard.addPin(event.bot().serverHostname, entry) - - // Queue link for posting to social media. - socialManager.queueEntry(index) - - entries.save() - - if (Constants.NO_TITLE == entry.title) { - event.sendMessage("Please specify a title, by typing:") - event.sendMessage(helpFormat("${index.toLinkLabel()}:|This is the title")) - } - } - } - } - - override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false - - override fun matches(message: String): Boolean { - return message.matches(LINK_MATCH) - } - - internal fun fetchTitle(link: String): String { - try { - val html = Jsoup.connect(link) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") - .get() - val title = html.title() - if (title.isNotBlank()) { - return title - } - } catch (ignore: IOException) { - // Do nothing - } - return Constants.NO_TITLE - } - - private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean { - synchronized(entries) { - return try { - val match = entries.links.single { it.link == link } - event.sendMessage( - "Duplicate".bold() + " >> " + printLink(entries.links.indexOf(match), match) - ) - true - } catch (ignore: NoSuchElementException) { - false - } - } - } - - internal fun matchTagKeywords(title: String, tags: MutableList) { - for (match in keywords) { - val m = Regex.escape(match) - if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { - tags.add(match) - } - } - } - - override fun setProperty(key: String, value: String) { - super.setProperty(key, value) - if (KEYWORDS_PROP == key) { - keywords.addAll(value.split(TAG_MATCH)) - } else if (TAGS_PROP == key) { - defaultTags.addAll(value.split(TAG_MATCH)) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt deleted file mode 100644 index ff4278d..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Posting.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.links - -import net.thauvin.erik.mobibot.Constants -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.LinksManager.Companion.entries -import net.thauvin.erik.mobibot.entries.EntriesUtils -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import net.thauvin.erik.mobibot.entries.EntryLink -import org.pircbotx.hooks.types.GenericMessageEvent - -class Posting : AbstractCommand() { - override val name = "posting" - override val help = listOf( - "Post a URL, by saying it on a line on its own:", - helpFormat(" [] ${Tags.COMMAND}: <+tag> [...]]"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1", - "To add a title, use its label and a pipe:", - helpFormat("${Constants.LINK_CMD}1:|This is the title"), - "To add a comment:", - helpFormat("${Constants.LINK_CMD}1:This is a comment"), - "I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1", - "To edit a comment, see: ", - helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - val cmds = args.substring(1).split(":", limit = 2) - val entryIndex = cmds[0].toInt() - 1 - - if (entryIndex < entries.links.size) { - val cmd = cmds[1].trim() - if (cmd.isBlank()) { - showEntry(entryIndex, event) // L1: - } else if (LinksManager.isUpToDate(event)) { - if (cmd == "-") { - removeEntry(channel, entryIndex, event) // L1:- - } else { - when (cmd[0]) { - '|' -> 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> - } - } - } - } - } - - override fun matches(message: String): Boolean { - return message.matches("${Constants.LINK_CMD}\\d+:.*".toRegex()) - } - - 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) - event.sendMessage(EntriesUtils.printComment(entryIndex, commentIndex, comment)) - entries.save() - } - - private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) { - if (cmd.length > 1) { - val entry: EntryLink = entries.links[entryIndex] - entry.title = cmd.substring(1).trim() - LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) - entries.save() - } - } - - private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) { - val entry: EntryLink = entries.links[entryIndex] - if (entry.login == event.user.login || event.isChannelOp(channel)) { - val link = cmd.substring(1) - if (link.matches(LinksManager.LINK_MATCH)) { - val oldLink = entry.link - entry.link = link - LinksManager.pinboard.updatePin(event.bot().serverHostname, oldLink, entry) - event.sendMessage(EntriesUtils.printLink(entryIndex, entry)) - entries.save() - } - } - } - - private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) { - if (event.isChannelOp(channel)) { - if (cmd.length > 1) { - val entry: EntryLink = entries.links[index] - entry.nick = cmd.substring(1) - LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.printLink(index, entry)) - entries.save() - } - } else { - event.sendMessage("Please ask a channel op to change the author of this link for you.") - } - } - - private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) { - val entry: EntryLink = entries.links[index] - if (entry.login == event.user.login || event.isChannelOp(channel)) { - LinksManager.pinboard.deletePin(entry) - LinksManager.socialManager.removeEntry(index) - entries.links.removeAt(index) - event.sendMessage("Entry ${index.toLinkLabel()} removed.") - entries.save() - } else { - event.sendMessage("Please ask a channel op to remove this entry for you.") - } - } - - private fun showEntry(index: Int, event: GenericMessageEvent) { - val entry: EntryLink = entries.links[index] - event.sendMessage(EntriesUtils.printLink(index, entry)) - if (entry.tags.isNotEmpty()) { - event.sendMessage(EntriesUtils.printTags(index, entry)) - } - if (entry.comments.isNotEmpty()) { - val comments = entry.comments - for (i in comments.indices) { - event.sendMessage(EntriesUtils.printComment(index, i, comments[i])) - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt deleted file mode 100644 index 1662857..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Tags.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.links - -import net.thauvin.erik.mobibot.Constants -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 : 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 isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val COMMAND = "tags" - } - - 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 < LinksManager.entries.links.size && LinksManager.isUpToDate(event)) { - val cmd = cmds[1].trim() - val entry: EntryLink = LinksManager.entries.links[index] - if (cmd.isNotEmpty()) { - if (entry.login == event.user.login || event.isChannelOp(channel)) { - entry.setTags(cmd) - LinksManager.pinboard.updatePin(event.bot().serverHostname, entry.link, entry) - event.sendMessage(EntriesUtils.printTags(index, entry)) - LinksManager.entries.save() - } else { - event.sendMessage("Please ask a channel op to change the tags for you.") - } - } else { - if (entry.tags.isNotEmpty()) { - event.sendMessage(EntriesUtils.printTags(index, entry)) - } else { - event.sendMessage("The entry has no tags. Why don't add some?") - } - } - } - } - - override fun matches(message: String): Boolean { - return message.matches("^${Constants.LINK_CMD}\\d+${Constants.TAG_CMD}:.*".toRegex()) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt b/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt deleted file mode 100644 index 825e374..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/links/View.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * View.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.links - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.lastOrEmpty -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.links.LinksManager.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 : AbstractCommand() { - override val name = VIEW_CMD - override val help = listOf( - "To list or search the current URL posts:", - helpFormat("%c $name [<start>] [<query>]") - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - companion object { - const val MAX_ENTRIES = 6 - const val VIEW_CMD = "view" - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (entries.links.isNotEmpty()) { - val p = parseArgs(args) - showPosts(p.first, p.second, event) - } else { - event.sendMessage("There is currently nothing to view. Why don't you post something?") - } - } - - internal fun parseArgs(args: String): Pair<Int, String> { - var query = args.lowercase().trim() - var start = 0 - if (query.isEmpty() && entries.links.size > MAX_ENTRIES) { - start = entries.links.size - MAX_ENTRIES - } - if (query.matches("^\\d+(| .*)".toRegex())) { // view [<start>] [<query>] - val split = query.split(" ", limit = 2) - try { - start = split[0].toInt() - 1 - query = split.lastOrEmpty().trim() - if (start > entries.links.size) { - start = 0 - } - } catch (ignore: NumberFormatException) { - // Do nothing - } - } - return Pair(start, query) - } - - private fun showPosts(start: Int, query: String, event: GenericMessageEvent) { - var index = start - var entry: EntryLink - var sent = 0 - while (index < entries.links.size && sent < MAX_ENTRIES) { - entry = entries.links[index] - if (query.isNotBlank()) { - if (entry.matches(query)) { - event.sendMessage(EntriesUtils.printLink(index, entry, true)) - sent++ - } - } else { - event.sendMessage(EntriesUtils.printLink(index, entry, true)) - sent++ - } - index++ - if (sent == MAX_ENTRIES && index < entries.links.size) { - event.sendMessage("To view more, try: ") - event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent) - ) - ) - } - } - if (sent == 0) { - event.sendMessage("No matches. Please try again.") - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt deleted file mode 100644 index cfd2c27..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * NickComparator.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.seen - -import java.io.Serializable - -class NickComparator : Comparator<String>, Serializable { - override fun compare(a: String, b: String): Int { - return a.lowercase().compareTo(b.lowercase()) - } - - companion object { - @Suppress("ConstPropertyName") - private const val serialVersionUID = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt deleted file mode 100644 index c9ee0f3..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Seen.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.seen - -import com.google.common.collect.ImmutableSortedSet -import net.thauvin.erik.mobibot.Utils -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.loadSerialData -import net.thauvin.erik.mobibot.Utils.saveSerialData -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.commands.AbstractCommand -import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime -import org.pircbotx.User -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.util.* - - -class Seen(private val serialObject: String) : AbstractCommand() { - private val logger: Logger = LoggerFactory.getLogger(Seen::class.java) - private val allKeyword = "all" - val seenNicks = TreeMap<String, SeenNick>(NickComparator()) - - override val name = "seen" - override val help = listOf("To view when a nickname was last seen:", helpFormat("%c $name <nick>")) - private val helpOp = help.plus( - arrayOf("To view all ${"seen".bold()} nicks:", helpFormat("%c $name $allKeyword")) - ) - override val isOpOnly = false - override val isPublic = true - override val isVisible = true - - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (isEnabled()) { - if (args.isNotBlank() && !args.contains(' ')) { - val ch = event.bot().userChannelDao.getChannel(channel) - if (args == allKeyword && ch.isOp(event.user) && seenNicks.isNotEmpty()) { - event.sendMessage("The ${"seen".bold()} nicks are:") - event.sendList(seenNicks.keys.toList(), 7, separator = ", ", isIndent = true) - return - } - ch.users.forEach { - if (args.equals(it.nick, true)) { - event.sendMessage("${it.nick} is on ${channel}.") - return - } - } - if (seenNicks.containsKey(args)) { - val seenNick = seenNicks.getValue(args) - val lastSeen = System.currentTimeMillis() - seenNick.lastSeen - event.sendMessage("${seenNick.nick} was last seen on $channel ${lastSeen.toUptime()} ago.") - return - } - event.sendMessage("I haven't seen $args on $channel lately.") - } else { - helpResponse(channel, args, event) - } - } - } - - fun add(nick: String) { - if (isEnabled()) { - seenNicks[nick] = SeenNick(nick, System.currentTimeMillis()) - save() - } - } - - fun add(users: ImmutableSortedSet<User>) { - if (isEnabled()) { - users.forEach { - seenNicks[it.nick] = SeenNick(it.nick, System.currentTimeMillis()) - } - save() - } - } - - fun clear() { - seenNicks.clear() - } - - fun count(): Int = seenNicks.size - - override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean { - return if (event.isChannelOp(channel)) { - for (h in helpOp) { - event.sendMessage(Utils.helpCmdSyntax(h, event.bot().nick, true)) - } - true - } else { - super.helpResponse(channel, topic, event) - } - } - - fun load() { - if (isEnabled()) { - @Suppress("UNCHECKED_CAST") - seenNicks.putAll( - loadSerialData( - serialObject, - TreeMap<String, SeenNick>(), - logger, - "seen nicknames" - ) as TreeMap<String, SeenNick> - ) - } - } - - fun save() { - saveSerialData(serialObject, seenNicks, logger, "seen nicknames") - } - - init { - load() - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt deleted file mode 100644 index 7924977..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SeenNick.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.seen - -import java.io.Serializable - -data class SeenNick(val nick: String, val lastSeen: Long) : Serializable { - companion object { - @Suppress("ConstPropertyName") - private const val serialVersionUID = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt deleted file mode 100644 index 061ca6a..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Tell.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.tell - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -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(private val serialObject: String) : AbstractCommand() { - // Messages queue - private val messages: MutableList<TellMessage> = mutableListOf() - - // Maximum number of days to keep messages - private var maxDays = 7 - - // Message maximum queue size - private var maxSize = 50 - - /** - * The tell command. - */ - override val name = "tell" - - override val help = listOf( - "To send a message to someone when they join the channel:", - helpFormat("%c $name <nick> <message>"), - "To view queued and sent messages:", - helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.' - ) - override val isOpOnly: Boolean = false - override val isPublic: Boolean = isEnabled() - override val isVisible: Boolean = isEnabled() - - /** - * Cleans the messages queue. - */ - private fun clean(): Boolean { - return TellManager.clean(messages, maxDays.toLong()) - } - - override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { - if (isEnabled()) { - when { - args.isBlank() -> { - helpResponse(channel, args, event) - } - - args.startsWith(View.VIEW_CMD) -> { - if (event.isChannelOp(channel) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) { - viewAll(event) - } else { - viewMessages(event) - } - } - - args.startsWith("$TELL_DEL_KEYWORD ") -> { - deleteMessage(channel, args, event) - } - - else -> { - newMessage(channel, args, event) - } - } - if (clean()) { - save() - } - } - } - - // Delete message. - private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) { - val split = args.split(" ") - if (split.size == 2) { - val id = split[1] - if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) { - if (messages.removeIf { it.sender.equals(event.user.nick, true) && it.isReceived }) { - save() - event.sendMessage("Delivered messages have been deleted.") - } else { - event.sendMessage("No delivered messages were found.") - } - } else { - if (messages.removeIf { - it.id == id && - (it.sender.equals(event.user.nick, true) || event.isChannelOp(channel)) - }) { - save() - event.sendMessage("The message was deleted from the queue.") - } else { - event.sendMessage("The specified message [ID $id] could not be found.") - } - } - } else { - helpResponse(channel, args, event) - } - } - - override fun isEnabled(): Boolean { - return maxSize > 0 && maxDays > 0 - } - - override fun setProperty(key: String, value: String) { - super.setProperty(key, value) - if (MAX_DAYS_PROP == key) { - maxDays = value.toIntOrDefault(maxDays) - } else if (MAX_SIZE_PROP == key) { - maxSize = value.toIntOrDefault(maxSize) - } - } - - // New message. - 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(event.user.nick, split[0], split[1].trim()) - messages.add(message) - save() - event.sendMessage("Message [ID ${message.id}] was queued for ${message.recipient.bold()}") - } else { - event.sendMessage("Sorry, the messages queue is currently full.") - } - } else { - helpResponse(channel, args, event) - } - } - - /** - * Saves the messages queue. - */ - private fun save() { - TellManager.save(serialObject, messages) - } - - /** - * Checks and sends messages. - */ - fun send(event: GenericUserEvent) { - val nickname = event.user.nick - if (isEnabled() && nickname != event.getBot<PircBotX>().nick) { - messages.filter { it.isMatch(nickname) }.forEach { message -> - if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) { - if (message.sender == nickname) { - if (event !is MessageEvent) { - event.user.send().message( - "${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}" - ) - message.isReceived = true - message.isNotified = true - save() - } - } else { - event.user.send().message( - "${message.sender} wanted me to tell you: ${message.message.reverseColor()}" - ) - message.isReceived = true - save() - } - } else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived - && !message.isNotified - ) { - event.user.send().message( - "Your message ${"[ID ${message.id}]".reverseColor()} was sent to " - + "${message.recipient.bold()} on ${message.receptionDate}" - ) - message.isNotified = true - save() - } - } - } - } - - /** - * Returns the messages queue size. - * - * @return The size. - */ - fun size(): Int = messages.size - - // View all messages. - private fun viewAll(event: GenericMessageEvent) { - if (messages.isNotEmpty()) { - for (message in messages) { - event.sendMessage( - "${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " + - (if (message.isReceived) "DELIVERED]" else "QUEUED]") - ) - } - } else { - event.sendMessage("There are no messages in the queue.") - } - } - - // View messages. - private fun viewMessages(event: GenericMessageEvent) { - var hasMessage = false - for (message in messages.filter { it.isMatch(event.user.nick) }) { - if (!hasMessage) { - hasMessage = true - event.sendMessage("Here are your messages: ") - } - if (message.isReceived) { - event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]" - ) - } else { - event.sendMessage( - message.sender.bold() + ARROW + message.recipient.bold() + - " [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]" - ) - } - event.sendMessage(helpFormat(message.message)) - } - if (!hasMessage) { - event.sendMessage("You have no messages in the queue.") - } else { - event.sendMessage("To delete one or all delivered messages:") - event.sendMessage( - helpFormat( - helpCmdSyntax("%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>", event.bot().nick, true) - ) - ) - event.sendMessage(help.last()) - } - } - - companion object { - /** - * Max days property. - */ - const val MAX_DAYS_PROP = "tell-max-days" - - /** - * Max size property. - */ - const val MAX_SIZE_PROP = "tell-max-size" - - // Arrow - private const val ARROW = " --> " - - // All keyword - private const val TELL_ALL_KEYWORD = "all" - - //T he delete command. - private const val TELL_DEL_KEYWORD = "del" - } - - /** - * Creates a new instance. - */ - init { - initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP) - - // Load the message queue - messages.addAll(TellManager.load(serialObject)) - if (clean()) { - save() - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt deleted file mode 100644 index b65a4da..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * TellManager.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.tell - -import net.thauvin.erik.mobibot.Utils.loadSerialData -import net.thauvin.erik.mobibot.Utils.saveSerialData -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.time.Clock -import java.time.LocalDateTime - -/** - * The Tell Messages Manager. - */ -object TellManager { - private val logger: Logger = LoggerFactory.getLogger(TellManager::class.java) - - /** - * Cleans the messages queue. - */ - @JvmStatic - fun clean(tellMessages: MutableList<TellMessage>, tellMaxDays: Long): Boolean { - if (logger.isDebugEnabled) logger.debug("Cleaning the messages.") - val today = LocalDateTime.now(Clock.systemUTC()) - return tellMessages.removeIf { o: TellMessage -> o.queued.plusDays(tellMaxDays).isBefore(today) } - } - - /** - * Loads the messages. - */ - @JvmStatic - fun load(file: String): List<TellMessage> { - @Suppress("UNCHECKED_CAST") - return loadSerialData(file, emptyList<TellMessage>(), logger, "message queue") as List<TellMessage> - } - - /** - * Saves the messages. - */ - @JvmStatic - fun save(file: String, messages: List<TellMessage?>?) { - if (messages != null) { - saveSerialData(file, messages, logger, "messages") - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt deleted file mode 100644 index d17fbb5..0000000 --- a/bin/main/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * TellMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.tell - -import java.io.Serializable -import java.time.Clock -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter - -/** - * Tell Message. - */ -class TellMessage( - /** - * Returns the message's sender. - */ - val sender: String, - - /** - * Returns the message's recipient. - */ - val recipient: String, - - /** - * Returns the message text. - */ - val message: String -) : Serializable { - /** - * Returns the queued date/time. - */ - var queued: LocalDateTime = LocalDateTime.now(Clock.systemUTC()) - - /** - * Returns the message id. - */ - var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) - - /** - * Returns `true` if a notification was sent. - */ - var isNotified = false - - /** - * Returns `true` if the message was received. - */ - var isReceived = false - set(value) { - if (value) { - receptionDate = LocalDateTime.now(Clock.systemUTC()) - } - field = value - } - - /** - * Returns the message creating date. - */ - var receptionDate: LocalDateTime = LocalDateTime.MIN - - /** - * Matches the message sender or recipient. - */ - fun isMatch(nick: String?): Boolean { - return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true) - } - - override fun toString(): String { - return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " + - "queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}") - } - - companion object { - @Suppress("ConstPropertyName") - private const val serialVersionUID = 2L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt b/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt deleted file mode 100644 index e8676ec..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/Entries.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Entries.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 net.thauvin.erik.mobibot.Utils.today - -class Entries( - var channel: String = "", - var ircServer: String = "", - var logsDir: String = "", - var backlogs: String = "" -) { - val links = mutableListOf<EntryLink>() - - var lastPubDate = today() - - fun load() { - lastPubDate = FeedsManager.loadFeed(this) - } - - fun save() { - lastPubDate = today() - FeedsManager.saveFeed(this) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt deleted file mode 100644 index 9c09626..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * EntriesUtils.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.green - -/** - * Entries utilities. - */ -object EntriesUtils { - /** - * Prints an entry's comment for display on the channel. - */ - @JvmStatic - fun printComment(entryIndex: Int, commentIndex: Int, comment: EntryComment): String = - ("${entryIndex.toLinkLabel()}.${commentIndex + 1}: [${comment.nick}] ${comment.comment}") - - /** - * Prints an entry's link for display on the channel. - */ - @JvmStatic - @JvmOverloads - fun printLink(entryIndex: Int, entry: EntryLink, isView: Boolean = false): String { - val buff = StringBuilder().append(entryIndex.toLinkLabel()).append(": ") - .append('[').append(entry.nick).append(']') - if (isView && entry.comments.isNotEmpty()) { - buff.append("[+").append(entry.comments.size).append(']') - } - buff.append(' ') - with(entry) { - if (Constants.NO_TITLE == title) { - buff.append(title) - } else { - buff.append(title.bold()) - } - buff.append(" ( ").append(link.green()).append(" )") - } - return buff.toString() - } - - /** - * Prints an entry's tags/categories for display on the channel. e.g. L1T: tag1, tag2 - */ - @JvmStatic - fun printTags(entryIndex: Int, entry: EntryLink): String = - entryIndex.toLinkLabel() + "${Constants.TAG_CMD}: " + entry.formatTags(", ") - - /** - * Builds link label based on its index. e.g: L1 - */ - @JvmStatic - fun Int.toLinkLabel(): String = Constants.LINK_CMD + (this + 1) -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt deleted file mode 100644 index e18d692..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * EntryComment.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 java.io.Serializable -import java.time.LocalDateTime - -/** - * Entry comments data class. - */ -data class EntryComment(var comment: String, var nick: String) : Serializable { - /** - * Creation date. - */ - val date: LocalDateTime = LocalDateTime.now() - - override fun toString(): String = "EntryComment{comment='$comment', date=$date, nick='$nick'}" - - companion object { - // Serial version UID - @Suppress("ConstPropertyName") - private const val serialVersionUID: Long = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt b/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt deleted file mode 100644 index 4a69446..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ /dev/null @@ -1,213 +0,0 @@ -/* - * EntryLink.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.SyndCategory -import com.rometools.rome.feed.synd.SyndCategoryImpl -import net.thauvin.erik.mobibot.commands.links.LinksManager -import java.io.Serializable -import java.util.* - -/** - * The class used to store link entries. - */ -class EntryLink( - // Link's comments - val comments: MutableList<EntryComment> = mutableListOf(), - - // Tags/categories - val tags: MutableList<SyndCategory> = mutableListOf(), - - // Channel - var channel: String, - - // Creation date - var date: Date = Calendar.getInstance().time, - - // Link's URL - var link: String, - - // Author's login - var login: String = "", - - // Author's nickname - var nick: String, - - // Link's title - var title: String -) : Serializable { - /** - * Creates a new entry. - */ - constructor( - link: String, - title: String, - nick: String, - login: String, - channel: String, - tags: List<String?> - ) : this(link = link, title = title, nick = nick, login = login, channel = channel) { - setTags(tags) - } - - /** - * Creates a new entry. - */ - constructor( - link: String, - title: String, - nick: String, - channel: String, - date: Date, - tags: List<SyndCategory> - ) : this(link = link, title = title, nick = nick, channel = channel, date = Date(date.time)) { - this.tags.addAll(tags) - } - - /** - * Adds a new comment - */ - fun addComment(comment: EntryComment): Int { - comments.add(comment) - return comments.lastIndex - } - - /** - * Adds a new comment. - */ - fun addComment(comment: String, nick: String): Int { - return addComment(EntryComment(comment, nick)) - } - - /** - * Deletes a specific comment. - */ - 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) - } - - /** - * Formats the tags. - */ - fun formatTags(sep: String, prefix: String = ""): String { - return tags.joinToString(separator = sep, prefix = prefix) { it.name } - } - - /** - * Returns a comment. - */ - fun getComment(index: Int): EntryComment = comments[index] - - /** - * Returns true if a string is contained in the link, title, or nick. - */ - fun matches(match: String?): Boolean { - return if (match.isNullOrEmpty()) { - false - } else { - link.contains(match, true) || title.contains(match, true) || nick.contains(match, true) - } - } - - /** - * Sets a comment. - */ - fun setComment(index: Int, comment: String?, nick: String?) { - if (index < comments.size && !comment.isNullOrBlank() && !nick.isNullOrBlank()) { - comments[index] = EntryComment(comment, nick) - } - } - - /** - * Sets the tags. - */ - fun setTags(tags: String) { - setTags(tags.split(LinksManager.TAG_MATCH)) - } - - /** - * Sets the tags. - */ - private fun setTags(tags: List<String?>) { - if (tags.isNotEmpty()) { - var category: SyndCategoryImpl - for (tag in tags) { - if (!tag.isNullOrBlank()) { - val t = tag.lowercase() - val mod = t[0] - if (mod == '-') { - // Don't remove the channel tag - if (channel.substring(1) != t.substring(1)) { - category = SyndCategoryImpl() - category.name = t.substring(1) - this.tags.remove(category) - } - } else { - category = SyndCategoryImpl() - if (mod == '+') { - category.name = t.substring(1) - } else { - category.name = t - } - if (!this.tags.contains(category)) { - this.tags.add(category) - } - } - } - } - } - } - - /** - * Returns a string representation of the object. - */ - override fun toString(): String { - return ("EntryLink{channel='$channel', comments=$comments, date=$date, link='$link', login='$login'," + - "nick='$nick', tags=$tags, title='$title'}") - } - - companion object { - // Serial version UID - @Suppress("ConstPropertyName") - private const val serialVersionUID: Long = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt deleted file mode 100644 index f786cb2..0000000 --- a/bin/main/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ /dev/null @@ -1,187 +0,0 @@ -/* - * FeedsManager.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.* -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.* -import kotlin.io.path.exists - -/** - * Manages the RSS feeds. - */ -class FeedsManager private constructor() { - companion object { - private val logger: Logger = LoggerFactory.getLogger(FeedsManager::class.java) - - // The file containing the current entries. - private const val CURRENT_XML = "current.xml" - - // The .xml extension. - private const val DOT_XML = ".xml" - - /** - * Loads the current feed. - */ - @JvmStatic - @Throws(IOException::class, FeedException::class) - fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): 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. - */ - @JvmStatic - fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) { - 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.isNotEmpty()) { - 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.removePrefix("#")}@${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() + DOT_XML - ) - ), 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.") - } - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt deleted file mode 100644 index 8c8e736..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * AbstractModule.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -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 { - /** - * The module name. - */ - abstract val name: String - - /** - * The module's commands, if any. - */ - @JvmField - val commands: MutableList<String> = mutableListOf() - - @JvmField - val help: MutableList<String> = mutableListOf() - val properties: MutableMap<String, String> = mutableMapOf() - - /** - * Responds to a command. - */ - abstract fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) - - /** - * Returns the module's property keys. - */ - val propertyKeys: Set<String> - get() = properties.keys - - /** - * Returns `true` if the module has properties. - */ - fun hasProperties(): Boolean { - return properties.isNotEmpty() - } - - /** - * Responds with the module's help. - */ - open fun helpResponse(event: GenericMessageEvent): Boolean { - for (h in help) { - event.sendMessage(helpCmdSyntax(h, event.bot().nick, isPrivateMsgEnabled && event is PrivateMessageEvent)) - } - return true - } - - /** - * Initializes the properties. - */ - fun initProperties(vararg keys: String) { - for (key in keys) { - properties[key] = "" - } - } - - /** - * Returns `true` if the module is enabled. - */ - val isEnabled: Boolean - get() = if (hasProperties()) { - isValidProperties - } else { - true - } - - /** - * Returns `true` if the module responds to private messages. - */ - open val isPrivateMsgEnabled: Boolean = false - - /** - * Ensures that all properties have values. - */ - open val isValidProperties: Boolean - get() { - for (s in properties.keys) { - if (properties[s].isNullOrBlank()) { - return false - } - } - return true - } - - /** - * Sets a property key and value. - */ - fun setProperty(key: String, value: String) { - if (key.isNotBlank()) { - properties[key] = value - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt b/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt deleted file mode 100644 index b7aae28..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Calc.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Calc.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.objecthunter.exp4j.ExpressionBuilder -import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException -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 : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Calc::class.java) - - override val name = "Calc" - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - event.respond(calculate(args)) - } catch (e: IllegalArgumentException) { - if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e) - event.respond("No idea. This is the kind of math I don't get.") - } catch (e: UnknownFunctionOrVariableException) { - if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e) - event.respond("No idea. I must've some form of Dyscalculia.") - } - } else { - helpResponse(event) - } - } - - companion object { - // Calc command - private const val CALC_CMD = "calc" - - /** - * Performs a calculation. e.g.: 1 + 1 * 2 - */ - @JvmStatic - @Throws(IllegalArgumentException::class) - fun calculate(query: String): String { - val decimalFormat = DecimalFormat("#.##") - val calc = ExpressionBuilder(query).build() - return query.replace(" ", "") + " = " + decimalFormat.format(calc.evaluate()).bold() - } - } - - init { - commands.add(CALC_CMD) - help.add("To solve a mathematical calculation:") - help.add(helpFormat("%c $CALC_CMD <calculation>")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt deleted file mode 100644 index bd92332..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ /dev/null @@ -1,176 +0,0 @@ -/* - * ChatGpt.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.Utils.sendMessage -import org.apache.commons.text.WordUtils -import org.json.JSONException -import org.json.JSONObject -import org.json.JSONWriter -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException -import java.net.URI -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse - -class ChatGpt : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) - - override val name = CHATGPT_NAME - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val answer = chat( - args.trim(), properties[API_KEY_PROP], - properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() - ) - if (answer.isNotBlank()) { - event.sendMessage(WordUtils.wrap(answer, 400)) - } else { - event.respond("$name is stumped.") - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } catch (e: NumberFormatException) { - if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e) - event.respond("The $name module is misconfigured.") - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The service name. - */ - const val CHATGPT_NAME = "ChatGPT" - - /** - * The API Key property. - */ - const val API_KEY_PROP = "chatgpt-api-key" - - /** - * The max tokens property. - */ - const val MAX_TOKENS_PROP = "chatgpt-max-tokens" - - // ChatGPT API URL - private const val API_URL = "https://api.openai.com/v1/completions" - - // ChatGPT command - private const val CHATGPT_CMD = "chatgpt" - - - @JvmStatic - @Throws(ModuleException::class) - fun chat(query: String, apiKey: String?, maxTokens: Int): String { - if (!apiKey.isNullOrEmpty()) { - val prompt = JSONWriter.valueToString("Q:$query\nA:") - val request = HttpRequest.newBuilder() - .uri(URI.create(API_URL)) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .header("User-Agent", Constants.USER_AGENT) - .POST( - HttpRequest.BodyPublishers.ofString( - """{ - "model": "text-davinci-003", - "prompt": $prompt, - "temperature": 0, - "max_tokens": $maxTokens, - "top_p": 1, - "frequency_penalty": 0, - "presence_penalty": 0 - }""".trimIndent() - ) - ) - .build() - try { - val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) - if (response.statusCode() == 200) { - try { - val jsonResponse = JSONObject(response.body()) - val choices = jsonResponse.getJSONArray("choices") - return choices.getJSONObject(0).getString("text").trim() - } catch (e: JSONException) { - throw ModuleException( - "$CHATGPT_CMD($query): JSON", - "A JSON error has occurred while conversing with $CHATGPT_NAME.", - e - ) - } - } else { - if (response.statusCode() == 429) { - throw ModuleException( - "$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later." - ) - } else { - throw IOException("HTTP Status Code: " + response.statusCode()) - } - } - } catch (e: IOException) { - throw ModuleException( - "$CHATGPT_CMD($query): IO", - "An IO error has occurred while conversing with $CHATGPT_NAME.", - e - ) - } - } else { - throw ModuleException("$CHATGPT_CMD($query)", "No $CHATGPT_NAME API key specified.") - } - } - } - - init { - commands.add(CHATGPT_CMD) - with(help) { - add("To get answers from $name:") - add(Utils.helpFormat("%c $CHATGPT_CMD <query>")) - add("For example:") - add(Utils.helpFormat("%c $CHATGPT_CMD explain quantum computing in simple terms")) - add(Utils.helpFormat("%c $CHATGPT_CMD how do I make an HTTP request in Javascript?")) - } - initProperties(API_KEY_PROP, MAX_TOKENS_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt deleted file mode 100644 index d14056e..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ /dev/null @@ -1,159 +0,0 @@ -/* - * CryptoPrices.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.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.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.sendList -import net.thauvin.erik.mobibot.Utils.sendMessage -import org.json.JSONObject -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException - -/** - * The Cryptocurrency Prices module. - */ -class CryptoPrices : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(CryptoPrices::class.java) - - override val name = "CryptoPrices" - - /** - * Returns the cryptocurrency market price from - * [Coinbase](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices#get-spot-price). - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (CURRENCIES.isEmpty()) { - try { - loadCurrencies() - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - } - } - - val debugMessage = "crypto($cmd $args)" - if (args == CODES_KEYWORD) { - event.sendMessage("The supported currencies are:") - event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true) - } else 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 - } - event.respond("${price.base} current price is $amount [${CURRENCIES[price.currency]}]") - } catch (e: CryptoException) { - if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e) - e.message?.let { - event.respond(it) - } - } catch (e: IOException) { - if (logger.isErrorEnabled) logger.error(debugMessage, e) - event.respond("An IO error has occurred while retrieving the cryptocurrency market price.") - } - } else { - helpResponse(event) - } - - } - - companion object { - // Crypto command - private const val CRYPTO_CMD = "crypto" - - // Fiat Currencies - private val CURRENCIES: MutableMap<String, String> = mutableMapOf() - - // Currency codes keyword - private const val CODES_KEYWORD = "codes" - - /** - * Get current market price. - */ - @JvmStatic - fun currentPrice(args: List<String>): CryptoPrice { - return if (args.size == 2) - spotPrice(args[0], args[1]) - else - spotPrice(args[0]) - } - - /** - * For testing purposes. - */ - fun getCurrencyName(code: String): String? { - return CURRENCIES[code] - } - - /** - * Loads the Fiat currencies.. - */ - @JvmStatic - @Throws(ModuleException::class) - fun loadCurrencies() { - try { - val json = JSONObject(CryptoPrice.apiCall(listOf("currencies"))) - val data = json.getJSONArray("data") - for (i in 0 until data.length()) { - val d = data.getJSONObject(i) - CURRENCIES[d.getString("id")] = d.getString("name") - } - } catch (e: CryptoException) { - throw ModuleException( - "loadCurrencies(): CE", - "An error has occurred while retrieving the currencies table.", - e - ) - } - } - } - - init { - commands.add(CRYPTO_CMD) - with(help) { - add("To retrieve a cryptocurrency's market price:") - add(helpFormat("%c $CRYPTO_CMD <symbol> [<currency>]")) - add("For example:") - add(helpFormat("%c $CRYPTO_CMD BTC")) - add(helpFormat("%c $CRYPTO_CMD ETH EUR")) - add(helpFormat("%c $CRYPTO_CMD ETH2 GPB")) - add("To list the supported currencies:") - add(helpFormat("%c $CRYPTO_CMD $CODES_KEYWORD")) - } - loadCurrencies() - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt deleted file mode 100644 index da0efd8..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ /dev/null @@ -1,222 +0,0 @@ -/* - * CurrencyConverter.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.Utils.sendList -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.PublicMessage -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 -import java.text.DecimalFormat -import java.util.* - - -/** - * The CurrencyConverter module. - */ -class CurrencyConverter : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java) - - override val name = "CurrencyConverter" - - // Reload currency codes - private fun reload(apiKey: String?) { - if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) { - try { - loadSymbols(apiKey) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - } - } - } - - /** - * Converts the specified currencies. - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - reload(properties[API_KEY_PROP]) - - when { - SYMBOLS.isEmpty() -> { - event.respond(EMPTY_SYMBOLS_TABLE) - } - - args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> { - val msg = convertCurrency(properties[API_KEY_PROP], args) - event.respond(msg.msg) - if (msg.isError) { - helpResponse(event) - } - } - - args.contains(CODES_KEYWORD) -> { - event.sendMessage("The supported currency codes are:") - event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true) - } - - else -> { - helpResponse(event) - } - } - } - - override fun helpResponse(event: GenericMessageEvent): Boolean { - reload(properties[API_KEY_PROP]) - - if (SYMBOLS.isEmpty()) { - event.sendMessage(EMPTY_SYMBOLS_TABLE) - } else { - val nick = event.bot().nick - event.sendMessage("To convert from one currency to another:") - event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) - event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to USD", nick, isPrivateMsgEnabled) - ) - ) - event.sendMessage("To list the supported currency codes:") - event.sendMessage( - helpFormat( - helpCmdSyntax("%c $CURRENCY_CMD $CODES_KEYWORD", nick, isPrivateMsgEnabled) - ) - ) - } - return true - } - - companion object { - /** - * The API Key property. - */ - const val API_KEY_PROP = "exchangerate-api-key" - - // Currency command - private const val CURRENCY_CMD = "currency" - - // Currency codes keyword - private const val CODES_KEYWORD = "codes" - - // Empty symbols table. - private const val EMPTY_SYMBOLS_TABLE = "Sorry, but the currency table is empty." - - // Currency symbols - private val SYMBOLS: TreeMap<String, String> = TreeMap() - - // Decimal format - private val DECIMAL_FORMAT = DecimalFormat("0.00#") - - /** - * Converts from a currency to another. - */ - @JvmStatic - fun convertCurrency(apiKey: String?, query: String): Message { - if (apiKey.isNullOrEmpty()) { - throw ModuleException("${CURRENCY_CMD}($query)", "No Exchange Rate API key specified.") - } - - val cmds = query.split(" ") - return if (cmds.size == 4) { - if (cmds[3] == cmds[1] || "0" == cmds[0]) { - PublicMessage("You're kidding, right?") - } else { - val to = cmds[1].uppercase() - val from = cmds[3].uppercase() - if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) { - try { - val amt = cmds[0].replace(",", "") - val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/pair/$to/$from/$amt") - val body = url.reader().body - val json = JSONObject(body) - - if (json.getString("result") == "success") { - val result = DECIMAL_FORMAT.format(json.getDouble("conversion_result")) - PublicMessage( - "${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}" - ) - } else { - ErrorMessage("Sorry, an error occurred while converting the currencies.") - } - } catch (ignore: IOException) { - ErrorMessage("Sorry, an IO error occurred while converting the currencies.") - } - } else { - ErrorMessage("Sounds like monopoly money to me!") - } - } - } else { - ErrorMessage("Invalid query. Let's try again.") - } - } - - /** - * Loads the currency ISO symbols. - */ - @JvmStatic - @Throws(ModuleException::class) - fun loadSymbols(apiKey: String?) { - if (!apiKey.isNullOrEmpty()) { - try { - val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/codes") - val json = JSONObject(url.reader().body) - if (json.getString("result") == "success") { - val codes = json.getJSONArray("supported_codes") - for (i in 0 until codes.length()) { - val code = codes.getJSONArray(i) - SYMBOLS[code.getString(0)] = code.getString(1) - } - } - } catch (e: IOException) { - throw ModuleException( - "loadCodes(): IOE", - "An IO error has occurred while retrieving the currencies.", - e - ) - } - } - } - } - - init { - commands.add(CURRENCY_CMD) - initProperties(API_KEY_PROP) - loadSymbols(properties[ChatGpt.API_KEY_PROP]) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt b/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt deleted file mode 100644 index 8420fb1..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Dice.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Dice.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent - -/** - * The Dice module. - */ -class Dice : AbstractModule() { - override val name = "Dice" - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - val arg = if (args.isBlank()) "2d6" else args.trim() - val match = Regex("^([1-9]|[12]\\d|3[0-2])[dD]([1-9]|[12]\\d|3[0-2])$").find(arg) - if (match != null) { - val (dice, sides) = match.destructured - event.respond("you rolled " + roll(dice.toInt(), sides.toInt())) - } else { - helpResponse(event) - } - } - - companion object { - // Dice command - private const val DICE_CMD = "dice" - - @JvmStatic - fun roll(dice: Int, sides: Int): String { - val result = StringBuilder() - var total = 0 - - repeat(dice) { - val roll = (1..sides).random() - total += roll - - if (result.isNotEmpty()) { - result.append(" + ") - } - - result.append(roll.bold()) - } - - if (dice != 1) { - result.append(" = ${total.bold()}") - } - - return result.toString() - } - } - - init { - commands.add(DICE_CMD) - help.add("To roll 2 dice with 6 sides:") - help.add(helpFormat("%c $DICE_CMD [2d6]")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt deleted file mode 100644 index f426d1e..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ /dev/null @@ -1,162 +0,0 @@ -/* - * GoogleSearch.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.ReleaseInfo -import net.thauvin.erik.mobibot.Utils.capitalise -import net.thauvin.erik.mobibot.Utils.colorize -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.unescapeXml -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.NoticeMessage -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 : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java) - - override val name = "GoogleSearch" - - /** - * Searches Google. - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val results = searchGoogle( - args, - properties[API_KEY_PROP], - properties[CSE_KEY_PROP], - event.user.nick - ) - for (msg in results) { - if (msg.isError) { - event.respond(msg.msg.colorize(msg.color)) - } else { - event.sendMessage(channel, msg) - } - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } else { - helpResponse(event) - } - } - - companion object { - // Google API Key property - const val API_KEY_PROP = "google-api-key" - - // Google Custom Search Engine ID property - const val CSE_KEY_PROP = "google-cse-cx" - - // Google command - private const val GOOGLE_CMD = "google" - - /** - * Performs a search on Google. - */ - @JvmStatic - @Throws(ModuleException::class) - fun searchGoogle( - query: String, - apiKey: String?, - cseKey: String?, - quotaUser: String = ReleaseInfo.PROJECT - ): List<Message> { - if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) { - throw ModuleException( - "${GoogleSearch::class.java.name} is disabled.", - "${GOOGLE_CMD.capitalise()} is disabled. The API keys are missing." - ) - } - val results = mutableListOf<Message>() - if (query.isNotBlank()) { - try { - val url = URL( - "https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" + - ""aUser=${quotaUser}&q=${query.encodeUrl()}&filter=1&num=5&alt=json" - ) - val json = JSONObject(url.reader().body) - if (json.has("items")) { - val ja = json.getJSONArray("items") - for (i in 0 until ja.length()) { - val j = ja.getJSONObject(i) - results.add(NoticeMessage(j.getString("title").unescapeXml())) - results.add(NoticeMessage(helpFormat(j.getString("link"), false), Colors.DARK_GREEN)) - } - } else if (json.has("error")) { - val error = json.getJSONObject("error") - val message = error.getString("message") - throw ModuleException("searchGoogle($query): ${error.getInt("code")} : $message", message) - } else { - results.add(ErrorMessage("No results found.", Colors.RED)) - } - } catch (e: IOException) { - throw ModuleException("searchGoogle($query): IOE", "An IO error has occurred searching Google.", e) - } catch (e: JSONException) { - throw ModuleException( - "searchGoogle($query): JSON", - "A JSON error has occurred searching Google.", - e - ) - } - } else { - results.add(ErrorMessage("Invalid query. Please try again.")) - } - return results - } - } - - init { - commands.add(GOOGLE_CMD) - help.add("To search Google:") - help.add(helpFormat("%c $GOOGLE_CMD <query>")) - initProperties(API_KEY_PROP, CSE_KEY_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt b/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt deleted file mode 100644 index 2760fa7..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Joke.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Joke.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.jokeapi.exceptions.HttpErrorException -import net.thauvin.erik.jokeapi.exceptions.JokeException -import net.thauvin.erik.jokeapi.joke -import net.thauvin.erik.jokeapi.models.Type -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.colorize -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage -import org.json.JSONException -import org.pircbotx.Colors -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.IOException - -/** - * The Joke module. - */ -class Joke : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Joke::class.java) - - override val name = "Joke" - - /** - * Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/). - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - with(event.bot()) { - try { - randomJoke().forEach { - sendIRC().notice(channel, it.msg.colorize(it.color)) - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } - } - - companion object { - // Joke command - private const val JOKE_CMD = "joke" - - /** - * Retrieves a random joke. - */ - @JvmStatic - @Throws(ModuleException::class) - fun randomJoke(): List<Message> { - return try { - val joke = joke(safe = true, type = Type.SINGLE, splitNewLine = true) - joke.joke.map { PublicMessage(it, Colors.CYAN) } - } catch (e: JokeException) { - throw ModuleException("randomJoke(): ${e.additionalInfo}", e.message, e) - } catch (e: HttpErrorException) { - throw ModuleException("randomJoke(): HTTP: ${e.statusCode}", e.message, e) - } catch (e: IOException) { - throw ModuleException("randomJoke(): IOE", "An IO error has occurred retrieving a random joke.", e) - } catch (e: JSONException) { - throw ModuleException("randomJoke(): JSON", "A parsing error has occurred retrieving a random joke.", e) - } - } - } - - init { - commands.add(JOKE_CMD) - help.add("To display a random joke:") - help.add(helpFormat("%c $JOKE_CMD")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt b/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt deleted file mode 100644 index 9ab2ead..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Lookup.kt +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Lookup.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Constants -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 - -/** - * The Lookup module. - */ -class Lookup : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Lookup::class.java) - - override val name = "Lookup" - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.matches("(\\S.)+(\\S)+".toRegex())) { - try { - event.respondWith(nslookup(args).prependIndent()) - } catch (ignore: UnknownHostException) { - if (args.matches( - ("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)") - .toRegex() - ) - ) { - try { - val lines = whois(args) - if (lines.isNotEmpty()) { - var line: String - var hasData = false - for (rawLine in lines) { - line = rawLine.trim() - if (line.matches("^\\b(?!\\b[Cc]omment\\b)\\w+\\b: .*$".toRegex())) { - if (!hasData) { - event.respondWith(line) - hasData = true - } else { - event.bot().sendIRC().notice(event.user.nick, line) - } - } - } - } else { - event.respond("Unknown host.") - } - } catch (ioe: IOException) { - if (logger.isWarnEnabled) { - logger.warn("Unable to perform whois IP lookup: $args", ioe) - } - event.respond("Unable to perform whois IP lookup: ${ioe.message}") - } - } else { - event.respond("Unknown host.") - } - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The whois default host. - */ - const val WHOIS_HOST = "whois.arin.net" - - // Lookup command - private const val LOOKUP_CMD = "lookup" - - /** - * Performs a DNS lookup on the specified query. - */ - @JvmStatic - @Throws(UnknownHostException::class) - fun nslookup(query: String): String { - val buffer = StringBuilder() - val results = InetAddress.getAllByName(query) - var hostInfo: String - for (result in results) { - if (result.hostAddress == query) { - hostInfo = result.hostName - if (hostInfo == query) { - throw UnknownHostException() - } - } else { - hostInfo = result.hostAddress - } - if (buffer.isNotEmpty()) { - buffer.append(", ") - } - buffer.append(hostInfo) - } - return buffer.toString() - } - - /** - * Performs a whois IP query. - */ - @Throws(IOException::class) - private fun whois(query: String): List<String> { - return whois(query, WHOIS_HOST) - } - - /** - * Performs a whois IP query. - */ - @JvmStatic - @Throws(IOException::class) - fun whois(query: String, host: String): List<String> { - val whoisClient = WhoisClient() - val lines: List<String> - with(whoisClient) { - try { - defaultTimeout = Constants.CONNECT_TIMEOUT - connect(host) - soTimeout = Constants.CONNECT_TIMEOUT - setSoLinger(false, 0) - lines = if (WHOIS_HOST == host) { - query("n - $query").split("\n") - } else { - query(query).split("\n") - } - } finally { - disconnect() - } - } - return lines - } - } - - init { - commands.add(LOOKUP_CMD) - help.add("To perform a DNS lookup query:") - help.add(helpFormat("%c $LOOKUP_CMD <ip address or hostname>")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt b/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt deleted file mode 100644 index 3be3a5f..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Mastodon.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.Utils.prefixIfMissing -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.social.SocialModule -import org.json.JSONException -import org.json.JSONObject -import org.json.JSONWriter -import java.io.IOException -import java.net.URI -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse - -class Mastodon : SocialModule() { - override val name = "Mastodon" - - override val handle: String? - get() = properties[HANDLE_PROP] - - override val isAutoPost: Boolean - get() = isEnabled && properties[AUTO_POST_PROP].toBoolean() - - override val isValidProperties: Boolean - get() = !(properties[INSTANCE_PROP].isNullOrBlank() || properties[ACCESS_TOKEN_PROP].isNullOrBlank()) - - /** - * Formats the entry for posting. - */ - override fun formatEntry(entry: EntryLink): String { - return "${entry.title} (via ${entry.nick} on ${entry.channel})${formatTags(entry)}\n\n${entry.link}" - } - - private fun formatTags(entry: EntryLink): String { - return entry.tags.filter { !it.name.equals(entry.channel.removePrefix("#"), true) } - .joinToString(separator = " ", prefix = "\n\n") { "#${it.name}" } - } - - /** - * Posts on Mastodon. - */ - @Throws(ModuleException::class) - override fun post(message: String, isDm: Boolean): String { - return toot( - apiKey = properties[ACCESS_TOKEN_PROP], - instance = properties[INSTANCE_PROP], - handle = handle, - message = message, - isDm = isDm - ) - } - - companion object { - // Property keys - const val ACCESS_TOKEN_PROP = "mastodon-access-token" - const val AUTO_POST_PROP = "mastodon-auto-post" - const val HANDLE_PROP = "mastodon-handle" - const val INSTANCE_PROP = "mastodon-instance" - - private const val MASTODON_CMD = "mastodon" - private const val TOOT_CMD = "toot" - - /** - * Post on Mastodon. - */ - @JvmStatic - @Throws(ModuleException::class) - fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String { - val request = HttpRequest.newBuilder() - .uri(URI.create("https://$instance/api/v1/statuses")) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .POST( - HttpRequest.BodyPublishers.ofString( - JSONWriter.valueToString( - if (isDm) { - mapOf("status" to "${handle?.prefixIfMissing('@')} $message", "visibility" to "direct") - } else { - mapOf("status" to message) - } - ) - ) - ) - .build() - try { - val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) - if (response.statusCode() == 200) { - return try { - val jsonResponse = JSONObject(response.body()) - if (isDm) { - jsonResponse.getString("content") - } else { - "Your message was posted to ${jsonResponse.getString("url")}" - } - } catch (e: JSONException) { - throw ModuleException("mastodonPost($message)", "A JSON error has occurred: ${e.message}", e) - } - } else { - throw IOException("Status Code: " + response.statusCode()) - } - } catch (e: IOException) { - throw ModuleException("mastodonPost($message)", "An IO error has occurred: ${e.message}", e) - } catch (e: InterruptedException) { - throw ModuleException("mastodonPost($message)", "An error has occurred: ${e.message}", e) - } - } - } - - init { - commands.add(MASTODON_CMD) - commands.add(TOOT_CMD) - help.add("To toot on Mastodon:") - help.add(Utils.helpFormat("%c $TOOT_CMD <message>")) - properties[AUTO_POST_PROP] = "false" - initProperties(ACCESS_TOKEN_PROP, HANDLE_PROP, INSTANCE_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt b/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt deleted file mode 100644 index a7416c2..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ModuleException.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -/** - * The `ModuleException` class. - */ -class ModuleException @JvmOverloads constructor( - val debugMessage: String, - message: String? = null, - cause: Throwable? = null -) : Exception(message, cause) { - companion object { - @Suppress("ConstPropertyName") - private const val serialVersionUID = 1L - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt b/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt deleted file mode 100644 index 944dbc1..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Ping.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Ping.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils.bot -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent - -/** - * The Ping module. - */ -class Ping : AbstractModule() { - override val name = "Ping" - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - event.bot().sendIRC().action(channel, randomPing()) - } - - companion object { - /** - * The ping responses. - */ - @JvmField - val PINGS = listOf( - "is barely alive.", - "is trying to stay awake.", - "has gone fishing.", - "is somewhere over the rainbow.", - "has fallen and can't get up.", - "is running. You better go chase it.", - "has just spontaneously combusted.", - "is talking to itself... don't interrupt. That's rude.", - "is bartending at an AA meeting.", - "is hibernating.", - "is saving energy: apathetic mode activated.", - "is busy. Go away!" - ) - - @JvmStatic - fun randomPing(): String { - return PINGS[PINGS.indices.random()] - } - - /** - * The ping command. - */ - private const val PING_CMD = "ping" - } - - init { - commands.add(PING_CMD) - help.add("To ping the bot:") - help.add(helpFormat("%c $PING_CMD")) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt deleted file mode 100644 index a299d8d..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * RockPaperScissors.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.helpFormat -import org.pircbotx.hooks.types.GenericMessageEvent - - -/** - * Simple module example in Kotlin. - */ -class RockPaperScissors : AbstractModule() { - override val name = "RockPaperScissors" - - init { - with(commands) { - add(Hands.ROCK.name.lowercase()) - add(Hands.PAPER.name.lowercase()) - add(Hands.SCISSORS.name.lowercase()) - } - - with(help) { - add("To play Rock Paper Scissors:") - add( - helpFormat( - "%c ${Hands.ROCK.name.lowercase()} | ${Hands.PAPER.name.lowercase()}" - + " | ${Hands.SCISSORS.name.lowercase()}" - ) - ) - } - } - - enum class Hands(val action: String) { - ROCK("crushes") { - override fun beats(hand: Hands): Boolean { - return hand == SCISSORS - } - }, - PAPER("covers") { - override fun beats(hand: Hands): Boolean { - return hand == ROCK - } - }, - SCISSORS("cuts") { - override fun beats(hand: Hands): Boolean { - return hand == PAPER - } - }; - - abstract fun beats(hand: Hands): Boolean - } - - companion object { - // For testing. - fun winLoseOrDraw(player: String, bot: String): String { - val hand = Hands.valueOf(player.uppercase()) - val botHand = Hands.valueOf(bot.uppercase()) - - return when { - hand == botHand -> "draw" - hand.beats(botHand) -> "win" - else -> "lose" - } - } - } - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - val hand = Hands.valueOf(cmd.uppercase()) - val botHand = Hands.entries[(0..Hands.entries.size).random()] - when { - hand == botHand -> { - event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.") - } - - hand.beats(botHand) -> { - event.respond("${hand.name.bold()} ${hand.action} ${botHand.name} » You ${"win".bold()}!") - } - - else -> { - event.respond("${botHand.name.bold()} ${botHand.action} ${hand.name} » You ${"lose".bold()}!") - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt b/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt deleted file mode 100644 index dcae5e7..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ /dev/null @@ -1,236 +0,0 @@ -/* - * StockQuote.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -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.reader -import net.thauvin.erik.mobibot.Utils.sendMessage -import net.thauvin.erik.mobibot.Utils.unescapeXml -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.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 : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java) - - override val name = "StockQuote" - - /** - * Returns the specified stock quote from Alpha Vantage. - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val messages = getQuote(args, properties[API_KEY_PROP]) - for (msg in messages) { - event.sendMessage(channel, msg) - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The API property key. - */ - const val API_KEY_PROP = "alphavantage-api-key" - - /** - * The Invalid Symbol error string. - */ - const val INVALID_SYMBOL = "Invalid symbol." - - // API URL - private const val API_URL = "https://www.alphavantage.co/query?function=" - - // Quote command - private const val STOCK_CMD = "stock" - - @Throws(ModuleException::class) - private fun getJsonResponse(response: String, debugMessage: String): JSONObject { - return if (response.isNotBlank()) { - val json = JSONObject(response) - try { - val info = json.getString("Information") - if (info.isNotEmpty()) { - throw ModuleException(debugMessage, info.unescapeXml()) - } - } catch (ignore: JSONException) { - // Do nothing - } - try { - var error = json.getString("Note") - if (error.isNotEmpty()) { - throw ModuleException(debugMessage, error.unescapeXml()) - } - error = json.getString("Error Message") - if (error.isNotEmpty()) { - throw ModuleException(debugMessage, error.unescapeXml()) - } - } catch (ignore: JSONException) { - // Do nothing - } - json - } else { - throw ModuleException(debugMessage, "Empty Response.") - } - } - - /** - * Retrieves a stock quote. - */ - @JvmStatic - @Throws(ModuleException::class) - fun getQuote(symbol: String, apiKey: String?): List<Message> { - if (apiKey.isNullOrBlank()) { - throw ModuleException( - "${StockQuote::class.java.name} is disabled.", - "${STOCK_CMD.capitalise()} is disabled. The API key is missing." - ) - } - val messages = mutableListOf<Message>() - if (symbol.isNotBlank()) { - val debugMessage = "getQuote($symbol)" - var response: String - try { - with(messages) { - // Search for symbol/keywords - response = URL( - "${API_URL}SYMBOL_SEARCH&keywords=" + symbol.encodeUrl() + "&apikey=" - + apiKey.encodeUrl() - ).reader().body - var json = getJsonResponse(response, debugMessage) - val symbols = json.getJSONArray("bestMatches") - if (symbols.isEmpty) { - messages.add(ErrorMessage(INVALID_SYMBOL)) - } else { - val symbolInfo = symbols.getJSONObject(0) - - // Get quote for symbol - response = URL( - "${API_URL}GLOBAL_QUOTE&symbol=" - + symbolInfo.getString("1. symbol").encodeUrl() + "&apikey=" - + apiKey.encodeUrl() - ).reader().body - json = getJsonResponse(response, debugMessage) - val quote = json.getJSONObject("Global Quote") - if (quote.isEmpty) { - add(ErrorMessage(INVALID_SYMBOL)) - } else { - - add( - PublicMessage( - "Symbol: " + quote.getString("01. symbol").unescapeXml() - + " [" + symbolInfo.getString("2. name").unescapeXml() + ']' - ) - ) - - val pad = 10 - - add( - PublicMessage( - "Price:".padEnd(pad).prependIndent() - + quote.getString("05. price").unescapeXml() - ) - ) - add( - PublicMessage( - "Previous:".padEnd(pad).prependIndent() - + quote.getString("08. previous close").unescapeXml() - ) - ) - - 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() - + quote.getString(it.second).unescapeXml() - ) - ) - } - - add( - NoticeMessage( - "Change:".padEnd(pad).prependIndent() - + quote.getString("09. change").unescapeXml() - + " [" + quote.getString("10. change percent").unescapeXml() + ']' - ) - ) - } - } - } - } catch (e: IOException) { - throw ModuleException("$debugMessage: IOE", "An IO error has occurred retrieving a stock quote.", e) - } catch (e: NullPointerException) { - throw ModuleException("$debugMessage: NPE", "An error has occurred retrieving a stock quote.", e) - } - } else { - messages.add(ErrorMessage(INVALID_SYMBOL)) - } - return messages - } - } - - init { - commands.add(STOCK_CMD) - help.add("To retrieve a stock quote:") - help.add(helpFormat("%c $STOCK_CMD <symbol|keywords>")) - initProperties(API_KEY_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/War.class b/bin/main/net/thauvin/erik/mobibot/modules/War.class deleted file mode 100644 index 6c36e541b639c5b04b624194b3b2df6b3d11ba55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2452 zcmeHJUvm>T5MMb??6`r@5CVjMYDuZxQXf#BNMXjcP07@D7&{5m;i02DE4IR&<VrfX zdE`?u)6T$qzXso<)79MtNOEL4Q<&*PAFN&Jw7b7u?N9QbfBpUv0Pe%LB`6SBliYfC z%;Kq#9@pZT7b#!(%Ay>`AvfL=rb|#HaAuE9nHMrS@;3cF9#{gi&Cd4s0|IBCcHVv* zSS{peoj|#@(dcgWTDW2EQM=LKa!S9^Yi)K3Tuv<v4`MCszBh0R?=v}65!%iT9yeNT zG$^*(%^wLYKg|tMdeZ3s-0ZdpEaf3M)l|}0ChZOjV_t?O0yDodjqbk_QrHJLt=4uh zNK*}Y3C<H(e49sGYo*I@krZBUOU;c6r+i2Q6-`9QtWc6pwDLn9nR~P{zcuDmL=&Yg z#fsLmiq>64>sdwX+g5~z224t2sn6+w(|>E-@Qu|hu-)Xuasp7RD5G|N!YlKbK!jD! z$1&#NB*(ro|K1NL=M-@}Rzo`Cw#On~tx>g`z@XSvG>VO-YRa`1Lr#@;^}}*<X%Vc6 zCtN$kH1(0zDD#))(C21?2h6K!%iW|$<qurq0R&xc3>)$G6kUHP2)+u3S=<^GhQ@Np zcZI|b*~LsOc0&Rl^E`z7KWkQj6}YqjXW=q|bJgQyax_`&V1m6k^10q(*!hliDq!Ib z)56WO#iAVxL*Pc|pIj}-^-|Uz$nt9Kcw_>X`mwYk;u~@*jKDw*$Do9R80UIN>5;d` zt*@Do#!Kjxe)M|E_Tn&HFT)K2zno0t0~UY4o+WVYDD*9C=|k`rk2EuZQBL`fmnCP# z)nOuZfVcN=-dL$;#&b<*yc`~^y8~T2itaJf$WzPpI}poolaS{p*Y3>~_&-5MAL%+E zP#LIL2mGOM%q!!~Qg@FP(se@ycnu0;@qT+GxMn&S@0Z{<fzyYd1_BFRVGKUct_=;` z27CeW&p-)QVHW04TSYyU<xI{)C0YL@n=inrWPTbJ(fSPP1^5)b;J*M^#_=4k+)d^H zl|NwRr=<%oVD(k9?+T7{xCh{KxQgRKqF3M=P@EC?4A*DiI_hrM8p`9|zR2;`&GGgn zT9<(=L*j=(6>8}FD_p%;%$%no-W?CZ&EmEc{MR|*w%-%(Dil}H5OJ?yj!qO(fi-r? U%)mD|7N8CuN`O1~b69Tu4Ue7BiU0rr diff --git a/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt b/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt deleted file mode 100644 index 80a06fa..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/Weather2.kt +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Weather2.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -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.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.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 : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java) - - override val name = "Weather" - - /** - * Fetches the weather data from a specific city. - */ - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val messages = getWeather(args, properties[API_KEY_PROP]) - if (messages[0].isError) { - helpResponse(event) - } else { - for (msg in messages) { - event.sendMessage(channel, msg) - } - } - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The OpenWeatherMap API Key property. - */ - const val API_KEY_PROP = "owm-api-key" - - // Weather command - private const val WEATHER_CMD = "weather" - - /** - * Converts and rounds temperature from °F to °C. - */ - fun ftoC(d: Double): Pair<Int, Int> { - val c = (d - 32) * 5 / 9 - return d.roundToInt() to c.roundToInt() - } - - /** - * Returns a country based on its country code. Defaults to [Country.UNITED_STATES] if not found. - */ - fun getCountry(countryCode: String): Country { - for (c in Country.entries) { - if (c.value.equals(countryCode, ignoreCase = true)) { - return c - } - } - return Country.UNITED_STATES - } - - /** - * Retrieves the weather data. - */ - @JvmStatic - @Throws(ModuleException::class) - fun getWeather(query: String, apiKey: String?): List<Message> { - if (apiKey.isNullOrBlank()) { - throw ModuleException( - "${Weather2::class.java.name} is disabled.", - "${WEATHER_CMD.capitalise()} is disabled. The API key is missing." - ) - } - val owm = OWM(apiKey) - val messages = mutableListOf<Message>() - owm.unit = OWM.Unit.IMPERIAL - if (query.isNotBlank()) { - val argv = query.split(",") - if (argv.size in 1..2) { - val city = argv[0].trim() - val code: String = if (argv.size > 1 && argv[1].isNotBlank()) { - argv[1].trim() - } else { - "US" - } - try { - val country = getCountry(code) - val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) { - owm.currentWeatherByZipCode(city.toInt(), country) - } else { - owm.currentWeatherByCityName(city, country) - } - if (cwd.hasCityName()) { - messages.add( - PublicMessage( - "City: ${cwd.cityName}, " + - country.name.replace('_', ' ').capitalizeWords() + " [${country.value}]" - ) - ) - cwd.mainData?.let { - with(it) { - if (hasTemp()) { - temp?.let { t -> - val (f, c) = ftoC(t) - messages.add(PublicMessage("Temperature: ${f}°F, ${c}°C")) - } - } - if (hasHumidity()) { - humidity?.let { h -> - messages.add(NoticeMessage("Humidity: ${h.roundToInt()}%")) - } - } - } - } - if (cwd.hasWindData()) { - cwd.windData?.let { - if (it.hasSpeed()) { - it.speed?.let { s -> - val w = mphToKmh(s) - messages.add(NoticeMessage("Wind: ${w.first} mph, ${w.second} km/h")) - } - } - } - } - if (cwd.hasWeatherList()) { - val condition = StringBuilder("Condition:") - cwd.weatherList?.let { - for (w in it) { - w?.let { - condition.append(' ') - .append(w.getDescription().capitalise()) - .append('.') - } - } - messages.add(NoticeMessage(condition.toString())) - } - } - if (cwd.hasCityId()) { - cwd.cityId?.let { - if (it > 0) { - messages.add( - NoticeMessage("https://openweathermap.org/city/$it", Colors.GREEN) - ) - } else { - messages.add( - NoticeMessage( - "https://openweathermap.org/find?q=" - + "$city,${code.uppercase()}".encodeUrl(), - Colors.GREEN - ) - ) - } - } - } - } - } catch (e: APIException) { - if (e.code == 404) { - throw ModuleException( - "getWeather($query): API ${e.code}", - "The requested city was not found.", - e - ) - } else { - throw ModuleException("getWeather($query): API ${e.code}", e.message, e) - } - } catch (e: NullPointerException) { - throw ModuleException("getWeather($query): NPE", "Unable to perform weather lookup.", e) - } - } - } - if (messages.isEmpty()) { - messages.add(ErrorMessage("Invalid syntax.")) - } - return messages - } - - /** - * Converts and rounds temperature from mph to km/h. - */ - fun mphToKmh(w: Double): Pair<Int, Int> { - val kmh = w * 1.60934 - return w.roundToInt() to kmh.roundToInt() - } - } - - init { - commands.add(WEATHER_CMD) - with(help) { - add("To display weather information:") - add(helpFormat("%c $WEATHER_CMD <city> [, <country code>]")) - add("For example:") - add(helpFormat("%c $WEATHER_CMD paris, fr")) - add("The default ISO 3166 country code is ${"US".bold()}. Zip codes supported in most countries.") - } - initProperties(API_KEY_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt deleted file mode 100644 index a72efab..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * WolframAlpha.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.isHttpSuccess -import net.thauvin.erik.mobibot.Utils.reader -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 -import java.net.URL - -class WolframAlpha : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(WolframAlpha::class.java) - - override val name = "WolframAlpha" - - private fun getUnits(unit: String?): String { - return if (unit?.lowercase() == METRIC) { - METRIC - } else { - IMPERIAL - } - } - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - if (args.isNotBlank()) { - try { - val query = args.trim().split("units=", limit = 2, ignoreCase = true) - event.sendMessage( - queryWolfram( - query[0].trim(), - units = if (query.size == 2) { - getUnits(query[1].trim()) - } else { - getUnits(properties[UNITS_PROP]) - }, - appId = properties[APPID_KEY_PROP] - ) - ) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } else { - helpResponse(event) - } - } - - companion object { - /** - * The Wolfram Alpha API Key property. - */ - const val APPID_KEY_PROP = "wolfram-appid" - - /** - * The Wolfram units properties - */ - const val UNITS_PROP = "wolfram-units" - - const val METRIC = "metric" - const val IMPERIAL = "imperial" - - // Wolfram command - private const val WOLFRAM_CMD = "wolfram" - - // Wolfram Alpha API URL - private const val API_URL = "http://api.wolframalpha.com/v1/spoken?appid=" - - @JvmStatic - @Throws(ModuleException::class) - fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String { - if (!appId.isNullOrEmpty()) { - try { - val urlReader = URL("${API_URL}${appId}&units=${units}&i=" + query.encodeUrl()).reader() - if (urlReader.responseCode.isHttpSuccess()) { - return urlReader.body - } else { - throw ModuleException( - "wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ", - urlReader.body.ifEmpty { - "Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})" - } - ) - } - } catch (ioe: IOException) { - throw ModuleException( - "wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe - ) - } - } else { - throw ModuleException("wolfram($query): No API Key", "No Wolfram Alpha API key specified.") - } - } - } - - init { - commands.add(WOLFRAM_CMD) - with(help) { - add("To get answers from Wolfram Alpha:") - add(Utils.helpFormat("%c $WOLFRAM_CMD <query> [units=(${METRIC}|${IMPERIAL})]")) - add("For example:") - add(Utils.helpFormat("%c $WOLFRAM_CMD days until christmas")) - add(Utils.helpFormat("%c $WOLFRAM_CMD distance earth moon units=metric")) - } - initProperties(APPID_KEY_PROP, UNITS_PROP) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt b/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt deleted file mode 100644 index 18072bc..0000000 --- a/bin/main/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ /dev/null @@ -1,390 +0,0 @@ -/* - * WorldTime.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import net.thauvin.erik.mobibot.Utils.bold -import net.thauvin.erik.mobibot.Utils.helpFormat -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 -import java.time.temporal.ChronoField - -/** - * The WorldTime module. - */ -class WorldTime : AbstractModule() { - override val name = "WorldTime" - - companion object { - /** - * Beats (Internet Time) keyword - */ - const val BEATS_KEYWORD = ".beats" - - /** - * Supported countries - */ - val COUNTRIES_MAP = buildMap<String, String> { - put("AG", "America/Antigua") - put("AI", "America/Anguilla") - put("AE", "Asia/Dubai") - put("AD", "Europe/Andorra") - put("AKDT", "America/Anchorage") - put("AF", "Asia/Kabul") - put("AKST", "America/Anchorage") - put("AL", "Europe/Tirane") - put("AM", "Asia/Yerevan") - put("AO", "Africa/Luanda") - put("AQ", "Antarctica/South_Pole") - put("AR", "America/Argentina/Buenos_Aires") - put("AS", "Pacific/Pago_Pago") - put("AT", "Europe/Vienna") - put("AU", "Australia/Sydney") - put("AW", "America/Aruba") - put("AX", "Europe/Mariehamn") - put("AZ", "Asia/Baku") - put("BA", "Europe/Sarajevo") - put("BB", "America/Barbados") - put("BD", "Asia/Dhaka") - put("BE", "Europe/Brussels") - put("BEAT", BEATS_KEYWORD) - put("BF", "Africa/Ouagadougou") - put("BG", "Europe/Sofia") - put("BH", "Asia/Bahrain") - put("BI", "Africa/Bujumbura") - put("BJ", "Africa/Porto-Novo") - put("BL", "America/St_Barthelemy") - put("BM", "Atlantic/Bermuda") - put("BMT", BEATS_KEYWORD) - put("BN", "Asia/Brunei") - put("BO", "America/La_Paz") - put("BQ", "America/Kralendijk") - put("BR", "America/Sao_Paulo") - put("BS", "America/Nassau") - put("BT", "Asia/Thimphu") - put("BW", "Africa/Gaborone") - put("BY", "Europe/Minsk") - put("BZ", "America/Belize") - put("CA", "America/Montreal") - put("CC", "Indian/Cocos") - put("CD", "Africa/Kinshasa") - put("CDT", "America/Chicago") - put("CET", "CET") - put("CF", "Africa/Bangui") - put("CG", "Africa/Brazzaville") - put("CH", "Europe/Zurich") - put("CI", "Africa/Abidjan") - put("CK", "Pacific/Rarotonga") - put("CL", "America/Santiago") - put("CM", "Africa/Douala") - put("CN", "Asia/Shanghai") - put("CO", "America/Bogota") - put("CR", "America/Costa_Rica") - put("CST", "America/Chicago") - put("CU", "Cuba") - put("CV", "Atlantic/Cape_Verde") - put("CW", "America/Curacao") - put("CX", "Indian/Christmas") - put("CY", "Asia/Nicosia") - put("CZ", "Europe/Prague") - put("DE", "Europe/Berlin") - put("DJ", "Africa/Djibouti") - put("DK", "Europe/Copenhagen") - put("DM", "America/Dominica") - put("DO", "America/Santo_Domingo") - put("DZ", "Africa/Algiers") - put("EC", "Pacific/Galapagos") - put("EDT", "America/New_York") - put("EE", "Europe/Tallinn") - put("EG", "Africa/Cairo") - put("EH", "Africa/El_Aaiun") - put("ER", "Africa/Asmara") - put("ES", "Europe/Madrid") - put("EST", "America/New_York") - put("ET", "Africa/Addis_Ababa") - put("FI", "Europe/Helsinki") - put("FJ", "Pacific/Fiji") - put("FK", "Atlantic/Stanley") - put("FM", "Pacific/Yap") - put("FO", "Atlantic/Faroe") - put("FR", "Europe/Paris") - put("GA", "Africa/Libreville") - put("GB", "Europe/London") - put("GD", "America/Grenada") - put("GE", "Asia/Tbilisi") - put("GF", "America/Cayenne") - put("GG", "Europe/Guernsey") - put("GH", "Africa/Accra") - put("GI", "Europe/Gibraltar") - put("GL", "America/Thule") - put("GM", "Africa/Banjul") - put("GMT", "GMT") - put("GN", "Africa/Conakry") - put("GP", "America/Guadeloupe") - put("GQ", "Africa/Malabo") - put("GR", "Europe/Athens") - put("GS", "Atlantic/South_Georgia") - put("GT", "America/Guatemala") - put("GU", "Pacific/Guam") - put("GW", "Africa/Bissau") - put("GY", "America/Guyana") - put("HK", "Asia/Hong_Kong") - put("HN", "America/Tegucigalpa") - put("HR", "Europe/Zagreb") - put("HST", "Pacific/Honolulu") - put("HT", "America/Port-au-Prince") - put("HU", "Europe/Budapest") - put("ID", "Asia/Jakarta") - put("IE", "Europe/Dublin") - put("IL", "Asia/Tel_Aviv") - put("IM", "Europe/Isle_of_Man") - put("IN", "Asia/Kolkata") - put("IO", "Indian/Chagos") - put("IQ", "Asia/Baghdad") - put("IR", "Asia/Tehran") - put("IS", "Atlantic/Reykjavik") - put("IT", "Europe/Rome") - put("JE", "Europe/Jersey") - put("JM", "Jamaica") - put("JO", "Asia/Amman") - put("JP", "Asia/Tokyo") - put("KE", "Africa/Nairobi") - put("KG", "Asia/Bishkek") - put("KH", "Asia/Phnom_Penh") - put("KI", "Pacific/Tarawa") - put("KM", "Indian/Comoro") - put("KN", "America/St_Kitts") - put("KP", "Asia/Pyongyang") - put("KR", "Asia/Seoul") - put("KW", "Asia/Riyadh") - put("KY", "America/Cayman") - put("KZ", "Asia/Oral") - put("LA", "Asia/Vientiane") - put("LB", "Asia/Beirut") - put("LC", "America/St_Lucia") - put("LI", "Europe/Vaduz") - put("LK", "Asia/Colombo") - put("LR", "Africa/Monrovia") - put("LS", "Africa/Maseru") - put("LT", "Europe/Vilnius") - put("LU", "Europe/Luxembourg") - put("LV", "Europe/Riga") - put("LY", "Africa/Tripoli") - put("MA", "Africa/Casablanca") - put("MC", "Europe/Monaco") - put("MD", "Europe/Chisinau") - put("MDT", "America/Denver") - put("ME", "Europe/Podgorica") - put("MF", "America/Marigot") - put("MG", "Indian/Antananarivo") - put("MH", "Pacific/Majuro") - put("MK", "Europe/Skopje") - put("ML", "Africa/Timbuktu") - put("MM", "Asia/Yangon") - put("MN", "Asia/Ulaanbaatar") - put("MO", "Asia/Macau") - put("MP", "Pacific/Saipan") - put("MQ", "America/Martinique") - put("MR", "Africa/Nouakchott") - put("MS", "America/Montserrat") - put("MST", "America/Denver") - put("MT", "Europe/Malta") - put("MU", "Indian/Mauritius") - put("MV", "Indian/Maldives") - put("MW", "Africa/Blantyre") - put("MX", "America/Mexico_City") - put("MY", "Asia/Kuala_Lumpur") - put("MZ", "Africa/Maputo") - put("NA", "Africa/Windhoek") - put("NC", "Pacific/Noumea") - put("NE", "Africa/Niamey") - put("NF", "Pacific/Norfolk") - put("NG", "Africa/Lagos") - put("NI", "America/Managua") - put("NL", "Europe/Amsterdam") - put("NO", "Europe/Oslo") - put("NP", "Asia/Kathmandu") - put("NR", "Pacific/Nauru") - put("NU", "Pacific/Niue") - put("NZ", "Pacific/Auckland") - put("OM", "Asia/Muscat") - put("PA", "America/Panama") - put("PDT", "America/Los_Angeles") - put("PE", "America/Lima") - put("PF", "Pacific/Tahiti") - put("PG", "Pacific/Port_Moresby") - put("PH", "Asia/Manila") - put("PK", "Asia/Karachi") - put("PL", "Europe/Warsaw") - put("PM", "America/Miquelon") - put("PN", "Pacific/Pitcairn") - put("PR", "America/Puerto_Rico") - put("PS", "Asia/Gaza") - put("PST", "America/Los_Angeles") - put("PT", "Europe/Lisbon") - put("PW", "Pacific/Palau") - put("PY", "America/Asuncion") - put("QA", "Asia/Qatar") - put("RE", "Indian/Reunion") - put("RO", "Europe/Bucharest") - put("RS", "Europe/Belgrade") - put("RU", "Europe/Moscow") - put("RW", "Africa/Kigali") - put("SA", "Asia/Riyadh") - put("SB", "Pacific/Guadalcanal") - put("SC", "Indian/Mahe") - put("SD", "Africa/Khartoum") - put("SE", "Europe/Stockholm") - put("SG", "Asia/Singapore") - put("SH", "Atlantic/St_Helena") - put("SI", "Europe/Ljubljana") - put("SJ", "Atlantic/Jan_Mayen") - put("SK", "Europe/Bratislava") - put("SL", "Africa/Freetown") - put("SM", "Europe/San_Marino") - put("SN", "Africa/Dakar") - put("SO", "Africa/Mogadishu") - put("SR", "America/Paramaribo") - put("SS", "Africa/Juba") - put("ST", "Africa/Sao_Tome") - put("SV", "America/El_Salvador") - put("SX", "America/Lower_Princes") - put("SY", "Asia/Damascus") - put("SZ", "Africa/Mbabane") - put("TC", "America/Grand_Turk") - put("TD", "Africa/Ndjamena") - put("TF", "Indian/Kerguelen") - put("TG", "Africa/Lome") - put("TH", "Asia/Bangkok") - put("TJ", "Asia/Dushanbe") - put("TK", "Pacific/Fakaofo") - put("TL", "Asia/Dili") - put("TM", "Asia/Ashgabat") - put("TN", "Africa/Tunis") - put("TO", "Pacific/Tongatapu") - put("TR", "Europe/Istanbul") - put("TT", "America/Port_of_Spain") - put("TV", "Pacific/Funafuti") - put("TW", "Asia/Taipei") - put("TZ", "Africa/Dar_es_Salaam") - put("UA", "Europe/Kiev") - put("UG", "Africa/Kampala") - put("UK", "Europe/London") - put("UM", "Pacific/Wake") - put("US", "America/New_York") - put("UTC", "UTC") - put("UY", "America/Montevideo") - put("UZ", "Asia/Tashkent") - put("VA", "Europe/Vatican") - put("VC", "America/St_Vincent") - put("VE", "America/Caracas") - put("VG", "America/Tortola") - put("VI", "America/St_Thomas") - put("VN", "Asia/Ho_Chi_Minh") - put("VU", "Pacific/Efate") - put("WF", "Pacific/Wallis") - put("WS", "Pacific/Apia") - put("YE", "Asia/Aden") - put("YT", "Indian/Mayotte") - put("ZA", "Africa/Johannesburg") - put("ZM", "Africa/Lusaka") - put("ZULU", "Zulu") - put("ZW", "Africa/Harare") - ZoneId.getAvailableZoneIds().filter { it.length <= 3 && !containsKey(it) } - .forEach { tz -> put(tz, tz) } - } - - // 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 ${"'HH:mm'".bold()} on ${"'EEEE, d MMMM yyyy'".bold()} in '") - - /** - * Returns the current Internet (beat) Time. - */ - 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 "%c%03d".format('@', beats) - } - - /** - * Returns the time for the given timezone/city. - */ - @JvmStatic - 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 ${internetTime().bold()} $BEATS_KEYWORD" - } else { - (ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(dtf) - + tz.substring(tz.lastIndexOf('/') + 1).replace('_', ' ').bold()) - } - } else { - "Unsupported country/zone. Please try again." - } - } - } - - 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)) - } - } - - override val isPrivateMsgEnabled = true - - init { - with(help) { - add("To display a country's current date/time:") - add(helpFormat("%c $TIME_CMD [<country code or zone>]")) - add("For a listing of the supported countries/zones:") - add(helpFormat("%c $TIME_CMD $ZONES_ARGS")) - } - commands.add(TIME_CMD) - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt deleted file mode 100644 index 0607936..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * ErrorMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.msg - -/** - * The `ErrorMessage` class. - */ -class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isError = true) diff --git a/bin/main/net/thauvin/erik/mobibot/msg/Message.kt b/bin/main/net/thauvin/erik/mobibot/msg/Message.kt deleted file mode 100644 index 23a33b9..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/Message.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Message.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.msg - -import net.thauvin.erik.semver.Constants - -/** - * The `Message` class. - */ -open class Message @JvmOverloads constructor( - var msg: String, - var color: String = DEFAULT_COLOR, - var isNotice: Boolean = false, - isError: Boolean = false, - var isPrivate: Boolean = false -) { - companion object { - var DEFAULT_COLOR = Constants.EMPTY - } - - init { - if (isError) { - isNotice = true - } - } - - /** Error flag. */ - var isError = isError - set(value) { - if (value) isNotice = true - field = value - } - - override fun toString(): String { - return "Message(color='$color', isError=$isError, isNotice=$isNotice, isPrivate=$isPrivate, msg='$msg')" - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt deleted file mode 100644 index 037d504..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * NoticeMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.msg - -/** - * The `NoticeMessage` class. - */ -class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isNotice = true) - diff --git a/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt deleted file mode 100644 index b424fdf..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * PrivateMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.msg - -/** - * The `PrivateMessage` class. - */ -class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : - Message(msg, color, isPrivate = true) diff --git a/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt deleted file mode 100644 index 9c5e088..0000000 --- a/bin/main/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * PublicMessage.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.msg - -/** - * The `PublicMessage` class. - */ -class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message(msg, color) diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt deleted file mode 100644 index 91f2dd9..0000000 --- a/bin/main/net/thauvin/erik/mobibot/social/SocialManager.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * SocialManager.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.social - -import net.thauvin.erik.mobibot.Addons -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.util.* - -/** - * Social Manager. - */ -class SocialManager { - private val entries: MutableSet<Int> = HashSet() - private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) - private val modules = ArrayList<SocialModule>() - private val timer = Timer(true) - - /** - * Adds social modules. - */ - fun add(addons: Addons, vararg modules: SocialModule) { - modules.forEach { - if (addons.add(it)) { - this.modules.add(it) - } - } - } - - /** - * Returns the number of entries. - */ - fun entriesCount(): Int = entries.size - - /** - * Sends a social notification (dm, etc.) - */ - fun notification(msg: String) { - modules.forEach { - it.notification(msg) - } - } - - /** - * Posts to social media. - */ - fun postEntry(index: Int) { - if (entries.contains(index)) { - modules.forEach { - it.postEntry(index) - } - entries.remove(index) - } - } - - /** - * Queues an entry for posting to social media. - */ - fun queueEntry(index: Int) { - if (modules.isNotEmpty()) { - entries.add(index) - if (logger.isDebugEnabled) { - logger.debug("Scheduling {} for posting on social media.", index.toLinkLabel()) - } - timer.schedule(SocialTimer(this, index), Constants.TIMER_DELAY * 60L * 1000L) - } - } - - /** - * Removes entries from queue. - */ - fun removeEntry(index: Int) { - entries.remove(index) - } - - /** - * Posts all entries on shutdown. - */ - fun shutdown() { - timer.cancel() - entries.forEach { - postEntry(it) - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt deleted file mode 100644 index b594670..0000000 --- a/bin/main/net/thauvin/erik/mobibot/social/SocialModule.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SocialModule.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.social - -import net.thauvin.erik.mobibot.commands.links.LinksManager -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import net.thauvin.erik.mobibot.entries.EntryLink -import net.thauvin.erik.mobibot.modules.AbstractModule -import net.thauvin.erik.mobibot.modules.ModuleException -import org.pircbotx.hooks.types.GenericMessageEvent -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -abstract class SocialModule : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(SocialManager::class.java) - - abstract val handle: String? - abstract val isAutoPost: Boolean - - abstract fun formatEntry(entry: EntryLink): String - - /** - * Sends a DM. - */ - fun notification(msg: String) { - if (isEnabled && !handle.isNullOrBlank()) { - try { - post(message = msg, isDm = true) - if (logger.isDebugEnabled) logger.debug("Notified $handle on $name: $msg") - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn("Failed to notify $handle on $name: $msg", e) - } - } - } - - abstract fun post(message: String, isDm: Boolean): String - - /** - * Post entry to social media. - */ - fun postEntry(index: Int) { - if (isAutoPost && LinksManager.entries.links.size >= index) { - try { - if (logger.isDebugEnabled) { - logger.debug("Posting {} to $name.", index.toLinkLabel()) - } - post(message = formatEntry(LinksManager.entries.links[index]), isDm = false) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn( - "Failed to post entry ${index.toLinkLabel()} on $name.", - e - ) - } - } - } - - override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { - try { - event.respond(post("$args (by ${event.user.nick} on $channel)", false)) - } catch (e: ModuleException) { - if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) - e.message?.let { - event.respond(it) - } - } - } -} diff --git a/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt b/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt deleted file mode 100644 index 3fd315e..0000000 --- a/bin/main/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SocialTimer.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.social - -import java.util.* - -class SocialTimer(private var socialManager: SocialManager, private var index: Int) : TimerTask() { - override fun run() { - socialManager.postEntry(index) - } -} diff --git a/bin/test/current.xml b/bin/test/current.xml deleted file mode 100644 index 535a400..0000000 --- a/bin/test/current.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ current.xml - ~ - ~ Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - ~ - ~ 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. - --> - -<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> - <channel> - <title>#mobibot IRC Links - https://www.mobitopia.org/mobibot/logs - Links from irc.example.com on #mobibot - en - Sun, 31 Oct 2021 21:45:11 GMT - 2021-10-31T21:45:11Z - en - - Example 2 - https://www.example.com/2 - Posted by <b>Skynx</b> on <a href="irc://irc.libera.chat/#mobibot"><b>#mobibot</b></a> - tag2-1 - tag2-2 - Sun, 31 Oct 2021 21:45:11 GMT - https://www.foo.com - mobibot@irc.libera.chat (Skynx) - 2021-10-31T00:01:00Z - - - Example 1 - https://www.example.com/1 - 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. - - tag1-1 - tag1-2 - Sun, 31 Oct 2021 21:43:15 GMT - https://www.example.com/ - mobibot@irc.libera.chat (ErikT) - 2021-10-31T00:00:00Z - - - diff --git a/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt b/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt deleted file mode 100644 index afb8ce6..0000000 --- a/bin/test/net/thauvin/erik/mobibot/AddonsTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * AddonsTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import assertk.assertThat -import assertk.assertions.containsExactly -import assertk.assertions.isEqualTo -import assertk.assertions.size -import net.thauvin.erik.mobibot.commands.ChannelFeed -import net.thauvin.erik.mobibot.commands.Cycle -import net.thauvin.erik.mobibot.commands.Die -import net.thauvin.erik.mobibot.commands.Ignore -import net.thauvin.erik.mobibot.commands.links.Comment -import net.thauvin.erik.mobibot.commands.links.View -import net.thauvin.erik.mobibot.modules.* -import kotlin.test.Test -import java.util.* - -class AddonsTest { - private val p = Properties().apply { - put("disabled-modules", "war,dice Lookup") - put("disabled-commands", "View | comment") - } - private val addons = Addons(p) - - @Test - fun addTest() { - // Modules - addons.add(Joke()) - addons.add(RockPaperScissors()) - addons.add(War()) - addons.add(Dice()) - addons.add(Lookup()) - assertThat(addons::modules).size().isEqualTo(2) - assertThat(addons.names.modules, "names.modules").containsExactly("Joke", "RockPaperScissors") - - // Commands - addons.add(View()) - addons.add(Comment()) - addons.add(Cycle()) - addons.add(Die()) // invisible - addons.add(ChannelFeed("channel")) // no properties, disabled - p[Ignore.IGNORE_PROP] = "nick" - addons.add(Ignore()) - assertThat(addons::commands).size().isEqualTo(3) - - assertThat(addons.names.ops, "names.ops").containsExactly("cycle") - - assertThat(addons.names.commands, "names.command").containsExactly( - "joke", - "rock", - "paper", - "scissors", - "ignore" - ) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt deleted file mode 100644 index a3994ec..0000000 --- a/bin/test/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * ExceptionSanitizer.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import net.thauvin.erik.mobibot.Utils.obfuscate -import net.thauvin.erik.mobibot.Utils.replaceEach -import net.thauvin.erik.mobibot.modules.ModuleException - -object ExceptionSanitizer { - /** - * Returns a sanitized exception to avoid displaying api keys, etc. in CI logs. - */ - 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(this) { - if (!cause?.message.isNullOrBlank()) { - return ModuleException( - debugMessage, - cause?.javaClass?.name + ": " + cause?.message?.replaceEach(search, obfuscate), - this - ) - } else if (!message.isNullOrBlank()) { - return ModuleException(debugMessage, message?.replaceEach(search, obfuscate), this) - } - } - } - return this - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt b/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt deleted file mode 100644 index 6e5fa8f..0000000 --- a/bin/test/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * FeedReaderTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import assertk.all -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.* -import com.rometools.rome.io.FeedException -import net.thauvin.erik.mobibot.FeedReader.Companion.readFeed -import net.thauvin.erik.mobibot.msg.Message -import kotlin.test.Test -import java.io.IOException -import java.net.MalformedURLException -import java.net.UnknownHostException - -/** - * The `FeedReader Test` class. - */ -class FeedReaderTest { - @Test - fun readFeedTest() { - var messages = readFeed("https://feeds.thauvin.net/ethauvin") - assertThat(messages, "messages").all { - size().isEqualTo(10) - index(1).prop(Message::msg).contains("erik.thauvin.net") - } - - messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=0") - assertThat(messages, "messages").index(0).prop(Message::msg).contains("nothing") - - messages = readFeed("https://lorem-rss.herokuapp.com/feed?length=84", 42) - assertThat(messages, "messages").size().isEqualTo(84) - messages.forEachIndexed { i, m -> - if (i % 2 == 0) { - assertThat(m, "messages($i)").prop(Message::msg).startsWith("Lorem ipsum") - } else { - assertThat(m, "messages($i)").prop(Message::msg).contains("http://example.com/test/") - } - } - - assertFailure { readFeed("blah") }.isInstanceOf(MalformedURLException::class.java) - - assertFailure { readFeed("https://www.example.com") }.isInstanceOf(FeedException::class.java) - - assertFailure { readFeed("https://www.thauvin.net/foo") }.isInstanceOf(IOException::class.java) - - assertFailure { readFeed("https://www.examplesfoo.com/") }.isInstanceOf(UnknownHostException::class.java) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt b/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt deleted file mode 100644 index 1384a72..0000000 --- a/bin/test/net/thauvin/erik/mobibot/LocalProperties.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * LocalProperties.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import org.testng.annotations.BeforeSuite -import java.io.IOException -import java.net.InetAddress -import java.net.UnknownHostException -import java.nio.file.Files -import java.nio.file.Paths -import java.util.* - -/** - * Access to `local.properties`. - */ -open class LocalProperties { - @BeforeSuite(alwaysRun = true) - fun loadProperties() { - val localPath = Paths.get("local.properties") - if (Files.exists(localPath)) { - try { - Files.newInputStream(localPath).use { stream -> localProps.load(stream) } - } catch (ignore: IOException) { - // Do nothing - } - } - } - - companion object { - private val localProps = Properties() - - fun getHostName(): String { - val ciName = System.getenv("CI_NAME") - return ciName ?: try { - InetAddress.getLocalHost().hostName - } catch (ignore: UnknownHostException) { - "Unknown Host" - } - } - - fun getProperty(key: String): String { - return if (localProps.containsKey(key)) { - localProps.getProperty(key) - } else { - val env = System.getenv(keyToEnv(key)) - env?.let { - localProps.setProperty(key, env) - } - env - } - } - - private fun keyToEnv(key: String): String { - return key.replace('-', '_').uppercase() - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt b/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt deleted file mode 100644 index b527fc5..0000000 --- a/bin/test/net/thauvin/erik/mobibot/PinboardTest.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * PinboardTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.entries.EntryLink -import org.testng.Assert.assertFalse -import org.testng.Assert.assertTrue -import kotlin.test.Test -import java.net.URL - -class PinboardTest : LocalProperties() { - private val pinboard = Pinboard() - - @Test - fun testPinboard() { - val apiToken = getProperty("pinboard-api-token") - val url = "https://www.example.com/${(1000..5000).random()}" - val ircServer = "irc.test.com" - val entry = EntryLink(url, "Test Example", "ErikT", "", "#mobitopia", listOf("test")) - - pinboard.setApiToken(apiToken) - - pinboard.addPin(ircServer, entry) - assertTrue(validatePin(apiToken, url = entry.link, entry.title, entry.nick, entry.channel), "addPin") - - entry.link = "https://www.example.com/${(5001..9999).random()}" - pinboard.updatePin(ircServer, url, entry) - assertTrue(validatePin(apiToken, url = entry.link, ircServer), "updatePin") - - entry.title = "Foo Title" - pinboard.updatePin(ircServer, entry.link, entry) - assertTrue(validatePin(apiToken, url = entry.link, entry.title), "updatePin(${entry.title}") - - pinboard.deletePin(entry) - assertFalse(validatePin(apiToken, url = entry.link), "deletePin") - } - - private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean { - val response = - URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body - - matches.forEach { - if (!response.contains(it)) { - return false - } - } - - return response.contains(url) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt b/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt deleted file mode 100644 index 7b7ba8c..0000000 --- a/bin/test/net/thauvin/erik/mobibot/UtilsTest.kt +++ /dev/null @@ -1,278 +0,0 @@ -/* - * UtilsTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.length -import net.thauvin.erik.mobibot.Utils.appendIfMissing -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.colorize -import net.thauvin.erik.mobibot.Utils.cyan -import net.thauvin.erik.mobibot.Utils.encodeUrl -import net.thauvin.erik.mobibot.Utils.getIntProperty -import net.thauvin.erik.mobibot.Utils.green -import net.thauvin.erik.mobibot.Utils.helpCmdSyntax -import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.lastOrEmpty -import net.thauvin.erik.mobibot.Utils.obfuscate -import net.thauvin.erik.mobibot.Utils.plural -import net.thauvin.erik.mobibot.Utils.reader -import net.thauvin.erik.mobibot.Utils.red -import net.thauvin.erik.mobibot.Utils.replaceEach -import net.thauvin.erik.mobibot.Utils.reverseColor -import net.thauvin.erik.mobibot.Utils.toIntOrDefault -import net.thauvin.erik.mobibot.Utils.toIsoLocalDate -import net.thauvin.erik.mobibot.Utils.toUtcDateTime -import net.thauvin.erik.mobibot.Utils.today -import net.thauvin.erik.mobibot.Utils.underline -import net.thauvin.erik.mobibot.Utils.unescapeXml -import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR -import org.pircbotx.Colors -import org.testng.annotations.BeforeClass -import kotlin.test.Test -import java.io.File -import java.io.IOException -import java.net.URL -import java.time.LocalDateTime -import java.util.* - -/** - * The `Utils Test` class. - */ -class UtilsTest { - private val ascii = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - private val cal = Calendar.getInstance() - private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) - private val test = "This is a test." - - @BeforeClass - fun setUp() { - cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 - } - - @Test - fun testAppendIfMissing() { - val dir = "dir" - val sep = '/' - val url = "https://erik.thauvin.net" - assertThat(dir.appendIfMissing(File.separatorChar), "appendIfMissing(dir)") - .isEqualTo(dir + File.separatorChar) - assertThat(url.appendIfMissing(sep), "appendIfMissing(url)").isEqualTo("$url$sep") - assertThat("$url$sep".appendIfMissing(sep), "appendIfMissing($url$sep)").isEqualTo("$url$sep") - } - - @Test - fun testBold() { - assertThat(1.bold(), "bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) - assertThat(2L.bold(), "bold(2L)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) - assertThat(ascii.bold(), "ascii.bold()").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat("test".bold(), "test.bold()").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) - } - - - @Test - fun testCapitalise() { - assertThat("test".capitalise(), "capitalize(test)").isEqualTo("Test") - assertThat("Test".capitalise(), "capitalize(Test)").isEqualTo("Test") - assertThat(test.capitalise(), "capitalize($test)").isEqualTo(test) - assertThat("".capitalise(), "capitalize()").isEqualTo("") - } - - @Test - fun textCapitaliseWords() { - assertThat(test.capitalizeWords(), "captiatlizeWords(test)").isEqualTo("This Is A Test.") - assertThat("Already Capitalized".capitalizeWords(), "already capitalized") - .isEqualTo("Already Capitalized") - assertThat(" a test ".capitalizeWords(), "with spaces").isEqualTo(" A Test ") - } - - @Test - fun testColorize() { - assertThat(ascii.colorize(Colors.REVERSE), "reverse.colorize()").isEqualTo( - Colors.REVERSE + ascii + Colors.REVERSE - ) - assertThat(ascii.colorize(Colors.RED), "red.colorize()") - .isEqualTo(Colors.RED + ascii + Colors.NORMAL) - assertThat(ascii.colorize(Colors.BOLD), "colorized(bold)") - .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) - assertThat(null.colorize(Colors.RED), "null.colorize()").isEqualTo("") - assertThat("".colorize(Colors.RED), "colorize()").isEqualTo("") - assertThat(ascii.colorize(DEFAULT_COLOR), "ascii.colorize()").isEqualTo(ascii) - assertThat(" ".colorize(Colors.NORMAL), "blank.colorize()") - .isEqualTo(Colors.NORMAL + " " + Colors.NORMAL) - } - - @Test - fun testCyan() { - assertThat(ascii.cyan()).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) - } - - @Test - fun testEncodeUrl() { - assertThat("Hello Günter".encodeUrl()).isEqualTo("Hello%20G%C3%BCnter") - } - - @Test - fun testGetIntProperty() { - val p = Properties() - p["one"] = "1" - p["two"] = "two" - assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1) - assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2) - assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3) - } - - @Test - fun testGreen() { - assertThat(ascii.green()).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) - } - - @Test - fun testHelpCmdSyntax() { - val bot = "mobibot" - assertThat(helpCmdSyntax("%c $test %n $test", bot, false), "helpCmdSyntax(private)") - .isEqualTo("$bot: $test $bot $test") - assertThat(helpCmdSyntax("%c %n $test %c $test %n", bot, true), "helpCmdSyntax(public)") - .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") - } - - @Test - fun testHelpFormat() { - assertThat(helpFormat(test, isBold = true, isIndent = false), "helpFormat(bold)") - .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") - assertThat(helpFormat(test, isBold = false, isIndent = true), "helpFormat(indent)") - .isEqualTo(test.prependIndent()) - assertThat(helpFormat(test, isBold = true, isIndent = true), "helpFormat(bold,indent)") - .isEqualTo(test.colorize(Colors.BOLD).prependIndent()) - - } - - @Test - fun testIsoLocalDate() { - assertThat(cal.time.toIsoLocalDate(), "isoLocalDate(date)").isEqualTo("1952-02-17") - assertThat(localDateTime.toIsoLocalDate(), "isoLocalDate(localDate)").isEqualTo("1952-02-17") - } - - @Test - fun testLastOrEmpty() { - val two = listOf("1", "2") - assertThat(two.lastOrEmpty(), "lastOrEmpty(1,2)").isEqualTo("2") - val one = listOf("1") - assertThat(one.lastOrEmpty(), "lastOrEmpty(1)").isEqualTo("") - } - - @Test - fun testObfuscate() { - assertThat(ascii.obfuscate(), "obfuscate()").all { - length().isEqualTo(ascii.length) - isEqualTo(("x".repeat(ascii.length))) - } - assertThat(" ".obfuscate(), "obfuscate(blank)").isEqualTo(" ") - } - - @Test - fun testPlural() { - val week = "week" - val weeks = "weeks" - - for (i in -1..3) { - assertThat(week.plural(i.toLong()), "plural($i)").isEqualTo(if (i > 1) weeks else week) - } - } - - @Test - fun testReplaceEach() { - val search = arrayOf("one", "two", "three") - val replace = arrayOf("1", "2", "3") - assertThat(search.joinToString(",").replaceEach(search, replace), "replaceEach(1,2,3") - .isEqualTo(replace.joinToString(",")) - - assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test) - - assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)") - .isEqualTo(test.replace("t", "").replace("e", "E")) - - assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)") - .isEqualTo(test) - } - - @Test - fun testRed() { - assertThat(ascii.red()).isEqualTo(ascii.colorize(Colors.RED)) - } - - @Test - fun testReverseColor() { - assertThat(ascii.reverseColor()).isEqualTo(Colors.REVERSE + ascii + Colors.REVERSE) - } - - @Test - fun testToday() { - assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate()) - } - - @Test - fun testToIntOrDefault() { - assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10) - assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2) - } - - @Test - fun testUnderline() { - assertThat(ascii.underline()).isEqualTo(ascii.colorize(Colors.UNDERLINE)) - } - - @Test - fun testUnescapeXml() { - assertThat("<a name="test & ''">".unescapeXml()).isEqualTo( - "" - ) - } - - @Test - @Throws(IOException::class) - fun testUrlReader() { - val reader = URL("https://postman-echo.com/status/200").reader() - assertThat(reader.body).isEqualTo("{\n \"status\": 200\n}") - assertThat(reader.responseCode).isEqualTo(200) - } - - @Test - fun testUtcDateTime() { - assertThat(cal.time.toUtcDateTime(), "utcDateTime(date)").isEqualTo("1952-02-17 12:30") - assertThat(localDateTime.toUtcDateTime(), "utcDateTime(localDate)").isEqualTo("1952-02-17 12:30") - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt deleted file mode 100644 index 99ba951..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * InfoTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 assertk.assertThat -import assertk.assertions.isEqualTo -import net.thauvin.erik.mobibot.commands.Info.Companion.toUptime -import kotlin.test.Test - -class InfoTest { - @Test - fun testToUptime() { - assertThat( - 547800300076L.toUptime(), - "upTime(full)" - ).isEqualTo("17 years 4 months 2 weeks 1 day 6 hours 45 minutes") - assertThat(24300000L.toUptime(), "upTime(hours minutes)").isEqualTo("6 hours 45 minutes") - assertThat(110700000L.toUptime(), "upTime(days hours minutes)").isEqualTo("1 day 6 hours 45 minutes") - assertThat( - 1320300000L.toUptime(), - "upTime(weeks days hours minutes)" - ).isEqualTo("2 weeks 1 day 6 hours 45 minutes") - assertThat(2700000L.toUptime(), "upTime(45 minutes)").isEqualTo("45 minutes") - assertThat(60000L.toUptime(), "upTime(1 minute)").isEqualTo("1 minute") - assertThat(59000L.toUptime(), "upTime(59 seconds)").isEqualTo("59 seconds") - assertThat(0L.toUptime(), "upTime(0 second)").isEqualTo("0 second") - - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt deleted file mode 100644 index b6fbbff..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * RecapTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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 assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.matches -import assertk.assertions.prop -import assertk.assertions.size -import kotlin.test.Test - -class RecapTest { - @Test - fun storeRecapTest() { - for (i in 1..20) { - Recap.storeRecap("sender$i", "test $i", false) - } - assertThat(Recap.recaps, "Recap.recaps").all { - size().isEqualTo(Recap.MAX_RECAPS) - prop(MutableList::first) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender11: test 11".toRegex()) - prop(MutableList::last) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender20: test 20".toRegex()) - } - - Recap.storeRecap("sender", "test action", true) - assertThat(Recap.recaps.last()) - .matches("[1-2]\\d{3}-[01]\\d-[0-3]\\d [0-2]\\d:[0-6]\\d - sender test action".toRegex()) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt deleted file mode 100644 index fa2bb70..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * LinksManagerTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.links - -import assertk.all -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isEqualTo -import assertk.assertions.isTrue -import assertk.assertions.size -import net.thauvin.erik.mobibot.Constants -import kotlin.test.Test - -class LinksManagerTest { - private val linksManager = LinksManager() - - @Test - fun fetchTitle() { - assertThat(linksManager.fetchTitle("https://erik.thauvin.net/"), "fetchTitle(Erik)").contains("Erik's Weblog") - assertThat( - linksManager.fetchTitle("https://www.google.com/foo"), - "fetchTitle(Foo)" - ).isEqualTo(Constants.NO_TITLE) - } - - @Test - fun testMatches() { - assertThat(linksManager.matches("https://www.example.com/"), "matches(url)").isTrue() - assertThat(linksManager.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "matches(HTTP)").isTrue() - } - - @Test - fun matchTagKeywordsTest() { - linksManager.setProperty(LinksManager.KEYWORDS_PROP, "key1 key2,key3") - val tags = mutableListOf() - - linksManager.matchTagKeywords("Test title with key2", tags) - assertThat(tags, "tags").contains("key2") - tags.clear() - - linksManager.matchTagKeywords("Test key3 title with key1", tags) - assertThat(tags, "tags(key1, key3)").all { - contains("key1") - contains("key3") - size().isEqualTo(2) - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt deleted file mode 100644 index e315891..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * ViewTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.links - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.prop -import net.thauvin.erik.mobibot.entries.EntryLink -import kotlin.test.Test - -class ViewTest { - @Test - fun testParseArgs() { - val view = View() - - for (i in 1..10) { - LinksManager.entries.links.add( - EntryLink( - "https://www.example.com/$i", - "Example $i", - "nick$i", - "login$i", - "#channel", - emptyList() - ) - ) - } - - assertThat(view.parseArgs("1"), "parseArgs(1)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("") - } - - assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all { - prop(Pair::first).isEqualTo(1) - prop(Pair::second).isEqualTo("foo") - } - - assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all { - prop(Pair::first).isEqualTo(2) - prop(Pair::second).isEqualTo("foo") - } - - assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all { - prop(Pair::first).isEqualTo(3) - prop(Pair::second).isEqualTo("foo bar") - } - - assertThat(view.parseArgs("foo bar"), "parseArgs(foo bar)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("foo bar") - } - - assertThat(view.parseArgs("${Int.MAX_VALUE}1"), "parseArgs(overflow)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("${Int.MAX_VALUE}1") - } - - assertThat(view.parseArgs("1a"), "parseArgs(1a)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("1a") - } - - assertThat(view.parseArgs("20"), "parseArgs(20)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("") - } - - assertThat(view.parseArgs(""), "parseArgs()").all { - prop(Pair::first).isEqualTo(LinksManager.entries.links.size - View.MAX_ENTRIES) - prop(Pair::second).isEqualTo("") - } - - LinksManager.entries.links.clear() - - assertThat(view.parseArgs("4"), "parseArgs(4)").all { - prop(Pair::first).isEqualTo(0) - prop(Pair::second).isEqualTo("") - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt deleted file mode 100644 index 37b884b..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SeenTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.seen - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import org.testng.annotations.AfterClass -import org.testng.annotations.BeforeClass -import kotlin.test.Test -import kotlin.io.path.deleteIfExists -import kotlin.io.path.fileSize - -class SeenTest { - private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") - private val seen = Seen(tmpFile.toAbsolutePath().toString()) - private val nick = "ErikT" - - @BeforeClass - fun saveTest() { - seen.add("ErikT") - assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) - } - - @AfterClass(alwaysRun = true) - fun afterClass() { - tmpFile.deleteIfExists() - } - - @Test(priority = 1, groups = ["commands"]) - fun loadTest() { - seen.clear() - assertThat(seen::seenNicks).isEmpty() - seen.load() - assertThat(seen::seenNicks).key(nick).isNotNull() - } - - @Test - fun addTest() { - val last = seen.seenNicks[nick]?.lastSeen - seen.add(nick.lowercase()) - assertThat(seen).all { - prop(Seen::seenNicks).size().isEqualTo(1) - prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last) - prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(nick.lowercase()) - } - } - - @Test(priority = 10, groups = ["commands"]) - fun clearTest() { - seen.clear() - seen.save() - seen.load() - assertThat(seen::seenNicks).size().isEqualTo(0) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt deleted file mode 100644 index cc09237..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * TellMessageTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.tell - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isFalse -import assertk.assertions.isTrue -import assertk.assertions.prop -import kotlin.test.Test -import java.time.Duration -import java.time.LocalDateTime -import java.time.temporal.Temporal - -/** - * The `TellMessageTest` class. - */ -class TellMessageTest { - private fun isValidDate(date: Temporal): Boolean { - return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 - } - - @Test - fun testTellMessage() { - val message = "Test message." - val recipient = "recipient" - val sender = "sender" - val tellMessage = TellMessage(sender, recipient, message) - assertThat(tellMessage).all { - prop(TellMessage::sender).isEqualTo(sender) - prop(TellMessage::recipient).isEqualTo(recipient) - prop(TellMessage::message).isEqualTo(message) - } - assertThat(isValidDate(tellMessage.queued), "isValidDate()").isTrue() - assertThat(tellMessage.isMatch(sender), "isMatch(sender)").isTrue() - assertThat(tellMessage.isMatch(recipient), "isMatch(recipient)").isTrue() - assertThat(tellMessage.isMatch("foo"), "isMatch(foo)").isFalse() - tellMessage.isReceived = false - assertThat(tellMessage.receptionDate, "receptionDate").isEqualTo(LocalDateTime.MIN) - tellMessage.isReceived = true - assertThat(isValidDate(tellMessage.receptionDate), "isValidDate(creationDate)").isTrue() - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt deleted file mode 100644 index 5acba78..0000000 --- a/bin/test/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * TellMessagesMgrTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.tell - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import org.testng.annotations.AfterClass -import org.testng.annotations.BeforeClass -import kotlin.test.Test -import java.time.LocalDateTime -import kotlin.io.path.createTempFile -import kotlin.io.path.deleteIfExists -import kotlin.io.path.fileSize - -class TellMessagesMgrTest { - private val testFile = createTempFile(suffix = ".ser") - private val maxDays = 10L - private val testMessages = mutableListOf().apply { - for (i in 0..5) { - this.add(i, TellMessage("sender$i", "recipient$i", "message $i")) - } - } - - @BeforeClass - fun saveTest() { - TellManager.save(testFile.toAbsolutePath().toString(), testMessages) - assertThat(testFile.fileSize()).isGreaterThan(0) - } - - @AfterClass - fun afterClass() { - testFile.deleteIfExists() - } - - @Test - fun cleanTest() { - testMessages.add(TellMessage("sender", "recipient", "message").apply { - queued = LocalDateTime.now().minusDays(maxDays) - }) - val size = testMessages.size - assertThat(TellManager.clean(testMessages, maxDays + 2), "clean(maxDays=${maxDays + 2})").isFalse() - assertThat(TellManager.clean(testMessages, maxDays), "clean(maxDays=$maxDays)").isTrue() - assertThat(testMessages, "testMessages").size().isEqualTo(size - 1) - } - - @Test - fun loadTest() { - val messages = TellManager.load(testFile.toAbsolutePath().toString()) - for (i in messages.indices) { - assertThat(messages).index(i).all { - prop(TellMessage::sender).isEqualTo(testMessages[i].sender) - prop(TellMessage::recipient).isEqualTo(testMessages[i].recipient) - prop(TellMessage::message).isEqualTo(testMessages[i].message) - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt deleted file mode 100644 index 0ad26b5..0000000 --- a/bin/test/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * EntriesUtilsTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.assertThat -import assertk.assertions.contains -import assertk.assertions.isEqualTo -import net.thauvin.erik.mobibot.Constants -import net.thauvin.erik.mobibot.entries.EntriesUtils.printComment -import net.thauvin.erik.mobibot.entries.EntriesUtils.printLink -import net.thauvin.erik.mobibot.entries.EntriesUtils.printTags -import net.thauvin.erik.mobibot.entries.EntriesUtils.toLinkLabel -import kotlin.test.Test - -class EntriesUtilsTest { - private val comment = EntryComment("comment", "nick") - private val links = buildList { - for (i in 0..5) { - add( - EntryLink( - "https://www.mobitopia.org/$i", - "Mobitopia$i", - "Skynx$i", - "JimH$i", - "#mobitopia$i", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") - ) - ) - } - } - - @Test - fun printCommentTest() { - assertThat(printComment(0, 0, comment)).isEqualTo("${Constants.LINK_CMD}1.1: [nick] comment") - } - - @Test - fun printLinkTest() { - for (i in links.indices) { - assertThat( - printLink(i - 1, links[i]), "link $i" - ).isEqualTo("L$i: [Skynx$i] \u0002Mobitopia$i\u0002 ( \u000303https://www.mobitopia.org/$i\u000F )") - } - - assertThat(links.first().addComment(comment), "addComment()").isEqualTo(0) - assertThat(printLink(0, links.first(), isView = true), "printLink(isView=true)").contains("[+1]") - } - - @Test - fun printTagsTest() { - for (i in links.indices) { - assertThat( - printTags(i - 1, links[i]), "tag $i" - ).isEqualTo("L${i}T: tag1, tag2, tag3, tag4, tag5") - } - } - - @Test - fun toLinkLabelTest() { - assertThat(1.toLinkLabel()).isEqualTo("${Constants.LINK_CMD}2") - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt deleted file mode 100644 index f421099..0000000 --- a/bin/test/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * EntryLinkTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.* -import com.rometools.rome.feed.synd.SyndCategory -import com.rometools.rome.feed.synd.SyndCategoryImpl -import kotlin.test.Test -import java.security.SecureRandom -import java.util.* - -/** - * The `EntryUtilsTest` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-19 - * @since 1.0 - */ -class EntryLinkTest { - private val entryLink = EntryLink( - "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", - listOf("tag1", "tag2", "tag3", "TAG4", "Tag5") - ) - - @Test - fun testAddDeleteComment() { - var i = 0 - while (i < 5) { - entryLink.addComment("c$i", "u$i") - i++ - } - assertThat(entryLink.comments, "comments").size().isEqualTo(i) - i = 0 - for (comment in entryLink.comments) { - 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, "hasComments()").isEmpty() - entryLink.addComment("nothing", "nobody") - entryLink.setComment(0, "something", "somebody") - val comment = entryLink.getComment(0) - assertThat(comment, "comment[first]").all { - prop(EntryComment::nick).isEqualTo("somebody") - prop(EntryComment::comment).isEqualTo("something") - } - assertThat(entryLink.deleteComment(comment), "deleteComment").isTrue() - assertThat(entryLink.deleteComment(comment), "comment is already deleted").isFalse() - } - - @Test - fun testConstructor() { - val tags = listOf(SyndCategoryImpl().apply { name = "tag1" }, SyndCategoryImpl().apply { name = "tag2" }) - val link = EntryLink("link", "title", "nick", "channel", Date(), tags) - assertThat(link, "link").all { - prop(EntryLink::tags).size().isEqualTo(tags.size) - prop(EntryLink::tags).index(0).prop(SyndCategory::getName).isEqualTo("tag1") - } - } - - @Test - fun testMatches() { - assertThat(entryLink.matches("mobitopia"), "matches(mobitopia)").isTrue() - assertThat(entryLink.matches("skynx"), "match(nick)").isTrue() - assertThat(entryLink.matches("www.mobitopia.org"), "matches(url)").isTrue() - assertThat(entryLink.matches("foo"), "matches(foo)").isFalse() - assertThat(entryLink.matches(""), "matches(empty)").isFalse() - assertThat(entryLink.matches(null), "matches(null)").isFalse() - } - - - @Test - fun testTags() { - val tags: List = entryLink.tags - for ((i, tag) in tags.withIndex()) { - assertThat(tag.name, "tag.name($i)").isEqualTo("tag${i + 1}") - } - assertThat(entryLink::tags).size().isEqualTo(5) - entryLink.setTags("-tag5, tag4") - entryLink.setTags("+mobitopia") - entryLink.setTags("-mobitopia") - assertThat( - entryLink.formatTags(","), - "formatTags(',')" - ).isEqualTo("tag1,tag2,tag3,tag4,mobitopia") - entryLink.setTags("-tag4 tag5") - assertThat( - entryLink.formatTags(" ", ","), "formatTag(' ',',')" - ).isEqualTo(",tag1 tag2 tag3 mobitopia tag5") - val size = entryLink.tags.size - entryLink.setTags("") - assertThat(entryLink.tags, "setTags('')").size().isEqualTo(size) - entryLink.setTags(" ") - assertThat(entryLink.tags, "setTags(' ')").size().isEqualTo(size) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt deleted file mode 100644 index 1611009..0000000 --- a/bin/test/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * FeedMgrTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.* -import net.thauvin.erik.mobibot.Utils.today -import org.testng.annotations.BeforeSuite -import kotlin.test.Test -import java.nio.file.Paths -import java.util.* -import kotlin.io.path.deleteIfExists -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(FeedsManager.loadFeed(entries), "loadFeed()").isEqualTo("2021-10-31") - - assertThat(entries.links, "entries.links").size().isEqualTo(2) - entries.links.forEachIndexed { i, entryLink -> - assertThat(entryLink, "entryLink[${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, "nick[first]").isEqualTo("ErikT") - assertThat(date, "date[first]").isEqualTo(Date(1635638400000L)) - assertThat(comments.first(), "comments[first]").all { - prop(EntryComment::comment).endsWith("comment 1.") - prop(EntryComment::nick).isEqualTo("ErikT") - } - assertThat(comments.last(), "comments[last]").all { - prop(EntryComment::comment).endsWith("comment 2.") - prop(EntryComment::nick).isEqualTo("Skynx") - } - } - - assertThat(entries.links, "links").index(1).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 - FeedsManager.saveFeed(entries, currentFile.name) - - assertThat(currentFile, "currentFile").exists() - assertThat(backlogFile, "backlogFile").exists() - - assertThat(currentFile.fileSize(), "currentFile == backlogFile").isEqualTo(backlogFile.fileSize()) - - // Load the test feed - entries.links.clear() - FeedsManager.loadFeed(entries, currentFile.name) - - entries.links.forEachIndexed { i, entryLink -> - assertThat(entryLink.title, "entryLink.title[${i + 1}]").isEqualTo("Example ${i + 1}") - } - - assertThat(currentFile.deleteIfExists(), "currentFile.deleteIfExists()").isTrue() - assertThat(backlogFile.deleteIfExists(), "backlogFile.deleteIfExists()").isTrue() - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt deleted file mode 100644 index 3bd4c0f..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * CalcTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.isEqualTo -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 kotlin.test.Test - -/** - * The `CalcTest` class. - */ -class CalcTest { - @Test - fun testCalculate() { - assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}") - assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}") - assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}") - assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt deleted file mode 100644 index 085f228..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * ChatGptTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.hasNoCause -import assertk.assertions.isInstanceOf -import net.thauvin.erik.mobibot.LocalProperties -import kotlin.test.Test - -class ChatGptTest : LocalProperties() { - @Test - fun testApiKey() { - assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } - .isInstanceOf(ModuleException::class.java) - .hasNoCause() - } - - @Test(groups = ["modules", "no-ci"]) - fun testChat() { - val apiKey = getProperty(ChatGpt.API_KEY_PROP) - assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) - ).contains("XMLHttpRequest") - assertThat( - ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) - ).contains("URLEncoder") - - assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } - .isInstanceOf(ModuleException::class.java) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt deleted file mode 100644 index d0ecbc9..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * CryptoPricesTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.all -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isGreaterThan -import assertk.assertions.prop -import net.thauvin.erik.crypto.CryptoPrice -import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice -import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName -import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies -import org.testng.annotations.BeforeClass -import kotlin.test.Test - -/** - * The `CryptoPricesTest` class. - */ -class CryptoPricesTest { - @BeforeClass - @Throws(ModuleException::class) - fun before() { - loadCurrencies() - } - - @Test - @Throws(ModuleException::class) - fun testMarketPrice() { - var price = currentPrice(listOf("BTC")) - assertThat(price, "currentPrice(BTC)").all { - prop(CryptoPrice::base).isEqualTo("BTC") - prop(CryptoPrice::currency).isEqualTo("USD") - prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) - } - - price = currentPrice(listOf("ETH", "EUR")) - assertThat(price, "currentPrice(ETH, EUR)").all { - prop(CryptoPrice::base).isEqualTo("ETH") - prop(CryptoPrice::currency).isEqualTo("EUR") - prop(CryptoPrice::amount).transform { it.signum() }.isGreaterThan(0) - } - } - - @Test - fun testGetCurrencyName() { - assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") - assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt deleted file mode 100644 index 8d8c997..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * CurrencyConverterTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.all -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isInstanceOf -import assertk.assertions.matches -import assertk.assertions.prop -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency -import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols -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 kotlin.test.Test - - -/** - * The `CurrencyConvertTest` class. - */ -class CurrencyConverterTest : LocalProperties() { - - @BeforeClass - @Throws(ModuleException::class) - fun before() { - val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) - loadSymbols(apiKey) - } - - @Test - fun testConvertCurrency() { - val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) - assertThat( - convertCurrency(apiKey, "100 USD to EUR").msg, - "convertCurrency(100 USD to EUR)" - ).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex()) - assertThat( - convertCurrency(apiKey, "1 USD to GBP").msg, - "convertCurrency(1 USD to BGP)" - ).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex()) - assertThat( - convertCurrency(apiKey, "100,000.00 CAD to USD").msg, - "convertCurrency(100,000.00 GBP to USD)" - ).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex()) - assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all { - prop(Message::msg).contains("You're kidding, right?") - isInstanceOf(PublicMessage::class.java) - } - assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all { - prop(Message::msg).contains("Invalid query.") - isInstanceOf(ErrorMessage::class.java) - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt deleted file mode 100644 index 8bafede..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * DiceTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - - -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.matches -import kotlin.test.Test - -class DiceTest { - @Test - fun testRoll() { - assertThat(Dice.roll(1, 1), "roll(1d1)").isEqualTo("\u00021\u0002") - assertThat(Dice.roll(2, 1), "roll(2d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002") - assertThat(Dice.roll(5, 1), "roll(5d1)") - .isEqualTo("\u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 + \u00021\u0002 = \u00025\u0002") - assertThat(Dice.roll(2, 6), "roll(2d6)") - .matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex()) - assertThat(Dice.roll(3, 7), "roll(3d7)") - .matches("\u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 \\+ \u0002[1-7]\u0002 = \u0002\\d{1,2}\u0002".toRegex()) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt deleted file mode 100644 index 85c990c..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * GoogleSearchTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.all -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import kotlin.test.Test - -/** - * The `GoogleSearchTest` class. - */ -class GoogleSearchTest : LocalProperties() { - @Test - fun testAPIKeys() { - assertThat( - searchGoogle("", "apikey", "cssKey").first(), - "searchGoogle(empty)" - ).isInstanceOf(ErrorMessage::class.java) - - assertFailure { searchGoogle("test", "", "apiKey") } - .isInstanceOf(ModuleException::class.java).hasNoCause() - - assertFailure { searchGoogle("test", "apiKey", "") } - .isInstanceOf(ModuleException::class.java).hasNoCause() - - assertFailure { searchGoogle("test", "apiKey", "cssKey") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("API key not valid. Please pass a valid API key.") - } - - @Test\n@DisableOnCi - @Throws(ModuleException::class) - fun testSearchGoogle() { - val apiKey = getProperty(GoogleSearch.API_KEY_PROP) - val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP) - - try { - var query = "mobibot" - var messages = searchGoogle(query, apiKey, cseKey) - assertThat(messages, "searchGoogle($query)").all { - isNotEmpty() - index(0).prop(Message::msg).contains(query, true) - } - - query = "adadflkjl" - messages = searchGoogle(query, apiKey, cseKey) - assertThat(messages, "searchGoogle($query)").index(0).all { - isInstanceOf(ErrorMessage::class.java) - prop(Message::msg).isEqualTo("No results found.") - } - } catch (e: ModuleException) { - // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI")) { - throw e.sanitize(apiKey, cseKey) - } else { - throw e - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt deleted file mode 100644 index 0af8e75..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * JokeTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke -import net.thauvin.erik.mobibot.msg.Message -import net.thauvin.erik.mobibot.msg.PublicMessage -import kotlin.test.Test - -/** - * The `JokeTest` class. - */ -class JokeTest { - @Test - @Throws(ModuleException::class) - fun testRandomJoke() { - val joke = randomJoke() - assertThat(joke, "randomJoke()").all { - size().isGreaterThan(0) - each { - it.isInstanceOf(PublicMessage::class.java) - it.prop(Message::msg).doesNotContain("\n") - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt deleted file mode 100644 index a3fc226..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * LookupTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.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 kotlin.test.Test - -/** - * The `Lookup Test` class. - */ -class LookupTest { - @Test - @Throws(Exception::class) - fun testLookup() { - var result = nslookup("apple.com") - assertThat(result, "lookup(apple.com)").contains("17.253.144.10") - - result = nslookup("204.122.16.136") - assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") - } - - @Test - @Throws(Exception::class) - fun testWhois() { - val result = whois("17.178.96.59", Lookup.WHOIS_HOST) - assertThat(result, "whois(17.178.96.59").any { it.contains("Apple Inc.") } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt deleted file mode 100644 index f4b5e99..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * MastodonTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.assertThat -import assertk.assertions.contains -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot -import kotlin.test.Test - -class MastodonTest : LocalProperties() { - @Test - @Throws(ModuleException::class) - fun testToot() { - val msg = "Testing Mastodon API from ${getHostName()}" - assertThat( - toot( - getProperty(Mastodon.ACCESS_TOKEN_PROP), - getProperty(Mastodon.INSTANCE_PROP), - getProperty(Mastodon.HANDLE_PROP), - msg, - true - ) - ).contains(msg) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt deleted file mode 100644 index 1416eec..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * ModuleExceptionTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.all -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import org.testng.annotations.DataProvider -import kotlin.test.Test -import java.io.IOException -import java.lang.reflect.Method - -/** - * The `ModuleExceptionTest` class. - */ -class ModuleExceptionTest { - companion object { - const val DEBUG_MESSAGE = "debugMessage" - const val MESSAGE = "message" - } - - @DataProvider(name = "dp") - fun createData(@Suppress("UNUSED_PARAMETER") m: Method?): Array> { - return arrayOf( - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com"))), - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foobar.com?"))), - arrayOf(ModuleException(DEBUG_MESSAGE, MESSAGE)) - ) - } - - @Test(dataProvider = "dp") - fun testGetDebugMessage(e: ModuleException) { - assertThat(e::debugMessage).isEqualTo(DEBUG_MESSAGE) - } - - @Test(dataProvider = "dp") - fun testGetMessage(e: ModuleException) { - assertThat(e).hasMessage(MESSAGE) - } - - @Test - fun testSanitizeMessage() { - val apiKey = "1234567890" - var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me")) - assertThat( - e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))" - ).isNotNull().all { - contains("xxxxxxxxxx", "userID=xx", "java.io.IOException") - doesNotContain(apiKey, "me") - } - - e = ModuleException(DEBUG_MESSAGE, MESSAGE, null) - assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, null)").hasMessage(MESSAGE) - - e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException()) - assertThat(e.sanitize(apiKey), "ModuleException(debugMessage, message, IOException())").hasMessage(MESSAGE) - - e = ModuleException(DEBUG_MESSAGE, apiKey) - assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, apiKey)").isNotNull() - .doesNotContain(apiKey) - - val msg: String? = null - e = ModuleException(DEBUG_MESSAGE, msg, IOException(msg)) - assertThat(e.sanitize(apiKey).message, "ModuleException(debugMessage, msg, IOException(msg))").isNull() - - e = ModuleException(DEBUG_MESSAGE, msg, IOException("foo is $apiKey")) - assertThat( - e.sanitize(" ", apiKey, "foo").message, - "ModuleException(debugMessage, msg, IOException(foo is $apiKey))" - ).isNotNull().all { - doesNotContain(apiKey) - endsWith("xxx is xxxxxxxxxx") - } - assertThat(e.sanitize(), "exception should be unchanged").isEqualTo(e) - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt deleted file mode 100644 index d1399e0..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/PingTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * PingTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isNotEmpty -import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing -import kotlin.test.Test - -/** - * The `PingTest` class. - */ -class PingTest { - @Test - fun testPingsArray() { - assertThat(Ping.PINGS, "Ping.PINGS").isNotEmpty() - } - - @Test - fun testRandomPing() { - for (i in 0..9) { - assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing()) - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt deleted file mode 100644 index f836b0e..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * RockPaperScissorsTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.assertThat -import assertk.assertions.isEqualTo -import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw -import kotlin.test.Test - -class RockPaperScissorsTest { - @Test - fun testWinLoseOrDraw() { - assertThat(winLoseOrDraw("scissors", "paper"), "scissors vs. paper").isEqualTo("win") - assertThat(winLoseOrDraw("paper", "rock"), "paper vs. rock").isEqualTo("win") - assertThat(winLoseOrDraw("rock", "scissors"), "rock vs. scissors").isEqualTo("win") - assertThat(winLoseOrDraw("paper", "scissors"), "paper vs. scissors").isEqualTo("lose") - assertThat(winLoseOrDraw("rock", "paper"), "rock vs. paper").isEqualTo("lose") - assertThat(winLoseOrDraw("scissors", "rock"), "scissors vs. rock").isEqualTo("lose") - assertThat(winLoseOrDraw("scissors", "scissors"), "scissors vs. scissors").isEqualTo("draw") - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt deleted file mode 100644 index d96812b..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * StockQuoteTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.all -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.* -import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote -import net.thauvin.erik.mobibot.msg.ErrorMessage -import net.thauvin.erik.mobibot.msg.Message -import kotlin.test.Test - -/** - * The `StockQuoteTest` class. - */ -class StockQuoteTest : LocalProperties() { - private fun buildMatch(label: String): String { - return "${label}:[ ]+[0-9.]+".prependIndent() - } - - @Test - @Throws(ModuleException::class) - fun testGetQuote() { - val apiKey = getProperty(StockQuote.API_KEY_PROP) - try { - var symbol = "apple inc" - val messages = getQuote(symbol, apiKey) - assertThat(messages, "response not empty").isNotEmpty() - assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg).matches("Symbol: AAPL .*".toRegex()) - assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg).matches(buildMatch("Price").toRegex()) - assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg) - .matches(buildMatch("Previous").toRegex()) - assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg).matches(buildMatch("Open").toRegex()) - - symbol = "blahfoo" - assertThat(getQuote(symbol, apiKey).first(), "getQuote($symbol)").all { - isInstanceOf(ErrorMessage::class.java) - prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) - } - assertThat(getQuote("", "apikey").first(), "getQuote(empty)").all { - isInstanceOf(ErrorMessage::class.java) - prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL) - } - assertFailure { getQuote("test", "") }.isInstanceOf(ModuleException::class.java).hasNoCause() - } catch (e: ModuleException) { - // Avoid displaying api keys in CI logs - if ("true" == System.getenv("CI")) { - throw e.sanitize(apiKey) - } else { - throw e - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt deleted file mode 100644 index 66f4d22..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Weather2Test.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.all -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.* -import net.aksingh.owmjapis.api.APIException -import net.aksingh.owmjapis.core.OWM -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.Weather2.Companion.API_KEY_PROP -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 net.thauvin.erik.mobibot.msg.Message -import kotlin.test.Test - -/** - * The `Weather2Test` class. - */ -class Weather2Test : LocalProperties() { - @Test - fun testFtoC() { - val t = ftoC(32.0) - assertThat(t.second, "32 °F is 0 °C").isEqualTo(0) - } - - @Test - fun testGetCountry() { - assertThat(getCountry("foo"), "foo is not a valid country").isEqualTo(OWM.Country.UNITED_STATES) - assertThat(getCountry("fr"), "country should France").isEqualTo(OWM.Country.FRANCE) - - val country = OWM.Country.entries.toTypedArray() - repeat(3) { - val rand = country[(country.indices).random()] - assertThat(getCountry(rand.value), rand.name).isEqualTo(rand) - } - } - - @Test - fun testMphToKmh() { - val w = mphToKmh(0.62) - assertThat(w.second, "0.62 mph is 1 km/h").isEqualTo(1) - } - - @Test - @Throws(ModuleException::class) - fun testWeather() { - var query = "98204" - var messages = getWeather(query, getProperty(API_KEY_PROP)) - assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { - contains("Everett, United States") - contains("US") - } - assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("98204%2CUS") - - query = "San Francisco" - messages = getWeather(query, getProperty(API_KEY_PROP)) - assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { - contains("San Francisco") - contains("US") - } - assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("5391959") - - query = "London, GB" - messages = getWeather(query, getProperty(API_KEY_PROP)) - assertThat(messages, "getWeather($query)").index(0).prop(Message::msg).all { - contains("London, United Kingdom") - contains("GB") - } - assertThat(messages, "getWeather($query)").index(messages.size - 1).prop(Message::msg).endsWith("2643743") - - try { - query = "Foo, US" - getWeather(query, getProperty(API_KEY_PROP)) - } catch (e: ModuleException) { - assertThat(e.cause, "getWeather($query)").isNotNull().isInstanceOf(APIException::class.java) - } - - query = "test" - assertFailure { getWeather(query, "") }.isInstanceOf(ModuleException::class.java).hasNoCause() - assertFailure { getWeather(query, null) }.isInstanceOf(ModuleException::class.java).hasNoCause() - - messages = getWeather("", "apikey") - assertThat(messages, "getWeather(empty)").index(0).prop(Message::isError).isTrue() - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt deleted file mode 100644 index 855522c..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * WolframAlphaTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.hasMessage -import assertk.assertions.isInstanceOf -import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize -import net.thauvin.erik.mobibot.LocalProperties -import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram -import kotlin.test.Test - -class WolframAlphaTest : LocalProperties() { - @Test - fun testAppId() { - assertFailure { queryWolfram("1 gallon to liter", appId = "DEMO") } - .isInstanceOf(ModuleException::class.java) - .hasMessage("Error 1: Invalid appid") - - assertFailure { queryWolfram("1 gallon to liter", appId = "") } - .isInstanceOf(ModuleException::class.java) - } - - @Test(groups = ["modules", "no-ci"]) - @Throws(ModuleException::class) - fun queryWolframTest() { - val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) - try { - var query = "SFO to SEA" - assertThat(queryWolfram(query, appId = apiKey), "queryWolfram($query)").contains("miles") - - query = "SFO to LAX" - assertThat( - queryWolfram(query, WolframAlpha.METRIC, apiKey), - "queryWolfram($query)" - ).contains("kilometers") - } catch (e: ModuleException) { - // Avoid displaying api key in CI logs - if ("true" == System.getenv("CI")) { - throw e.sanitize(apiKey) - } else { - throw e - } - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt deleted file mode 100644 index 7931f33..0000000 --- a/bin/test/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * WordTimeTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.modules - -import assertk.assertThat -import assertk.assertions.endsWith -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.pircbotx.Colors -import kotlin.test.Test -import java.time.ZoneId - -/** - * The `WordTimeTest` class. - */ -class WordTimeTest { - @Test - fun testTime() { - assertThat(time(), "time()").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(""), "time()").endsWith("Los Angeles".bold()) - assertThat(time("PST"), "time(PST)").endsWith("Los Angeles".bold()) - assertThat(time("GB"), "time(GB)").endsWith("London".bold()) - assertThat(time("FR"), "time(FR)").endsWith("Paris".bold()) - assertThat(time("BLAH"), "time(BLAH)").startsWith("Unsupported") - assertThat(time("BEAT"), "time($BEATS_KEYWORD)").matches("[\\w ]+ .?@\\d{3}+.? .beats".toRegex()) - } - - @Test - fun testZones() { - COUNTRIES_MAP.filter { it.value != BEATS_KEYWORD }.forEach { - assertThat(ZoneId.of(it.value)) - } - } -} diff --git a/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt b/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt deleted file mode 100644 index 32a0495..0000000 --- a/bin/test/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * MessageTest.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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.msg - -import assertk.all -import assertk.assertThat -import assertk.assertions.isFalse -import assertk.assertions.isTrue -import assertk.assertions.prop -import kotlin.test.Test - -class MessageTest { - @Test - fun testConstructor() { - var msg = Message("foo") - - msg.isError = true - assertThat(msg.isNotice, "message is notice").isTrue() - - msg = Message("foo", isError = true) - assertThat(msg.isNotice, "message is notice too").isTrue() - } - - @Test - fun testErrorMessage() { - val msg = ErrorMessage("foo") - assertThat(msg).all { - prop(Message::isError).isTrue() - prop(Message::isNotice).isTrue() - prop(Message::isPrivate).isFalse() - } - } - - @Test - fun testIsError() { - val msg = Message("foo") - msg.isError = true - assertThat(msg).all { - prop(Message::isError).isTrue() - prop(Message::isNotice).isTrue() - prop(Message::isPrivate).isFalse() - } - msg.isError = false - assertThat(msg).all { - prop(Message::isError).isFalse() - prop(Message::isNotice).isTrue() - prop(Message::isPrivate).isFalse() - } - } - - @Test - fun testNoticeMessage() { - val msg = NoticeMessage("food") - assertThat(msg).all { - prop(Message::isError).isFalse() - prop(Message::isNotice).isTrue() - prop(Message::isPrivate).isFalse() - } - } - - @Test - fun testPrivateMessage() { - val msg = PrivateMessage("foo") - assertThat(msg).all { - prop(Message::isPrivate).isTrue() - prop(Message::isError).isFalse() - prop(Message::isNotice).isFalse() - } - } - - @Test - fun testPublicMessage() { - val msg = PublicMessage("foo") - assertThat(msg).all { - prop(Message::isError).isFalse() - prop(Message::isNotice).isFalse() - prop(Message::isPrivate).isFalse() - } - } -} diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 88634ae..01f14f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,11 +14,11 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231110234054" + const val VERSION = "0.8.0-rc+20231111131146" @JvmField val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1699688454937L), ZoneId.systemDefault() + Instant.ofEpochMilli(1699737106723L), ZoneId.systemDefault() ) const val WEBSITE = "https://www.mobitopia.org/mobibot/" diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml deleted file mode 100644 index 16644cc..0000000 --- a/src/main/resources/log4j2.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a40d20458bbd889ff195cb5108640cdcfa993ff8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 12 Nov 2023 14:19:38 -0800 Subject: [PATCH 756/858] Updated copyright --- .github/workflows/{gradle.yml => bld.yml} | 0 .idea/copyright/BSD_3.xml | 2 +- .idea/libraries/bld.xml | 3 +- .idea/misc.xml | 11 ++++++ .../java/net/thauvin/erik/MobibotBuild.java | 3 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 35 +++++++++++++++++-- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../mobibot/commands/links/LinksManager.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../mobibot/commands/seen/NickComparator.kt | 2 +- .../erik/mobibot/commands/seen/Seen.kt | 2 +- .../erik/mobibot/commands/seen/SeenNick.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellManager.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 2 +- .../erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/WolframAlpha.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 2 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 2 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 +- .../net/thauvin/erik/mobibot/DisableOnCi.kt | 2 +- .../erik/mobibot/DisableOnCiCondition.kt | 2 +- .../erik/mobibot/ExceptionSanitizer.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 2 +- .../erik/mobibot/commands/RecapTest.kt | 2 +- .../commands/links/LinksManagerTest.kt | 2 +- .../erik/mobibot/commands/links/ViewTest.kt | 2 +- .../erik/mobibot/commands/seen/SeenTest.kt | 2 +- .../mobibot/commands/tell/TellMessageTest.kt | 2 +- .../commands/tell/TellMessagesMgrTest.kt | 2 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 2 +- .../erik/mobibot/entries/EntryLinkTest.kt | 2 +- .../erik/mobibot/entries/FeedMgrTest.kt | 2 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 2 +- .../erik/mobibot/modules/ChatGptTest.kt | 2 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- .../mobibot/modules/CurrencyConverterTest.kt | 2 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 2 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../erik/mobibot/modules/LookupTest.kt | 2 +- .../erik/mobibot/modules/MastodonTest.kt | 2 +- .../mobibot/modules/ModuleExceptionTest.kt | 2 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 2 +- .../mobibot/modules/RockPaperScissorsTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/Weather2Test.kt | 2 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 2 +- .../erik/mobibot/modules/WordTimeTest.kt | 2 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 2 +- 103 files changed, 146 insertions(+), 102 deletions(-) rename .github/workflows/{gradle.yml => bld.yml} (100%) diff --git a/.github/workflows/gradle.yml b/.github/workflows/bld.yml similarity index 100% rename from .github/workflows/gradle.yml rename to .github/workflows/bld.yml diff --git a/.idea/copyright/BSD_3.xml b/.idea/copyright/BSD_3.xml index 275eca9..3c57002 100644 --- a/.idea/copyright/BSD_3.xml +++ b/.idea/copyright/BSD_3.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index cf75013..ca84ff0 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -6,6 +6,7 @@ + @@ -14,4 +15,4 @@ - \ No newline at end of file + diff --git a/.idea/misc.xml b/.idea/misc.xml index e853f87..8232032 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,6 +6,17 @@ + diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 9b6b32a..e7dbde3 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -1,7 +1,7 @@ /* * MobibotBuild.java * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -89,6 +89,7 @@ public class MobibotBuild extends Project { .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) + // Misc. .include(dependency("com.rometools", "rome", "2.1.0")) .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index c6f16cb..2c5f05d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index ea89f4c..98ef74a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index 371b523..d82f011 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 47dd7c2..ac4d6af 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index ac01b0a..7cb5aed 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 01f14f3..7a3d41c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -1,3 +1,34 @@ +/* + * ReleaseInfo.kt + * + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * + * 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. + */ + /* * This file is automatically generated * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -14,11 +45,11 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231111131146" + const val VERSION = "0.8.0-rc+20231111131825" @JvmField val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1699737106723L), ZoneId.systemDefault() + Instant.ofEpochMilli(1699737505341L), ZoneId.systemDefault() ) const val WEBSITE = "https://www.mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index d69c0c5..e4760d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index e0b091a..5f79472 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 9084660..038e378 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 18a8b1d..9608ca8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index c1708a9..f271bfa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 8d2b154..d083c10 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 8ee8c4f..ed0b6ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index f2a13ba..ec7823b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 1aaefd7..b2293b0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 0f492c9..20a6635 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 41b2c4b..85a03ab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 0159017..77154c7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 1f89982..7f76d35 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 4a881a7..33d6fef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index a8300f0..802bbde 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 90d8bb3..1443d44 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index 2d7add6..fba6b99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,7 +1,7 @@ /* * LinksManager.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index dca99cf..ff4278d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index c59cad9..1662857 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 1626a58..825e374 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index 2a3856b..cfd2c27 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,7 +1,7 @@ /* * NickComparator.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 83083fd..c9ee0f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,7 +1,7 @@ /* * Seen.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 1445f9f..7924977 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,7 +1,7 @@ /* * SeenNick.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index e9d18ae..061ca6a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index 229bc5f..b65a4da 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,7 +1,7 @@ /* * TellManager.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index cc48d59..d17fbb5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index 15506e0..e8676ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 6e58fd9..9c09626 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index c0cc2a8..e18d692 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 8098bbf..4a69446 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index e7017ab..f786cb2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,7 +1,7 @@ /* * FeedsManager.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 8dcf6d8..8c8e736 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index 9003783..b7aae28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index 09791a8..bd92332 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -1,7 +1,7 @@ /* * ChatGpt.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 4464732..4614a4c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 954f4d1..da0efd8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 84f2280..8420fb1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index a9b15a5..f426d1e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 91b9ad2..2760fa7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index eecc55e..9ab2ead 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 1141c0e..3be3a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,7 +1,7 @@ /* * Mastodon.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 9a0e641..a7416c2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 86b311a..944dbc1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index d224568..a299d8d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 6b591cd..dcae5e7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt index 0c64465..8799279 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt @@ -1,7 +1,7 @@ /* * War.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 83eba95..80a06fa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index 8d9c4e6..a72efab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,7 +1,7 @@ /* * WolframAlpha.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 0c379b3..18072bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index b9f5212..0607936 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 84e3a1e..d41ec8c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 2be52aa..037d504 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 8d34d28..b424fdf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 9e67ecb..9c5e088 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index 3fb3874..91f2dd9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,7 +1,7 @@ /* * SocialManager.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index 47a33d9..b594670 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,7 +1,7 @@ /* * SocialModule.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index e779a3c..3fd315e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,7 +1,7 @@ /* * SocialTimer.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 7f65548..9dd6034 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt index 01d98ca..2fbcb71 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -1,7 +1,7 @@ /* * DisableOnCi.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt index 24f1ca0..1d9baf4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt @@ -1,7 +1,7 @@ /* * DisableOnCiCondition.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index b02188e..a3994ec 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index cfc3d4e..46f9368 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index 95b6c08..c039d74 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index cf9d59a..3b81950 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index a024cff..682b350 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 33a411f..99ba951 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,7 +1,7 @@ /* * InfoTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 34dce0e..b6fbbff 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index eea09db..fa2bb70 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,7 +1,7 @@ /* * LinksManagerTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index b5028e3..e315891 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 331f97c..52810eb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,7 +1,7 @@ /* * SeenTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index f2c6f35..b28c45a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index 9ab1bce..e3c9c1f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 8e9bd6f..0ad26b5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 495ed15..5028e6e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index de6075d..b56e6ed 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 6df5616..3bd4c0f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 00fa43f..dc1133a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -1,7 +1,7 @@ /* * ChatGptTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 9847080..3eaa4c5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 57affe5..a177715 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 0aaacb9..8bafede 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index b0c154a..93c2560 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 9b8dcb3..0af8e75 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index e67c8ad..a3fc226 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index d189d75..f4b5e99 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,7 +1,7 @@ /* * MastodonTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index 9dda5b3..efc267a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index a6ff707..d1399e0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index fb8865b..f836b0e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index ae8cff2..d96812b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index e135866..66f4d22 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 417f9fc..5faffc2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,7 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 5c8cc84..35368b2 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index 477c1a4..32a0495 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,7 +1,7 @@ /* * MessageTest.kt * - * Copyright 2021-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: From 44c1e8db3e98a238d462721f27ce82330acab99a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 12 Nov 2023 16:06:40 -0800 Subject: [PATCH 757/858] Fixed bld-ci badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fbcacc2..041f0dc 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Kotlin](https://img.shields.io/badge/kotlin-1.9.20-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) -[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) +[![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) Some very basic instructions: From e34ea334d66885c7e2a06df7103fc58247282496 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 25 Nov 2023 18:08:13 -0800 Subject: [PATCH 758/858] Added detekt extension --- README.md | 2 +- config/detekt/baseline.xml | 31 ++++++------- lib/bld/bld-wrapper.properties | 4 +- release_info.txt | 1 + .../java/net/thauvin/erik/MobibotBuild.java | 43 +++++++++++++------ .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 36 ++-------------- .../thauvin/erik/mobibot/commands/Ignore.kt | 1 - .../net/thauvin/erik/mobibot/DisableOnCi.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 3 -- .../net/thauvin/erik/mobibot/UtilsTest.kt | 3 -- .../erik/mobibot/commands/seen/SeenTest.kt | 14 +++--- .../mobibot/commands/tell/TellMessageTest.kt | 3 -- .../erik/mobibot/entries/EntryLinkTest.kt | 7 --- .../thauvin/erik/mobibot/modules/CalcTest.kt | 3 -- .../erik/mobibot/modules/ChatGptTest.kt | 4 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 3 -- .../mobibot/modules/CurrencyConverterTest.kt | 4 -- .../erik/mobibot/modules/GoogleSearchTest.kt | 7 +-- .../thauvin/erik/mobibot/modules/JokeTest.kt | 3 -- .../erik/mobibot/modules/LookupTest.kt | 3 -- .../mobibot/modules/ModuleExceptionTest.kt | 3 -- .../thauvin/erik/mobibot/modules/PingTest.kt | 3 -- .../erik/mobibot/modules/StockQuoteTest.kt | 3 -- .../erik/mobibot/modules/Weather2Test.kt | 3 -- .../erik/mobibot/modules/WolframAlphaTest.kt | 4 +- .../erik/mobibot/modules/WordTimeTest.kt | 3 -- 27 files changed, 68 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index 041f0dc..fe1920f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.20-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-7f52ff.svg)](https://kotlinlang.org) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 5c6be73..641e97c 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -1,15 +1,15 @@ - + - + CyclomaticComplexMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) - CyclomaticComplexMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> + CyclomaticComplexMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> LongMethod:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) - LongMethod:Mobibot.kt$Mobibot.Companion$@JvmStatic @Throws(Exception::class) fun main(args: Array<String>) - LongMethod:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> - LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> + LongMethod:Mobibot.kt$Mobibot.Companion$@JvmStatic @Throws(Exception::class) fun main(args: Array<String>) + LongMethod:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> + LongMethod:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> LongParameterList:Comment.kt$Comment$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent ) - LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String ) + LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String ) MagicNumber:ChatGpt.kt$ChatGpt$400 MagicNumber:ChatGpt.kt$ChatGpt.Companion$200 MagicNumber:ChatGpt.kt$ChatGpt.Companion$429 @@ -52,22 +52,22 @@ NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?) NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message - NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>) + NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>) NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML) NestedBlockDepth:GoogleSearch.kt$GoogleSearch$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) - NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> + NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> NestedBlockDepth:LinksManager.kt$LinksManager$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Mastodon.kt$Mastodon.Companion$@JvmStatic @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String NestedBlockDepth:Posting.kt$Posting$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) NestedBlockDepth:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) - NestedBlockDepth:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> + NestedBlockDepth:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> NestedBlockDepth:Tell.kt$Tell$fun send(event: GenericUserEvent) NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun loadSerialData(file: String, default: Any, logger: Logger, description: String): Any NestedBlockDepth:Utils.kt$Utils$@JvmStatic fun saveSerialData(file: String, data: Any, logger: Logger, description: String) NestedBlockDepth:Weather2.kt$Weather2$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) - NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> + NestedBlockDepth:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> ReturnCount:Addons.kt$Addons$fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException @@ -76,18 +76,19 @@ SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String - ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> - ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message> + ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> + ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message> ThrowsCount:Mastodon.kt$Mastodon.Companion$@JvmStatic @Throws(ModuleException::class) fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String - ThrowsCount:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> + ThrowsCount:StockQuote.kt$StockQuote.Companion$@JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message> ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject - ThrowsCount:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> + ThrowsCount:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> ThrowsCount:WolframAlpha.kt$WolframAlpha.Companion$@JvmStatic @Throws(ModuleException::class) fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException TooManyFunctions:EntryLink.kt$EntryLink : Serializable TooManyFunctions:Mobibot.kt$Mobibot : ListenerAdapter TooManyFunctions:Tell.kt$Tell : AbstractCommand + UtilityClassWithPublicConstructor:LocalProperties.kt$LocalProperties WildcardImport:AddonsTest.kt$import net.thauvin.erik.mobibot.modules.* WildcardImport:EntryLinkTest.kt$import assertk.assertions.* WildcardImport:FeedMgrTest.kt$import assertk.assertions.* diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 8375666..e219f09 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,9 +1,9 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.1 -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.3-SNAPSHOT +bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.3 bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0-SNAPSHOT -bld.extensions-pmd=com.uwyn.rife2:bld-testng:0.9.2 +bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= diff --git a/release_info.txt b/release_info.txt index f5efde2..b00e5ba 100644 --- a/release_info.txt +++ b/release_info.txt @@ -17,6 +17,7 @@ object {{v className/}} { const val VERSION = "{{v version/}}" @JvmField + @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( Instant.ofEpochMilli({{v epoch/}}L), ZoneId.systemDefault() ) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index e7dbde3..d1c489a 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -35,9 +35,10 @@ import rife.bld.BuildCommand; import rife.bld.Project; import rife.bld.dependencies.Repository; import rife.bld.extension.CompileKotlinOperation; -import rife.bld.extension.CompileKotlinOptions; +import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; +import rife.bld.operations.exceptions.ExitStatusException; import rife.tools.FileUtils; import rife.tools.exceptions.FileUtilsErrorException; @@ -68,12 +69,13 @@ public class MobibotBuild extends Project { autoDownloadPurge = true; repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io")); - var log4j = version(2, 21, 1); + var log4j = version(2, 22, 0); + var kotlin = version(1, 9, 21); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) - .include(dependency("org.apache.commons", "commons-lang3", "3.13.0")) + .include(dependency("org.apache.commons", "commons-lang3", "3.14.0")) .include(dependency("org.apache.commons", "commons-text", "1.11.0")) .include(dependency("commons-codec", "commons-codec", "1.16.0")) .include(dependency("commons-net", "commons-net", "3.10.0")) @@ -81,7 +83,10 @@ public class MobibotBuild extends Project { .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "32.1.3-jre")) // Kotlin - .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", version(1, 9, 20))) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) + .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging @@ -97,13 +102,13 @@ public class MobibotBuild extends Project { .include(dependency("org.json", "json", "20231013")) .include(dependency("org.jsoup", "jsoup", "1.16.2")) // Thauvin - .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.1")) - .include(dependency("net.thauvin.erik", "jokeapi", "0.9.0")) - .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.0")) + .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.2-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "jokeapi", "0.9.1-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.1-SNAPSHOT")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 27, 0))) - .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 20))) + .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 21))) .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))); @@ -132,11 +137,6 @@ public class MobibotBuild extends Project { releaseInfo(); new CompileKotlinOperation() .fromProject(this) - .compileOptions( - new CompileKotlinOptions() - .jdkRelease(javaRelease) - .verbose(true) - ) .execute(); } @@ -150,6 +150,23 @@ public class MobibotBuild extends Project { FileUtils.copy(new File(buildDistDirectory(), jarFileName()), new File(deploy, "mobibot.jar")); } + @BuildCommand(summary = "Checks source with Detekt") + public void detekt() throws ExitStatusException, IOException, InterruptedException { + new DetektOperation() + .fromProject(this) + .baseline("config/detekt/baseline.xml") + .execute(); + } + + @BuildCommand(value = "detekt-baseline", summary = "Creates the Detekt baseline") + public void detektBaseline() throws ExitStatusException, IOException, InterruptedException { + new DetektOperation() + .fromProject(this) + .baseline("config/detekt/baseline.xml") + .createBaseline(true) + .execute(); + } + @BuildCommand(summary = "Generates JaCoCo Reports") public void jacoco() throws IOException { new JacocoReportOperation() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 98ef74a..d037e44 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -63,7 +63,7 @@ object Constants { * User-Agent */ const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36" + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 7a3d41c..fbdfe48 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -1,34 +1,3 @@ -/* - * ReleaseInfo.kt - * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) - * - * 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. - */ - /* * This file is automatically generated * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -45,11 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231111131825" + const val VERSION = "0.8.0-rc+20231125180722" @JvmField + @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1699737505341L), ZoneId.systemDefault() + Instant.ofEpochMilli(1700964443060L), ZoneId.systemDefault() ) const val WEBSITE = "https://www.mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index d083c10..2109693 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -143,5 +143,4 @@ class Ignore : AbstractCommand() { ignored.addAll(value.split(LinksManager.TAG_MATCH)) } } - } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt index 2fbcb71..22b0146 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -41,4 +41,4 @@ import org.junit.jupiter.api.extension.ExtendWith @Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @ExtendWith(DisableOnCiCondition::class) -annotation class DisabledOnCi +annotation class DisableOnCi diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 46f9368..bf4408f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -42,9 +42,6 @@ import java.net.MalformedURLException import java.net.UnknownHostException import kotlin.test.Test -/** - * The `FeedReader Test` class. - */ class FeedReaderTest { @Test fun readFeedTest() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 682b350..a65b3a5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -68,9 +68,6 @@ import java.time.LocalDateTime import java.util.* import kotlin.test.Test -/** - * The `Utils Test` class. - */ class UtilsTest { private val ascii = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 52810eb..62a1db7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -48,18 +48,18 @@ class SeenTest { seen.clear() assertThat(seen::seenNicks).isEmpty() seen.load() - assertThat(seen::seenNicks).key(nick).isNotNull() + assertThat(seen::seenNicks).key(NICK).isNotNull() } @Test @Order(2) fun addTest() { - val last = seen.seenNicks[nick]?.lastSeen - seen.add(nick.lowercase()) + val last = seen.seenNicks[NICK]?.lastSeen + seen.add(NICK.lowercase()) assertThat(seen).all { prop(Seen::seenNicks).size().isEqualTo(1) - prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last) - prop(Seen::seenNicks).key(nick).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(nick.lowercase()) + prop(Seen::seenNicks).key(NICK).isNotNull().prop(SeenNick::lastSeen).isNotEqualTo(last) + prop(Seen::seenNicks).key(NICK).isNotNull().prop(SeenNick::nick).isNotNull().isEqualTo(NICK.lowercase()) } } @@ -75,12 +75,12 @@ class SeenTest { companion object { private val tmpFile = kotlin.io.path.createTempFile(suffix = ".ser") private val seen = Seen(tmpFile.toAbsolutePath().toString()) - private const val nick = "ErikT" + private const val NICK = "ErikT" @JvmStatic @BeforeClass fun beforeClass() { - seen.add(nick) + seen.add(NICK) assertThat(tmpFile.fileSize(), "tmpFile.size").isGreaterThan(0) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index b28c45a..69bc2e9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -41,9 +41,6 @@ import java.time.LocalDateTime import java.time.temporal.Temporal import kotlin.test.Test -/** - * The `TellMessageTest` class. - */ class TellMessageTest { private fun isValidDate(date: Temporal): Boolean { return Duration.between(date, LocalDateTime.now()).toMinutes() < 1 diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 5028e6e..3a3bdda 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -39,13 +39,6 @@ import java.security.SecureRandom import java.util.* import kotlin.test.Test -/** - * The `EntryUtilsTest` class. - * - * @author [Erik C. Thauvin](https://erik.thauvin.net/) - * @created 2019-04-19 - * @since 1.0 - */ class EntryLinkTest { private val entryLink = EntryLink( "https://www.mobitopia.org/", "Mobitopia", "Skynx", "JimH", "#mobitopia", diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 3bd4c0f..04afe8c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -39,9 +39,6 @@ import net.thauvin.erik.mobibot.Utils.bold import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate import kotlin.test.Test -/** - * The `CalcTest` class. - */ class CalcTest { @Test fun testCalculate() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index dc1133a..603c353 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -35,7 +35,7 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasNoCause import assertk.assertions.isInstanceOf -import net.thauvin.erik.mobibot.DisabledOnCi +import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.LocalProperties import kotlin.test.Test @@ -48,7 +48,7 @@ class ChatGptTest : LocalProperties() { } @Test - @DisabledOnCi + @DisableOnCi fun testChat() { val apiKey = getProperty(ChatGpt.API_KEY_PROP) assertThat( diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 3eaa4c5..10b3a43 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -41,9 +41,6 @@ import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies import kotlin.test.Test -/** - * The `CryptoPricesTest` class. - */ class CryptoPricesTest { init { loadCurrencies() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index a177715..8ab64c0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -44,10 +44,6 @@ import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage import kotlin.test.Test - -/** - * The `CurrencyConvertTest` class. - */ class CurrencyConverterTest : LocalProperties() { init { val apiKey = getProperty(CurrencyConverter.API_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 93c2560..4942266 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -34,7 +34,7 @@ import assertk.all import assertk.assertFailure import assertk.assertThat import assertk.assertions.* -import net.thauvin.erik.mobibot.DisabledOnCi +import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle @@ -42,9 +42,6 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import kotlin.test.Test -/** - * The `GoogleSearchTest` class. - */ class GoogleSearchTest : LocalProperties() { @Test fun testAPIKeys() { @@ -65,7 +62,7 @@ class GoogleSearchTest : LocalProperties() { } @Test - @DisabledOnCi + @DisableOnCi @Throws(ModuleException::class) fun testSearchGoogle() { val apiKey = getProperty(GoogleSearch.API_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 0af8e75..441d435 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -38,9 +38,6 @@ import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage import kotlin.test.Test -/** - * The `JokeTest` class. - */ class JokeTest { @Test @Throws(ModuleException::class) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index a3fc226..8699c0f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -37,9 +37,6 @@ import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois import kotlin.test.Test -/** - * The `Lookup Test` class. - */ class LookupTest { @Test @Throws(Exception::class) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index efc267a..416b3ab 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -39,9 +39,6 @@ import org.junit.jupiter.params.provider.MethodSource import java.io.IOException import kotlin.test.Test -/** - * The `ModuleExceptionTest` class. - */ class ModuleExceptionTest { companion object { const val DEBUG_MESSAGE = "debugMessage" diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index d1399e0..bbf9eb1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -36,9 +36,6 @@ import assertk.assertions.isNotEmpty import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing import kotlin.test.Test -/** - * The `PingTest` class. - */ class PingTest { @Test fun testPingsArray() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index d96812b..0816b17 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -41,9 +41,6 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import kotlin.test.Test -/** - * The `StockQuoteTest` class. - */ class StockQuoteTest : LocalProperties() { private fun buildMatch(label: String): String { return "${label}:[ ]+[0-9.]+".prependIndent() diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 66f4d22..7ae2b83 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -45,9 +45,6 @@ import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh import net.thauvin.erik.mobibot.msg.Message import kotlin.test.Test -/** - * The `Weather2Test` class. - */ class Weather2Test : LocalProperties() { @Test fun testFtoC() { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 5faffc2..3bfe0d1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -36,7 +36,7 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.hasMessage import assertk.assertions.isInstanceOf -import net.thauvin.erik.mobibot.DisabledOnCi +import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize import net.thauvin.erik.mobibot.LocalProperties import net.thauvin.erik.mobibot.modules.WolframAlpha.Companion.queryWolfram @@ -54,7 +54,7 @@ class WolframAlphaTest : LocalProperties() { } @Test - @DisabledOnCi + @DisableOnCi @Throws(ModuleException::class) fun queryWolframTest() { val apiKey = getProperty(WolframAlpha.APPID_KEY_PROP) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 35368b2..ae40e3a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -42,9 +42,6 @@ import org.pircbotx.Colors import java.time.ZoneId import kotlin.test.Test -/** - * The `WordTimeTest` class. - */ class WordTimeTest { @Test fun testTime() { From 3a773f7d46accd1cdea0543c8b538f6fe5180d84 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 25 Nov 2023 18:14:41 -0800 Subject: [PATCH 759/858] Added snapshots repository --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index d1c489a..d8f52e4 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -50,8 +50,7 @@ import java.util.ArrayList; import java.util.List; import java.util.jar.Attributes; -import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; -import static rife.bld.dependencies.Repository.MAVEN_LOCAL; +import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; import static rife.bld.dependencies.Scope.test; @@ -67,7 +66,12 @@ public class MobibotBuild extends Project { javaRelease = 17; downloadSources = true; autoDownloadPurge = true; - repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io")); + repositories = List.of( + MAVEN_LOCAL, + SONATYPE_SNAPSHOTS_LEGACY, + MAVEN_CENTRAL, + new Repository("https://jitpack.io") + ); var log4j = version(2, 22, 0); var kotlin = version(1, 9, 21); From 8412a8c26dbe8d78348eb28b2e9365f3916e101b Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 26 Nov 2023 00:43:44 -0800 Subject: [PATCH 760/858] Removed snapshot repository --- .idea/misc.xml | 2 ++ src/bld/java/net/thauvin/erik/MobibotBuild.java | 16 +++++++--------- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 8232032..26293d3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,6 +4,8 @@ + +

  17. Apache Commons Net
  18. CryptoPrice
  19. exp4j
  20. +
  21. Google Vertex AI
  22. JokeAPI
  23. jsoup
  24. kotlinx-cli
  25. @@ -80,9 +81,10 @@
  26. Performing Google searches
    mobibot: google mobitopia on irc
  27. -
  28. Getting answers from Wolfram Alpha and ChatGPT +
  29. Getting answers from Wolfram Alpha, ChatGPT and Google Gemini
    mobibot: wolfram days until christmas
    mobibot: chatgpt explain quantum computing in simple terms
    +
    mobibot: gemini what are all the colors in a rainbow?
  30. Displaying weather information
    mobibot: weather san francisco
    From a10104dd9dc889841a92b5a4fb38d0fb34a97924 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 17 Dec 2023 22:11:56 -0800 Subject: [PATCH 762/858] Added website link to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index fe1920f..b360d96 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,5 @@ Some very basic instructions: # launch /usr/bin/nohup java -jar mobibot.jar & ``` + +For a listing of features, see the [website](https://mobitopia.org/mobibot/). From 356f62990d191a21b85b2dbce589dc7e4287f155 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 18 Dec 2023 14:14:08 -0800 Subject: [PATCH 763/858] Added support for ChatGPT 3.5 Turbo --- properties/mobibot.properties | 1 + .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 +-- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 29 +++++++++---------- .../thauvin/erik/mobibot/modules/Gemini.kt | 14 ++++----- .../erik/mobibot/modules/ChatGptTest.kt | 3 +- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/properties/mobibot.properties b/properties/mobibot.properties index bc31e8a..a7526cc 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -87,6 +87,7 @@ disabled-modules=mastodon # gcloud config set project PROJECT_ID # gcloud auth login LOGIN # gcloud auth application-default login +# #gemini-project-id= #gemini-location=us-west1 #gemini-max-tokens=1024 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index f75b334..f805722 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231217213124" + const val VERSION = "0.8.0-rc+20231218140603" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1702877484912L), ZoneId.systemDefault() + Instant.ofEpochMilli(1702937164085L), ZoneId.systemDefault() ) const val WEBSITE = "https://www.mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index bd92332..e2ad717 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -34,10 +34,8 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Constants import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage -import org.apache.commons.text.WordUtils import org.json.JSONException import org.json.JSONObject -import org.json.JSONWriter import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -48,7 +46,7 @@ import java.net.http.HttpRequest import java.net.http.HttpResponse class ChatGpt : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) + val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) override val name = CHATGPT_NAME @@ -60,7 +58,7 @@ class ChatGpt : AbstractModule() { properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() ) if (answer.isNotBlank()) { - event.sendMessage(WordUtils.wrap(answer, 400)) + event.sendMessage(answer) } else { event.respond("$name is stumped.") } @@ -95,7 +93,7 @@ class ChatGpt : AbstractModule() { const val MAX_TOKENS_PROP = "chatgpt-max-tokens" // ChatGPT API URL - private const val API_URL = "https://api.openai.com/v1/completions" + private const val API_URL = "https://api.openai.com/v1/chat/completions" // ChatGPT command private const val CHATGPT_CMD = "chatgpt" @@ -105,7 +103,7 @@ class ChatGpt : AbstractModule() { @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String { if (!apiKey.isNullOrEmpty()) { - val prompt = JSONWriter.valueToString("Q:$query\nA:") + val content = query.replace("\"", "\\\"") val request = HttpRequest.newBuilder() .uri(URI.create(API_URL)) .header("Content-Type", "application/json") @@ -114,14 +112,15 @@ class ChatGpt : AbstractModule() { .POST( HttpRequest.BodyPublishers.ofString( """{ - "model": "text-davinci-003", - "prompt": $prompt, - "temperature": 0, - "max_tokens": $maxTokens, - "top_p": 1, - "frequency_penalty": 0, - "presence_penalty": 0 - }""".trimIndent() + "model": "gpt-3.5-turbo-1106", + "max_tokens": $maxTokens, + "messages": [ + { + "role": "user", + "content": "$content" + } + ] + }""".trimIndent() ) ) .build() @@ -131,7 +130,7 @@ class ChatGpt : AbstractModule() { try { val jsonResponse = JSONObject(response.body()) val choices = jsonResponse.getJSONArray("choices") - return choices.getJSONObject(0).getString("text").trim() + return choices.getJSONObject(0).getJSONObject("message").getString("content").trim() } catch (e: JSONException) { throw ModuleException( "$CHATGPT_CMD($query): JSON", diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt index 2f7c842..6f9b5ff 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt @@ -1,16 +1,12 @@ package net.thauvin.erik.mobibot.modules -import com.google.auth.Credentials import com.google.cloud.vertexai.VertexAI -import com.google.cloud.vertexai.api.GenerateContentResponse import com.google.cloud.vertexai.api.GenerationConfig import com.google.cloud.vertexai.generativeai.preview.ChatSession import com.google.cloud.vertexai.generativeai.preview.GenerativeModel import com.google.cloud.vertexai.generativeai.preview.ResponseHandler import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage -import okio.IOException -import org.apache.commons.text.WordUtils import org.pircbotx.hooks.types.GenericMessageEvent import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -86,7 +82,7 @@ class Gemini : AbstractModule() { val session = ChatSession(model) val response = session.sendMessage(query) - return ResponseHandler.getText(response); + return ResponseHandler.getText(response) } } catch (e: Exception) { throw ModuleException( @@ -96,7 +92,7 @@ class Gemini : AbstractModule() { ) } } else { - throw ModuleException("${GEMINI_CMD}($query)", "No ${GEMINI_NAME} Project ID or Location specified.") + throw ModuleException("${GEMINI_CMD}($query)", "No $GEMINI_NAME Project ID or Location specified.") } } } @@ -105,10 +101,10 @@ class Gemini : AbstractModule() { commands.add(GEMINI_CMD) with(help) { add("To get answers from $name:") - add(Utils.helpFormat("%c ${GEMINI_CMD} ")) + add(Utils.helpFormat("%c $GEMINI_CMD ")) add("For example:") - add(Utils.helpFormat("%c ${GEMINI_CMD} explain quantum computing in simple terms")) - add(Utils.helpFormat("%c ${GEMINI_CMD} how do I make an HTTP request in Javascript?")) + add(Utils.helpFormat("%c $GEMINI_CMD explain quantum computing in simple terms")) + add(Utils.helpFormat("%c $GEMINI_CMD how do I make an HTTP request in Javascript?")) } initProperties(PROJECT_ID_PROP, LOCATION_PROPR, MAX_TOKENS_PROP) } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 603c353..7180c13 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -54,11 +54,12 @@ class ChatGptTest : LocalProperties() { assertThat( ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) ).contains("XMLHttpRequest") + assertThat( ChatGpt.chat("how do I encode a URL in java?", apiKey, 60) ).contains("URLEncoder") - assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, 0) } + assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, -1) } .isInstanceOf(ModuleException::class.java) } } From c684332b9d804fb3b46de120a9f4b9678098f351 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 24 Dec 2023 12:11:11 -0800 Subject: [PATCH 764/858] Bumped to Kotlin 1.9.22 --- release_info.txt | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 6 +++--- src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/release_info.txt b/release_info.txt index b00e5ba..d2c33ac 100644 --- a/release_info.txt +++ b/release_info.txt @@ -22,7 +22,7 @@ object {{v className/}} { Instant.ofEpochMilli({{v epoch/}}L), ZoneId.systemDefault() ) - const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val WEBSITE = "https://mobitopia.org/mobibot/" const val AUTHOR = "Erik C. Thauvin" const val AUTHOR_URL = "https://erik.thauvin.net/" } diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index d6a294f..ba67d1d 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -71,7 +71,7 @@ public class MobibotBuild extends Project { ); var log4j = version(2, 22, 0); - var kotlin = version(1, 9, 21); + var kotlin = version(1, 9, 22); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) @@ -82,7 +82,7 @@ public class MobibotBuild extends Project { .include(dependency("commons-net", "commons-net", "3.10.0")) // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) - .include(dependency("com.google.guava", "guava", "32.1.3-jre")) + .include(dependency("com.google.guava", "guava", "33.0.0-jre")) .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 1, 0))) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) @@ -109,7 +109,7 @@ public class MobibotBuild extends Project { .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 0))) - .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 21))) + .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))); diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index f805722..f499559 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,15 +14,15 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231218140603" + const val VERSION = "0.8.0-rc+20231224114859" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1702937164085L), ZoneId.systemDefault() + Instant.ofEpochMilli(1703447340121L), ZoneId.systemDefault() ) - const val WEBSITE = "https://www.mobitopia.org/mobibot/" + const val WEBSITE = "https://mobitopia.org/mobibot/" const val AUTHOR = "Erik C. Thauvin" const val AUTHOR_URL = "https://erik.thauvin.net/" } From 3c4e76e5b916bb9bcc727776c90737f0722d4706 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 16 Jan 2024 10:21:27 -0800 Subject: [PATCH 765/858] Updated copyright --- .idea/intellij-javadocs-4.0.1.xml | 204 ++++++++++++++++++ .../java/net/thauvin/erik/MobibotBuild.java | 10 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../mobibot/commands/links/LinksManager.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../mobibot/commands/seen/NickComparator.kt | 2 +- .../erik/mobibot/commands/seen/Seen.kt | 2 +- .../erik/mobibot/commands/seen/SeenNick.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellManager.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 2 +- .../erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../thauvin/erik/mobibot/modules/Gemini.kt | 33 ++- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/WolframAlpha.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 2 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 2 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 +- .../net/thauvin/erik/mobibot/DisableOnCi.kt | 2 +- .../erik/mobibot/DisableOnCiCondition.kt | 2 +- .../erik/mobibot/ExceptionSanitizer.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 2 +- .../erik/mobibot/commands/RecapTest.kt | 2 +- .../commands/links/LinksManagerTest.kt | 2 +- .../erik/mobibot/commands/links/ViewTest.kt | 2 +- .../erik/mobibot/commands/seen/SeenTest.kt | 2 +- .../mobibot/commands/tell/TellMessageTest.kt | 2 +- .../commands/tell/TellMessagesMgrTest.kt | 2 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 2 +- .../erik/mobibot/entries/EntryLinkTest.kt | 2 +- .../erik/mobibot/entries/FeedMgrTest.kt | 2 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 2 +- .../erik/mobibot/modules/ChatGptTest.kt | 2 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- .../mobibot/modules/CurrencyConverterTest.kt | 2 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 2 +- .../erik/mobibot/modules/GeminiTest.kt | 4 +- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../erik/mobibot/modules/LookupTest.kt | 6 +- .../erik/mobibot/modules/MastodonTest.kt | 2 +- .../mobibot/modules/ModuleExceptionTest.kt | 2 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 2 +- .../mobibot/modules/RockPaperScissorsTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/Weather2Test.kt | 2 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 2 +- .../erik/mobibot/modules/WordTimeTest.kt | 2 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 2 +- 102 files changed, 344 insertions(+), 109 deletions(-) create mode 100644 .idea/intellij-javadocs-4.0.1.xml diff --git a/.idea/intellij-javadocs-4.0.1.xml b/.idea/intellij-javadocs-4.0.1.xml new file mode 100644 index 0000000..dd24abe --- /dev/null +++ b/.idea/intellij-javadocs-4.0.1.xml @@ -0,0 +1,204 @@ + + + + + UPDATE + false + true + + FIELD + METHOD + TYPE + + + PROTECTED + DEFAULT + PUBLIC + + + + + + ^.*(public|protected|private)*.+interface\s+\w+.* + /**\n + * The interface ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + ^.*(public|protected|private)*.+enum\s+\w+.* + /**\n + * The enum ${name}.\n + */ + + + ^.*(public|protected|private)*.+class\s+\w+.* + /**\n + * The type ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + .+ + /**\n + * The type ${name}.\n + */ + + + + + .+ + /**\n + * Instantiates a new ${name}.\n +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*\s*.*(\w(\s*<.+>)*)+\s+get\w+\s*\(.*\).+ + /**\n + * Gets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*(public|protected|private)*\s*.*(void|\w(\s*<.+>)*)+\s+set\w+\s*\(.*\).+ + /**\n + * Sets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+ + /**\n + * The entry point of application.\n + + <#if element.parameterList.parameters?has_content> + *\n +</#if> + * @param ${element.parameterList.parameters[0].name} the input arguments\n +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + .+ + /**\n + * ${name}<#if isNotVoid> ${return}</#if>.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${return}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*.+static.*(\w\s\w)+.+ + /**\n + * The constant ${element.getName()}.\n + */ + + + ^.*(public|protected|private)*.*(\w\s\w)+.+ + /**\n + <#if element.parent.isInterface()> + * The constant ${element.getName()}.\n +<#else> + * The ${name}.\n +</#if> */ + + + .+ + /**\n + <#if element.parent.isEnum()> + *${name} ${typeName}.\n +<#else> + * The ${name}.\n +</#if>*/ + + + + + \ No newline at end of file diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index ba67d1d..1eec4e8 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -1,7 +1,7 @@ /* * MobibotBuild.java * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -70,7 +70,7 @@ public class MobibotBuild extends Project { repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io") ); - var log4j = version(2, 22, 0); + var log4j = version(2, 22, 1); var kotlin = version(1, 9, 22); scope(compile) // PircBotX @@ -83,7 +83,7 @@ public class MobibotBuild extends Project { // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 1, 0))) + .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 2, 0))) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) @@ -91,7 +91,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.9")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.11")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) @@ -101,7 +101,7 @@ public class MobibotBuild extends Project { .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) .include(dependency("org.json", "json", "20231013")) - .include(dependency("org.jsoup", "jsoup", "1.17.1")) + .include(dependency("org.jsoup", "jsoup", "1.17.2")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.2")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.1")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index 2c5f05d..f3ae0a3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index d037e44..8716e14 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index d82f011..a91c6dc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 6b9c21a..88bed54 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 7cb5aed..88e475e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index f499559..13d9f52 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20231224114859" + const val VERSION = "0.8.0-rc+20240116102134" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1703447340121L), ZoneId.systemDefault() + Instant.ofEpochMilli(1705429295075L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index e4760d2..6a8e4ff 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 5f79472..5f79fab 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 038e378..873d31f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 9608ca8..88b6c6d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index f271bfa..e4e111d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 2109693..6dfff07 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index ed0b6ef..4fae22f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index ec7823b..3495b28 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index b2293b0..314ce99 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index 20a6635..bddd56c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 85a03ab..59b474a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 77154c7..886b26c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 7f76d35..90f3c04 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 33d6fef..54a85b2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index 4594da3..f422566 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 1443d44..2fade4e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index fba6b99..ea18613 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,7 +1,7 @@ /* * LinksManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index ff4278d..2d24ba7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1662857..1a582a9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 825e374..e6971ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index cfd2c27..e51a6bf 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,7 +1,7 @@ /* * NickComparator.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index c9ee0f3..1ad12f6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,7 +1,7 @@ /* * Seen.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 7924977..69923db 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,7 +1,7 @@ /* * SeenNick.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 061ca6a..790ce0e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index b65a4da..f34af98 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,7 +1,7 @@ /* * TellManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index d17fbb5..ac15595 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index e8676ec..676fbee 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index 9c09626..ea536bc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index e18d692..d9d5587 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index 4a69446..c88f8a4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index f786cb2..4dc8e9e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,7 +1,7 @@ /* * FeedsManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 8c8e736..17dbde2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index b7aae28..b5fe5b1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt index e2ad717..b94b5d7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt @@ -1,7 +1,7 @@ /* * ChatGpt.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 4614a4c..fe95fe0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index da0efd8..4fb7555 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index 8420fb1..b6d69ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt index 6f9b5ff..18db66d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt @@ -1,3 +1,34 @@ +/* + * Gemini.kt + * + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * + * 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.modules import com.google.cloud.vertexai.VertexAI @@ -63,7 +94,7 @@ class Gemini : AbstractModule() { */ const val MAX_TOKENS_PROP = "gemini-max-tokens" - // ChatGPT command + // Gemini command private const val GEMINI_CMD = "gemini" @JvmStatic diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index f426d1e..1611130 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 2760fa7..1c6a5eb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index 9ab2ead..baeebba 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 3be3a5f..166e39f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,7 +1,7 @@ /* * Mastodon.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index a7416c2..32240ca 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 944dbc1..4b56df5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index a299d8d..13afbb7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index dcae5e7..13b9329 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt index 8799279..e0fd947 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt @@ -1,7 +1,7 @@ /* * War.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 80a06fa..21bf0ae 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index a72efab..ba88711 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,7 +1,7 @@ /* * WolframAlpha.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 18072bc..8978275 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index 0607936..abcd043 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index d41ec8c..4b788ba 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index 037d504..ea11c9c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index b424fdf..27b1cbd 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index 9c5e088..ae909f6 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index 91f2dd9..c785106 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,7 +1,7 @@ /* * SocialManager.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index b594670..a13b429 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,7 +1,7 @@ /* * SocialModule.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index 3fd315e..e722ed8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,7 +1,7 @@ /* * SocialTimer.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 9dd6034..4555884 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt index 22b0146..8d73a5e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -1,7 +1,7 @@ /* * DisableOnCi.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt index 1d9baf4..b0f4771 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt @@ -1,7 +1,7 @@ /* * DisableOnCiCondition.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index a3994ec..d1ca71d 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index bf4408f..1cb2645 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index c039d74..c290262 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 3b81950..7e56912 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index a65b3a5..49ee7fa 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index 99ba951..add701e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,7 +1,7 @@ /* * InfoTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index b6fbbff..875342b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index fa2bb70..59338f4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,7 +1,7 @@ /* * LinksManagerTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index e315891..9eb433a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 62a1db7..6d5ba78 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,7 +1,7 @@ /* * SeenTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 69bc2e9..33b3598 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index e3c9c1f..d7bde4c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 0ad26b5..983e225 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 3a3bdda..577f425 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index b56e6ed..d2b959e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index 04afe8c..e7ef601 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index 7180c13..e38e81b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -1,7 +1,7 @@ /* * ChatGptTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 10b3a43..0e2ef84 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 8ab64c0..5ebb7dc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 8bafede..007f22f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt index 28845a9..db69fe7 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt @@ -1,7 +1,7 @@ /* - * ChatGptTest.kt + * GeminiTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 4942266..752b49e 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 441d435..118d736 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 8699c0f..048d91a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -44,8 +44,8 @@ class LookupTest { var result = nslookup("apple.com") assertThat(result, "lookup(apple.com)").contains("17.253.144.10") - result = nslookup("204.122.16.136") - assertThat(result, "lookup(204.122.16.136)").contains("nix3.thauvin.us") + result = nslookup("37.27.52.13") + assertThat(result, "lookup(37.27.52.13)").contains("nix4.thauvin.us") } @Test diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index f4b5e99..11d4a51 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,7 +1,7 @@ /* * MastodonTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index 416b3ab..ff706fd 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index bbf9eb1..5532e30 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index f836b0e..0e4b4ec 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 0816b17..3fe9189 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index 7ae2b83..f4f85f8 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 3bfe0d1..4f827bb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,7 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index ae40e3a..1211ea4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index 32a0495..44054f1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,7 +1,7 @@ /* * MessageTest.kt * - * Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: From d371bf5a01eea11a733788e9e0828dd614cd771a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 31 Jan 2024 15:55:32 -0800 Subject: [PATCH 766/858] Bumped bld to 1.8.0 --- .idea/libraries/bld.xml | 4 ++-- .vscode/settings.json | 2 +- README.md | 1 + lib/bld/bld-wrapper.jar | Bin 27321 -> 27293 bytes lib/bld/bld-wrapper.properties | 10 +++++----- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index ca84ff0..bff4f62 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.vscode/settings.json b/.vscode/settings.json index 133aa45..5633e79 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ ], "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.7.5.jar", + "${HOME}/.bld/dist/bld-1.8.0.jar", "lib/compile/*.jar", "lib/runtime/*.jar", "lib/test/*.jar" diff --git a/README.md b/README.md index b360d96..c9191ce 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-7f52ff.svg)](https://kotlinlang.org) +[![bld](https://img.shields.io/badge/1.8.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index d17ad8982355ae5516129c6b07841609641eb492..1ae4f55822672a66d89f6c692ffc7f10b2d438c9 100644 GIT binary patch delta 25301 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 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index e219f09..90471f8 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.1 -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.3 -bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0-SNAPSHOT -bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.0-SNAPSHOT +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.2 +bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.4 +bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0 +bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.0 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= -bld.version=1.7.5 +bld.version=1.8.0 From 6c2338b8b6f1852e27bac9c1753197591eff2baf Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 5 Feb 2024 17:42:48 -0800 Subject: [PATCH 767/858] Bumped depedencies --- lib/bld/bld-wrapper.jar | Bin 27293 -> 27293 bytes lib/bld/bld-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 1ae4f55822672a66d89f6c692ffc7f10b2d438c9..5e76a01d101edb93ddf297aa6a5ff753385cc8f4 100644 GIT binary patch delta 131 zcmbPxm2vJ>M&1B#W)=|!4h{|mg?^Wbymib#YGa?MF^Jy0%($Bg%rG(gr3+>lrkrO4 wGnms1!BUgWGweYElPfcv!SwzNPcY4$83Lx0GF`#+s?0cuc$Pnyj?Hoa047K%xBvhE delta 131 zcmbPxm2vJ>M&1B#W)=|!4h{|m!HsqkdFz;g)W$whV-UT0nQ=E0m|C<08is7ZU6uP diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 90471f8..77cb51b 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -3,7 +3,7 @@ bld.downloadExtensionSources=true bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.2 bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.4 bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0 -bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.0 +bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.1 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= From 6da6783395e977b43c7f5572c3f53cbbae4512aa Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 5 Feb 2024 17:43:06 -0800 Subject: [PATCH 768/858] Minor cleanups --- .circleci/config.yml | 2 +- .gitlab-ci.yml | 3 +++ .../java/net/thauvin/erik/MobibotBuild.java | 24 +++++++++---------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 77889be..09d8896 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ defaults: &defaults TERM: dumb CI_NAME: "CircleCI" -defaults_gradle: &defaults_bld +defaults_bld: &defaults_bld steps: - checkout - run: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 052df48..2398bba 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,8 @@ image: openjdk:17 +variables: + CI_NAME: "GitLab CI" + stages: - test diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 1eec4e8..024b908 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -50,8 +50,7 @@ import java.util.ArrayList; import java.util.List; import java.util.jar.Attributes; -import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; -import static rife.bld.dependencies.Repository.MAVEN_LOCAL; +import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; import static rife.bld.dependencies.Scope.test; @@ -67,8 +66,11 @@ public class MobibotBuild extends Project { javaRelease = 17; downloadSources = true; autoDownloadPurge = true; - repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, new Repository("https://jitpack.io") - ); + repositories = List.of( + MAVEN_LOCAL, + MAVEN_CENTRAL, + new Repository("https://jitpack.io"), + SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 22, 1); var kotlin = version(1, 9, 22); @@ -83,15 +85,13 @@ public class MobibotBuild extends Project { // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 2, 0))) + .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 3, 0))) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) - .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) - .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.11")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.12")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) @@ -103,15 +103,15 @@ public class MobibotBuild extends Project { .include(dependency("org.json", "json", "20231013")) .include(dependency("org.jsoup", "jsoup", "1.17.2")) // Thauvin - .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.2")) + .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.1")) - .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.1")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.2-SNAPSHOT")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 0))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); From 69c18841bc5dccd682f4a7240cac1cc7b79d59ad Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 9 Feb 2024 01:14:52 -0800 Subject: [PATCH 769/858] Bumped depencendies (common-codec, org.json) --- .vscode/settings.json | 2 ++ src/bld/java/net/thauvin/erik/MobibotBuild.java | 5 ++--- src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5633e79..1b057c3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,7 +8,9 @@ "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ "${HOME}/.bld/dist/bld-1.8.0.jar", + "lib/bld/*.jar", "lib/compile/*.jar", + "lib/provided/*.jar", "lib/runtime/*.jar", "lib/test/*.jar" ] diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 024b908..3977d27 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -80,14 +80,13 @@ public class MobibotBuild extends Project { // Commons (mostly for PircBotX) .include(dependency("org.apache.commons", "commons-lang3", "3.14.0")) .include(dependency("org.apache.commons", "commons-text", "1.11.0")) - .include(dependency("commons-codec", "commons-codec", "1.16.0")) + .include(dependency("commons-codec", "commons-codec", "1.16.1")) .include(dependency("commons-net", "commons-net", "3.10.0")) // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 3, 0))) // Kotlin - .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging @@ -100,7 +99,7 @@ public class MobibotBuild extends Project { .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) - .include(dependency("org.json", "json", "20231013")) + .include(dependency("org.json", "json", "20240205")) .include(dependency("org.jsoup", "jsoup", "1.17.2")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 13d9f52..72ba96c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240116102134" + const val VERSION = "0.8.0-rc+20240209010856" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1705429295075L), ZoneId.systemDefault() + Instant.ofEpochMilli(1707469736725L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" From 26f1a2da1dd5049c5748066ec807067beb64c51a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 9 Feb 2024 01:19:10 -0800 Subject: [PATCH 770/858] Fixed Kotlin badge version number --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9191ce..f051559 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.9.22-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/1.8.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) From 8aa4dcbcce42fb44196994620cfafa6ae3f96cf7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 9 Feb 2024 16:48:31 -0800 Subject: [PATCH 771/858] Bumped Google Cloud VertexAI dependency to 0.4.0 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 3977d27..c1d5baa 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -85,7 +85,7 @@ public class MobibotBuild extends Project { // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 3, 0))) + .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 4, 0))) // Kotlin .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 72ba96c..a4d58f3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240209010856" + const val VERSION = "0.8.0-rc+20240209164526" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1707469736725L), ZoneId.systemDefault() + Instant.ofEpochMilli(1707525927076L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt index 18db66d..b1a2c83 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt @@ -33,9 +33,9 @@ package net.thauvin.erik.mobibot.modules import com.google.cloud.vertexai.VertexAI import com.google.cloud.vertexai.api.GenerationConfig -import com.google.cloud.vertexai.generativeai.preview.ChatSession -import com.google.cloud.vertexai.generativeai.preview.GenerativeModel -import com.google.cloud.vertexai.generativeai.preview.ResponseHandler +import com.google.cloud.vertexai.generativeai.ChatSession +import com.google.cloud.vertexai.generativeai.GenerativeModel +import com.google.cloud.vertexai.generativeai.ResponseHandler import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage import org.pircbotx.hooks.types.GenericMessageEvent From 0d37c7b15250c5da643a5dc4fcb6462abcaaa12f Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 25 Feb 2024 21:24:16 -0800 Subject: [PATCH 772/858] Added the Snyk custom POM --- snyx.xml | 11 +++++++++++ src/bld/java/net/thauvin/erik/MobibotBuild.java | 14 +++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 snyx.xml diff --git a/snyx.xml b/snyx.xml new file mode 100644 index 0000000..8a129a6 --- /dev/null +++ b/snyx.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + + + + + + + diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index c1d5baa..1f72ddf 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -39,11 +39,13 @@ import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; import rife.bld.operations.exceptions.ExitStatusException; +import rife.bld.publish.PomBuilder; import rife.tools.FileUtils; import rife.tools.exceptions.FileUtilsErrorException; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -72,7 +74,7 @@ public class MobibotBuild extends Project { new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); - var log4j = version(2, 22, 1); + var log4j = version(2, 23, 0); var kotlin = version(1, 9, 22); scope(compile) // PircBotX @@ -85,9 +87,9 @@ public class MobibotBuild extends Project { // Google .include(dependency("com.google.code.gson", "gson", "2.10.1")) .include(dependency("com.google.guava", "guava", "33.0.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 4, 0))) + .include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 5, 0))) // Kotlin - .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.0")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging .include(dependency("org.slf4j", "slf4j-api", "2.0.12")) @@ -191,4 +193,10 @@ public class MobibotBuild extends Project { .extension(".kt") .execute(); } + + @BuildCommand(value = "snyk-pom", summary = "Generates the Snyk POM") + public void snykPom() throws FileUtilsErrorException { + PomBuilder.generateInto(publishOperation().info(), publishOperation().dependencies(), + Path.of(workDirectory.getPath(), "snyx.xml").toFile()); + } } From 2a9bb44fecff85e4ef2fd82313b68b6a7620ca43 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 25 Feb 2024 21:32:58 -0800 Subject: [PATCH 773/858] Added Root POM for Snyk --- snyx.xml => pom.xml | 0 src/bld/java/net/thauvin/erik/MobibotBuild.java | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename snyx.xml => pom.xml (100%) diff --git a/snyx.xml b/pom.xml similarity index 100% rename from snyx.xml rename to pom.xml diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 1f72ddf..6f4e7cc 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -194,9 +194,9 @@ public class MobibotBuild extends Project { .execute(); } - @BuildCommand(value = "snyk-pom", summary = "Generates the Snyk POM") - public void snykPom() throws FileUtilsErrorException { + @BuildCommand(value = "root-pom", summary = "Generates the POM file in the root directory") + public void rootPom() throws FileUtilsErrorException { PomBuilder.generateInto(publishOperation().info(), publishOperation().dependencies(), - Path.of(workDirectory.getPath(), "snyx.xml").toFile()); + Path.of(workDirectory.getPath(), "pom.xml").toFile()); } } From 7931e4adbe1d96afe2af97ceb2fae7364780524a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 26 Feb 2024 17:01:30 -0800 Subject: [PATCH 774/858] Bumped to bld 1.9.0 --- .github/workflows/bld.yml | 4 ++++ .idea/libraries/bld.xml | 4 ++-- .vscode/launch.json | 11 ----------- .vscode/settings.json | 17 ----------------- README.md | 2 +- lib/bld/bld-wrapper.jar | Bin 27293 -> 27319 bytes lib/bld/bld-wrapper.properties | 10 +++++----- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- 8 files changed, 14 insertions(+), 38 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/settings.json diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 19081a4..9bd9d9d 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -47,6 +47,10 @@ jobs: EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} run: ./bld jacoco + - name: Remove pom.xml + if: success() && matrix.java-version == env.COVERAGE_SDK + run: rm -rf pom.xml + - name: SonarCloud Scan uses: sonarsource/sonarcloud-github-action@master if: success() && matrix.java-version == env.COVERAGE_SDK diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index bff4f62..0b615c1 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index c6500f2..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "type": "java", - "name": "Run Tests", - "request": "launch", - "mainClass": "net.thauvin.erik.MobibotTest" - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 1b057c3..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "java.project.sourcePaths": [ - "src/main/java", - "src/main/resources", - "src/test/java", - "src/bld/java" - ], - "java.configuration.updateBuildConfiguration": "automatic", - "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.8.0.jar", - "lib/bld/*.jar", - "lib/compile/*.jar", - "lib/provided/*.jar", - "lib/runtime/*.jar", - "lib/test/*.jar" - ] -} diff --git a/README.md b/README.md index f051559..0ce0853 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Kotlin](https://img.shields.io/badge/kotlin-1.9.22-7f52ff.svg)](https://kotlinlang.org) -[![bld](https://img.shields.io/badge/1.8.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![bld](https://img.shields.io/badge/1.9.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 5e76a01d101edb93ddf297aa6a5ff753385cc8f4..c2adfe8730e64fdc0c51dc7994d0d143787eb1e2 100644 GIT binary patch delta 25244 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

    ^*a+>tbBpHE+pL~3DbtKbopH&MqggYR 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 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 77cb51b..95b4a2e 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.2 -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.4 -bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.0 -bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.1 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.3 +bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.5 +bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.1 +bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.2 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= -bld.version=1.8.0 +bld.version=1.9.0 diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index a4d58f3..e44a35b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240209164526" + const val VERSION = "0.8.0-rc+20240225204340" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1707525927076L), ZoneId.systemDefault() + Instant.ofEpochMilli(1708922620439L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" From cf0bafff940697dfbcb7755df95bae6ae8a10ecc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 3 Mar 2024 22:10:00 -0800 Subject: [PATCH 775/858] Reworked the ChatGPT JSON request programmatically --- .idea/misc.xml | 1 + .../java/net/thauvin/erik/MobibotBuild.java | 4 +-- .../thauvin/erik/mobibot/modules/ChatGpt.kt | 28 ++++++++----------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 26293d3..f63c128 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,6 +6,7 @@ + - - - - \ No newline at end of file + diff --git a/README.md b/README.md index 4ac4370..eb38cfb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-1.9.24-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-2.0.0-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/1.9.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) diff --git a/pom.xml b/pom.xml index df4bbe8..3ef1964 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ com.google.code.gson gson - 2.10.1 + 2.11.0 compile @@ -54,31 +54,31 @@ com.google.cloud google-cloud-vertexai - 1.3.0 + 1.4.0 compile org.jetbrains.kotlin kotlin-stdlib - 1.9.24 + 2.0.0 compile org.jetbrains.kotlin kotlin-stdlib-common - 1.9.24 + 2.0.0 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 1.9.24 + 2.0.0 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 1.9.24 + 2.0.0 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index d2e4cb1..8a7265e 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -78,7 +78,7 @@ public class MobibotBuild extends Project { SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 23, 1); - var kotlin = version(1, 9, 24); + var kotlin = version(2, 0, 0); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) @@ -88,9 +88,9 @@ public class MobibotBuild extends Project { .include(dependency("commons-codec", "commons-codec", "1.17.0")) .include(dependency("commons-net", "commons-net", "3.10.0")) // Google - .include(dependency("com.google.code.gson", "gson", "2.10.1")) + .include(dependency("com.google.code.gson", "gson", "2.11.0")) .include(dependency("com.google.guava", "guava", "33.2.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.3.0")) + .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.4.0")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) @@ -153,7 +153,7 @@ public class MobibotBuild extends Project { @Override public void updates() throws Exception { super.updates(); - rootPom(); + pomRoot(); } @BuildCommand(summary = "Copies all needed files to the deploy directory") @@ -203,13 +203,13 @@ public class MobibotBuild extends Project { .classTemplate(new File(workDirectory(), "release-info.txt")) .className("ReleaseInfo") .packageName(pkg) - .directory(new File(srcMainDirectory(), "kotlin")) + .directory(srcMainKotlin) .extension(".kt") .execute(); } - @BuildCommand(value = "root-pom", summary = "Generates the POM file in the root directory") - public void rootPom() throws FileUtilsErrorException { + @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory") + public void pomRoot() throws FileUtilsErrorException { PomBuilder.generateInto(publishOperation().info(), dependencies(), Path.of(workDirectory.getPath(), "pom.xml").toFile()); } From db0e4d30f1f3b4f32cad66163b121546249351d6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 12 Jul 2024 17:42:39 -0700 Subject: [PATCH 804/858] Updated various dependencies Cleaned up CI workflows --- .circleci/config.yml | 8 ++++++++ .github/workflows/bld.yml | 5 +++-- .gitlab-ci.yml | 18 ++++++++++++++---- bitbucket-pipelines.yml | 12 +++++++++++- lib/bld/bld-wrapper.properties | 10 +++++----- pom.xml | 8 ++++---- .../java/net/thauvin/erik/MobibotBuild.java | 16 ++++++++-------- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 ++-- 8 files changed, 55 insertions(+), 26 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 09d8896..c5e20a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,6 +9,14 @@ defaults: &defaults defaults_bld: &defaults_bld steps: - checkout + - run: + name: Install Kotlin via SDKMAN! + command: | + curl -s "https://get.sdkman.io" | bash + echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config + echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config + source "$HOME/.sdkman/bin/sdkman-init.sh" + sdk install kotlin 2.0.0 - run: name: Download the bld dependencies command: ./bld download diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 28175b0..21c09a8 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -8,10 +8,12 @@ jobs: env: COVERAGE_SDK: "17" + COVERAGE_KOTLIN: "2.0.0" strategy: matrix: java-version: [17, 21, 22] + kotlin-version: [1.9.24, 2.0.0] steps: - name: Checkout source repository @@ -48,12 +50,11 @@ jobs: run: ./bld jacoco - name: Remove pom.xml - if: success() && matrix.java-version == env.COVERAGE_SDK run: rm -rf pom.xml - name: SonarCloud Scan uses: sonarsource/sonarcloud-github-action@master - if: success() && matrix.java-version == env.COVERAGE_SDK + if: success() && matrix.java-version == env.COVERAGE_SDK && matrix.kotlin-version == env.COVERAGE_KOTLIN env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2398bba..14646f3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: openjdk:17 +image: ubuntu:latest variables: CI_NAME: "GitLab CI" @@ -6,9 +6,19 @@ variables: stages: - test +before_script: + - apt-get update -qq && apt-get install -y curl zip + - curl -s "https://get.sdkman.io" | bash + - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config + - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config + - source "$HOME/.sdkman/bin/sdkman-init.sh" + - sdk install java 17.0.11-tem + - sdk install kotlin 2.0.0 + - source "$HOME/.sdkman/bin/sdkman-init.sh" + test: stage: test script: - - ./bld download - - ./bld compile - - ./bld test + - ./bld download + - ./bld compile + - ./bld test diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 7c85194..5951f4b 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,10 +1,20 @@ -image: openjdk:17 +image: ubuntu:latest pipelines: default: - step: name: Test with bld script: + # Install Java & Kotlin via SDKMAN! + - apt-get update -qq && apt-get install -y curl zip + - curl -s "https://get.sdkman.io" | bash + - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config + - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config + - source "$HOME/.sdkman/bin/sdkman-init.sh" + - sdk install java 17.0.11-tem + - sdk install kotlin 2.0.0 + - source "$HOME/.sdkman/bin/sdkman-init.sh" + # Download, compile and test with bld - ./bld download - ./bld compile - ./bld test diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 1695c0b..0aa69b0 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.5 -bld.extensions=com.uwyn.rife2:bld-generated-version:0.9.6 -bld.extensions-kotlin=com.uwyn.rife2:bld-kotlin:0.9.8 -bld.extensions-detekt=com.uwyn.rife2:bld-detekt:0.9.4 -bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.4 +bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.8-SNAPSHOT +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.6 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.0-SNAPSHOT +bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=1.9.1 diff --git a/pom.xml b/pom.xml index 3ef1964..8fa40d6 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ commons-net commons-net - 3.10.0 + 3.11.1 compile @@ -48,13 +48,13 @@ com.google.guava guava - 33.2.0-jre + 33.2.1-jre compile com.google.cloud google-cloud-vertexai - 1.4.0 + 1.6.0 compile @@ -150,7 +150,7 @@ org.jsoup jsoup - 1.17.2 + 1.18.1 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 8a7265e..e9343c5 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -86,11 +86,11 @@ public class MobibotBuild extends Project { .include(dependency("org.apache.commons", "commons-lang3", "3.14.0")) .include(dependency("org.apache.commons", "commons-text", "1.12.0")) .include(dependency("commons-codec", "commons-codec", "1.17.0")) - .include(dependency("commons-net", "commons-net", "3.10.0")) + .include(dependency("commons-net", "commons-net", "3.11.1")) // Google .include(dependency("com.google.code.gson", "gson", "2.11.0")) - .include(dependency("com.google.guava", "guava", "33.2.0-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.4.0")) + .include(dependency("com.google.guava", "guava", "33.2.1-jre")) + .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.6.0")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) @@ -109,7 +109,7 @@ public class MobibotBuild extends Project { .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) .include(dependency("org.json", "json", "20240303")) - .include(dependency("org.jsoup", "jsoup", "1.17.2")) + .include(dependency("org.jsoup", "jsoup", "1.18.1")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.2-SNAPSHOT")) @@ -118,8 +118,8 @@ public class MobibotBuild extends Project { scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 3))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 3))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); @@ -189,7 +189,7 @@ public class MobibotBuild extends Project { } @BuildCommand(summary = "Generates JaCoCo Reports") - public void jacoco() throws IOException { + public void jacoco() throws Exception { new JacocoReportOperation() .fromProject(this) .sourceFiles(srcMainKotlin) @@ -197,7 +197,7 @@ public class MobibotBuild extends Project { } @BuildCommand(value = "release-info", summary = "Generates the ReleaseInfo class") - public void releaseInfo() { + public void releaseInfo() throws Exception { new GeneratedVersionOperation() .fromProject(this) .classTemplate(new File(workDirectory(), "release-info.txt")) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index c892cf0..3abf162 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240509074831" + const val VERSION = "0.8.0-rc+20240712110931" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1715266111851L), ZoneId.systemDefault() + Instant.ofEpochMilli(1720807771484L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" From c3a8018cc39881f7f5d508583c41712575207550 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 12 Jul 2024 21:58:34 -0700 Subject: [PATCH 805/858] Cleaned up CI workflows --- .circleci/config.yml | 58 ++++++++++-------- .github/workflows/bld.yml | 48 +++++++-------- .gitlab-ci.yml | 8 +-- .idea/libraries/bld.xml | 4 +- bitbucket-pipelines.yml | 6 +- lib/bld/bld-wrapper.jar | Bin 27319 -> 29578 bytes lib/bld/bld-wrapper.properties | 2 +- .../erik/mobibot/modules/ChatGptTest.kt | 2 +- 8 files changed, 68 insertions(+), 60 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c5e20a6..17b50f1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,8 @@ -version: 2 +version: 2.1 + +orbs: + sdkman: joshdholtz/sdkman@0.2.0 + defaults: &defaults working_directory: ~/repo environment: @@ -6,26 +10,31 @@ defaults: &defaults TERM: dumb CI_NAME: "CircleCI" -defaults_bld: &defaults_bld - steps: - - checkout - - run: - name: Install Kotlin via SDKMAN! - command: | - curl -s "https://get.sdkman.io" | bash - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config - source "$HOME/.sdkman/bin/sdkman-init.sh" - sdk install kotlin 2.0.0 - - run: - name: Download the bld dependencies - command: ./bld download - - run: - name: Compile source with bld - command: ./bld compile - - run: - name: Run tests with bld - command: ./bld test +commands: + build_and_test: + parameters: + reports-dir: + type: string + default: "build/reports/test_results" + steps: + - checkout + - sdkman/setup-sdkman + - sdkman/sdkman-install: + candidate: kotlin + version: 2.0.0 + - run: + name: Download dependencies + command: ./bld download + - run: + name: Compile source + command: ./bld compile + - run: + name: Run tests + command: ./bld jacoco -reports-dir=<< parameters.reports-dir >> + - store_test_results: + path: << parameters.reports-dir >> + - store_artifacts: + path: build/reports/jacoco/test/html jobs: bld_jdk17: @@ -34,7 +43,8 @@ jobs: docker: - image: cimg/openjdk:17.0 - <<: *defaults_bld + steps: + - build_and_test bld_jdk20: <<: *defaults @@ -42,10 +52,10 @@ jobs: docker: - image: cimg/openjdk:20.0 - <<: *defaults_bld + steps: + - build_and_test workflows: - version: 2 bld: jobs: - bld_jdk17 diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 21c09a8..a3c4951 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,19 +1,29 @@ name: bld-ci -on: [push, pull_request, workflow_dispatch] +on: [ push, pull_request, workflow_dispatch ] + +env: + ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} + CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} + CI_NAME: "GitHub CI" + COVERAGE_JDK: "21" + COVERAGE_KOTLIN: "2.0.0" + EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} + KOTLIN_HOME: /usr/share/kotlinc + MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} + MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} + MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} + OWM_API_KEY: ${{ secrets.OWM_API_KEY }} + PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} jobs: build-bld-project: runs-on: ubuntu-latest - env: - COVERAGE_SDK: "17" - COVERAGE_KOTLIN: "2.0.0" - strategy: matrix: - java-version: [17, 21, 22] - kotlin-version: [1.9.24, 2.0.0] + java-version: [ 17, 21, 22 ] + kotlin-version: [ 1.9.24, 2.0.0 ] steps: - name: Checkout source repository @@ -21,40 +31,28 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK ${{ matrix.java-version }} + - name: Set up JDK ${{ matrix.java-version }} with Kotlin ${{ matrix.kotlin-version }} uses: actions/setup-java@v4 with: distribution: "zulu" java-version: ${{ matrix.java-version }} - - name: Grant bld execute permission - run: chmod +x bld - - - name: Download the bld dependencies + - name: Download dependencies run: ./bld download - - name: Compile source with bld + - name: Compile source run: ./bld compile - - name: Run tests with bld - env: - CI_NAME: "GitHub CI" - ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} - CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} - OWM_API_KEY: ${{ secrets.OWM_API_KEY }} - PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }} - MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} - MASTODON_HANDLE: ${{ secrets.MASTODON_HANDLE }} - MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} - EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} + - name: Run tests run: ./bld jacoco - name: Remove pom.xml + if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.kotlin-version == env.COVERAGE_KOTLIN run: rm -rf pom.xml - name: SonarCloud Scan uses: sonarsource/sonarcloud-github-action@master - if: success() && matrix.java-version == env.COVERAGE_SDK && matrix.kotlin-version == env.COVERAGE_KOTLIN + if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.kotlin-version == env.COVERAGE_KOTLIN env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 14646f3..10b9b0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: ubuntu:latest +image: fedora:latest variables: CI_NAME: "GitLab CI" @@ -7,13 +7,13 @@ stages: - test before_script: - - apt-get update -qq && apt-get install -y curl zip + - dnf -qy update && dnf -y install zip - curl -s "https://get.sdkman.io" | bash - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config - source "$HOME/.sdkman/bin/sdkman-init.sh" - - sdk install java 17.0.11-tem - - sdk install kotlin 2.0.0 + - sdk install java + - sdk install kotlin - source "$HOME/.sdkman/bin/sdkman-init.sh" test: diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index a2969be..2fb5ff0 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 5951f4b..ace99d2 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -5,14 +5,14 @@ pipelines: - step: name: Test with bld script: - # Install Java & Kotlin via SDKMAN! + # Install latest Java & Kotlin via SDKMAN! - apt-get update -qq && apt-get install -y curl zip - curl -s "https://get.sdkman.io" | bash - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config - source "$HOME/.sdkman/bin/sdkman-init.sh" - - sdk install java 17.0.11-tem - - sdk install kotlin 2.0.0 + - sdk install java + - sdk install kotlin - source "$HOME/.sdkman/bin/sdkman-init.sh" # Download, compile and test with bld - ./bld download diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 8a6c6415420d24c2f97b665923c81552b4add950..f46bb77a290005df0476b73c34554b894620bf01 100644 GIT binary patch delta 27557 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 A#Q*>R delta 25368 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 diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 0aa69b0..e9d6ec3 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -7,4 +7,4 @@ bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.6 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=1.9.1 +bld.version=2.0.0-SNAPSHOT diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt index c031295..1809a9f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt @@ -49,7 +49,7 @@ class ChatGptTest : LocalProperties() { @Test fun testChatOnCoverage() { - if (System.getenv("CI") == null || System.getenv("COVERAGE_SDK") != null) { + if (System.getenv("CI") == null || System.getenv("COVERAGE_JDK") != null) { assertThat( ChatGpt.chat("how do I encode a URL in java?", getProperty(ChatGpt.API_KEY_PROP), 60) ).contains("URLEncoder") From 8d9acd8184f2c36abfde1ab37efb00c2b95f8636 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 15 Jul 2024 21:00:28 -0700 Subject: [PATCH 806/858] Moved to Gemini 1.5 Flash --- .../thauvin/erik/mobibot/modules/Gemini.kt | 28 +++++++++---------- .../erik/mobibot/modules/GeminiTest.kt | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt index c0faefa..2e4ed91 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt @@ -35,7 +35,6 @@ import com.google.cloud.vertexai.VertexAI import com.google.cloud.vertexai.api.GenerationConfig import com.google.cloud.vertexai.api.HarmCategory import com.google.cloud.vertexai.api.SafetySetting -import com.google.cloud.vertexai.generativeai.ChatSession import com.google.cloud.vertexai.generativeai.GenerativeModel import com.google.cloud.vertexai.generativeai.ResponseHandler import net.thauvin.erik.mobibot.Utils @@ -57,7 +56,7 @@ class Gemini : AbstractModule() { val answer = chat( args.trim(), properties[PROJECT_ID_PROP], - properties[LOCATION_PROPR], + properties[LOCATION_PROP], properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() ) if (!answer.isNullOrEmpty()) { @@ -83,17 +82,17 @@ class Gemini : AbstractModule() { const val GEMINI_NAME = "Gemini" /** - * The Google cloud project ID. + * The Google cloud project ID property. */ const val PROJECT_ID_PROP = "gemini-project-id" /** - * The Vertex AI location. + * The Vertex AI location property. */ - const val LOCATION_PROPR = "gemini-location" + const val LOCATION_PROP = "gemini-location" /** - * The max tokens property. + * The max number of tokens property. */ const val MAX_TOKENS_PROP = "gemini-max-tokens" @@ -112,31 +111,30 @@ class Gemini : AbstractModule() { try { VertexAI(projectId, location).use { vertexAI -> val generationConfig = GenerationConfig.newBuilder().setMaxOutputTokens(maxToken).build() - val safetySettings = Arrays.asList( + val safetySettings = listOf( SafetySetting.newBuilder() .setCategory(HarmCategory.HARM_CATEGORY_HATE_SPEECH) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE) + .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) .build(), SafetySetting.newBuilder() .setCategory(HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE) + .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) .build(), SafetySetting.newBuilder() .setCategory(HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE) + .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) .build(), SafetySetting.newBuilder() .setCategory(HarmCategory.HARM_CATEGORY_HARASSMENT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE) + .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) .build() ) - val model = GenerativeModel.Builder().setModelName("gemini-pro-vision") + val model = GenerativeModel.Builder().setModelName("gemini-1.5-flash-001") .setGenerationConfig(generationConfig) .setVertexAi(vertexAI).build() .withSafetySettings(safetySettings) - val session = ChatSession(model) - val response = session.sendMessage(query) + val response = model.generateContent(query) return ResponseHandler.getText(response) } } catch (e: Exception) { @@ -161,7 +159,7 @@ class Gemini : AbstractModule() { add(Utils.helpFormat("%c $GEMINI_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $GEMINI_CMD how do I make an HTTP request in Javascript?")) } - initProperties(PROJECT_ID_PROP, LOCATION_PROPR, MAX_TOKENS_PROP) + initProperties(PROJECT_ID_PROP, LOCATION_PROP, MAX_TOKENS_PROP) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt index db69fe7..1f0202f 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt @@ -49,7 +49,7 @@ class GeminiTest : LocalProperties() { @DisableOnCi fun chatPrompt() { val projectId = getProperty(Gemini.PROJECT_ID_PROP) - val location = getProperty(Gemini.LOCATION_PROPR) + val location = getProperty(Gemini.LOCATION_PROP) val maxTokens = getProperty(Gemini.MAX_TOKENS_PROP).toInt() assertThat( From ebf05fa398a328d8fec8867172f5ef43e7f7d273 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 15 Jul 2024 21:01:00 -0700 Subject: [PATCH 807/858] Fixed root POM generation --- pom.xml | 8 ++++---- src/bld/java/net/thauvin/erik/MobibotBuild.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 8fa40d6..71e21a8 100644 --- a/pom.xml +++ b/pom.xml @@ -2,10 +2,10 @@ 4.0.0 - - - - + net.thauvin.erik.mobibot + mobibot + 0.8.0-rc+20240715201342 + mobibot diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index e9343c5..5b15c86 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -210,7 +210,7 @@ public class MobibotBuild extends Project { @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory") public void pomRoot() throws FileUtilsErrorException { - PomBuilder.generateInto(publishOperation().info(), dependencies(), - Path.of(workDirectory.getPath(), "pom.xml").toFile()); + PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(), + new File(workDirectory, "pom.xml")); } } From 2905dec49f58fd9916091ade8033093a09c203af Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 9 Aug 2024 18:06:24 -0700 Subject: [PATCH 808/858] Bumped bld to version 2.0.1 --- .idea/bld.xml | 6 ++++++ .idea/libraries/bld.xml | 4 ++-- lib/bld/bld-wrapper.jar | Bin 29578 -> 29577 bytes lib/bld/bld-wrapper.properties | 10 +++++----- 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 .idea/bld.xml diff --git a/.idea/bld.xml b/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index 2fb5ff0..4dd96bf 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index f46bb77a290005df0476b73c34554b894620bf01..1d2b318f1cbb9e83f5b4e6d4d922146c95191b40 100644 GIT binary patch delta 27484 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 Date: Fri, 9 Aug 2024 18:10:59 -0700 Subject: [PATCH 809/858] Updated dependencies Bumped Kotlin to version 2.0.1 Bumped Commons Lang3 to version 3.16.0 Bumped slf4j-api to version 2.0.15 Bumped Commons Codec to version 1.17.1 Bumped Google Cloud VertexAI to version 1.7.0 --- README.md | 4 ++-- pom.xml | 18 +++++++++--------- .../java/net/thauvin/erik/MobibotBuild.java | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index eb38cfb..a4db76e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.0.0-7f52ff.svg)](https://kotlinlang.org) -[![bld](https://img.shields.io/badge/1.9.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![Kotlin](https://img.shields.io/badge/kotlin-2.0.10-7f52ff.svg)](https://kotlinlang.org) +[![bld](https://img.shields.io/badge/2.0.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/pom.xml b/pom.xml index 71e21a8..7996b05 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20240715201342 + 0.8.0-rc+20240809180718 mobibot @@ -18,7 +18,7 @@ org.apache.commons commons-lang3 - 3.14.0 + 3.16.0 compile @@ -30,7 +30,7 @@ commons-codec commons-codec - 1.17.0 + 1.17.1 compile @@ -54,31 +54,31 @@ com.google.cloud google-cloud-vertexai - 1.6.0 + 1.7.0 compile org.jetbrains.kotlin kotlin-stdlib - 2.0.0 + 2.0.10 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.0.0 + 2.0.10 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.0.0 + 2.0.10 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.0.0 + 2.0.10 compile @@ -96,7 +96,7 @@ org.slf4j slf4j-api - 2.0.13 + 2.0.15 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 5b15c86..1e6649b 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -78,19 +78,19 @@ public class MobibotBuild extends Project { SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 23, 1); - var kotlin = version(2, 0, 0); + var kotlin = version(2, 0, 10); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) - .include(dependency("org.apache.commons", "commons-lang3", "3.14.0")) + .include(dependency("org.apache.commons", "commons-lang3", "3.16.0")) .include(dependency("org.apache.commons", "commons-text", "1.12.0")) - .include(dependency("commons-codec", "commons-codec", "1.17.0")) + .include(dependency("commons-codec", "commons-codec", "1.17.1")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google .include(dependency("com.google.code.gson", "gson", "2.11.0")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.6.0")) + .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.7.0")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) @@ -99,7 +99,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.1")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.13")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.15")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) From ebc3da70e4b27d4b748446c02be1fbfa319da1ee Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 8 Sep 2024 17:39:18 -0700 Subject: [PATCH 810/858] Bumped bld to version 2.1.0 --- .idea/libraries/bld.xml | 4 ++-- .idea/libraries/compile.xml | 4 ++-- .idea/libraries/runtime.xml | 4 ++-- .idea/libraries/test.xml | 4 ++-- README.md | 4 ++-- lib/bld/bld-wrapper.jar | Bin 29577 -> 30440 bytes lib/bld/bld-wrapper.properties | 10 +++++----- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index 4dd96bf..5c4010c 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/.idea/libraries/compile.xml +++ b/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/.idea/libraries/runtime.xml +++ b/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/.idea/libraries/test.xml +++ b/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/README.md b/README.md index a4db76e..e438c94 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.0.10-7f52ff.svg)](https://kotlinlang.org) -[![bld](https://img.shields.io/badge/2.0.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![Kotlin](https://img.shields.io/badge/kotlin-2.0.20-7f52ff.svg)](https://kotlinlang.org) +[![bld](https://img.shields.io/badge/2.1.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 1d2b318f1cbb9e83f5b4e6d4d922146c95191b40..5425f1c05b18a00f2f1d69df4fc5885b310c372a 100644 GIT binary patch delta 15855 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 Ai~s-t delta 15009 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

    s$*rls@!sHBO;ZCZI;P{yKGwD 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 L_^*Fz5Rm@|Z!jRr diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 26eaf6a..47fd61f 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,10 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.5 -bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.8 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.7 -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.0 +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.6 +bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.1 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=2.0.1 +bld.version=2.1.0 From c60531f6170ed701ac742986cb8c5e13a6b823ea Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 8 Sep 2024 19:30:53 -0700 Subject: [PATCH 811/858] Moved to LangChain4J for ChatGPT and Gemini modules --- .gitignore | 1 + config/detekt/baseline.xml | 6 +- lib/bld/bld-wrapper.properties | 2 +- pom.xml | 50 +++++++++----- properties/mobibot.properties | 13 +--- .../java/net/thauvin/erik/MobibotBuild.java | 19 ++++-- .../net/thauvin/erik/mobibot/Mobibot.kt | 4 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 4 +- .../modules/{ChatGpt.kt => ChatGpt2.kt} | 67 ++++-------------- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../mobibot/modules/{Gemini.kt => Gemini2.kt} | 68 +++++-------------- .../{ChatGptTest.kt => ChatGpt2Test.kt} | 12 ++-- .../modules/{GeminiTest.kt => Gemini2Test.kt} | 15 ++-- website/index.html | 2 +- 14 files changed, 102 insertions(+), 163 deletions(-) rename src/main/kotlin/net/thauvin/erik/mobibot/modules/{ChatGpt.kt => ChatGpt2.kt} (62%) rename src/main/kotlin/net/thauvin/erik/mobibot/modules/{Gemini.kt => Gemini2.kt} (58%) rename src/test/kotlin/net/thauvin/erik/mobibot/modules/{ChatGptTest.kt => ChatGpt2Test.kt} (84%) rename src/test/kotlin/net/thauvin/erik/mobibot/modules/{GeminiTest.kt => Gemini2Test.kt} (79%) diff --git a/.gitignore b/.gitignore index 6c5fc87..d2daf1c 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ local.properties logs mobibot.properties out +/target/ diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index b20abc9..719ca4e 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -71,9 +71,6 @@ ReturnCount:Addons.kt$Addons$fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$fun ModuleException.sanitize(vararg sanitize: String): ModuleException ReturnCount:Seen.kt$Seen$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) - SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException - SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException - SwallowedException:WolframAlphaTest.kt$WolframAlphaTest$e: ModuleException ThrowsCount:ChatGpt.kt$ChatGpt.Companion$@JvmStatic @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$@JvmStatic @Throws(ModuleException::class) fun searchGoogle( query: String, apiKey: String?, cseKey: String?, quotaUser: String = ReleaseInfo.PROJECT ): List<Message> ThrowsCount:Joke.kt$Joke.Companion$@JvmStatic @Throws(ModuleException::class) fun randomJoke(): List<Message> @@ -82,7 +79,9 @@ ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject ThrowsCount:Weather2.kt$Weather2.Companion$@JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message> ThrowsCount:WolframAlpha.kt$WolframAlpha.Companion$@JvmStatic @Throws(ModuleException::class) fun queryWolfram(query: String, units: String = IMPERIAL, appId: String?): String + TooGenericExceptionCaught:ChatGpt2.kt$ChatGpt2.Companion$e: Exception TooGenericExceptionCaught:Gemini.kt$Gemini.Companion$e: Exception + TooGenericExceptionCaught:Gemini2.kt$Gemini2.Companion$e: Exception TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException TooManyFunctions:EntryLink.kt$EntryLink : Serializable @@ -94,6 +93,7 @@ WildcardImport:FeedMgrTest.kt$import assertk.assertions.* WildcardImport:FeedReaderTest.kt$import assertk.assertions.* WildcardImport:FeedsManager.kt$import com.rometools.rome.feed.synd.* + WildcardImport:Gemini2Test.kt$import assertk.assertions.* WildcardImport:GeminiTest.kt$import assertk.assertions.* WildcardImport:GoogleSearchTest.kt$import assertk.assertions.* WildcardImport:JokeTest.kt$import assertk.assertions.* diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 47fd61f..ed0fdb7 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,7 +1,7 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.6 +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.7 bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.1 diff --git a/pom.xml b/pom.xml index 7996b05..5d1b89a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20240809180718 + 0.8.0-rc+20240908192921 mobibot @@ -18,7 +18,7 @@ org.apache.commons commons-lang3 - 3.16.0 + 3.17.0 compile @@ -51,34 +51,28 @@ 33.2.1-jre compile - - com.google.cloud - google-cloud-vertexai - 1.7.0 - compile - org.jetbrains.kotlin kotlin-stdlib - 2.0.10 + 2.0.20 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.0.10 + 2.0.20 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.0.10 + 2.0.20 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.0.10 + 2.0.20 compile @@ -96,25 +90,49 @@ org.slf4j slf4j-api - 2.0.15 + 2.0.16 compile org.apache.logging.log4j log4j-api - 2.23.1 + 2.24.0 compile org.apache.logging.log4j log4j-core - 2.23.1 + 2.24.0 compile org.apache.logging.log4j log4j-slf4j2-impl - 2.23.1 + 2.24.0 + compile + + + dev.langchain4j + langchain4j-open-ai + 0.34.0 + compile + + + dev.langchain4j + langchain4j-google-ai-gemini + 0.34.0 + compile + + + dev.langchain4j + langchain4j-core + 0.34.0 + compile + + + dev.langchain4j + langchain4j + 0.34.0 compile diff --git a/properties/mobibot.properties b/properties/mobibot.properties index a7526cc..fd7b104 100644 --- a/properties/mobibot.properties +++ b/properties/mobibot.properties @@ -45,7 +45,6 @@ disabled-modules=mastodon # Automatically post links to Mastodon #mastodon-auto-post=true - # # Get Exchange Rate API key from: https://www.exchangerate-api.com/ # @@ -75,19 +74,13 @@ disabled-modules=mastodon #wolfram-units=imperial # -# ChatGPT/OpenAI API key from: https://beta.openai.com/account/api-keys +# Get ChatGPT/OpenAI API key from: https://platform.openai.com/api-keys # #chatgpt-api-key= #chatgpt-max-tokens=1024 # -# Set a Vertex AI Gemini API project in Google Cloud: -# https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/quickstart-multimodal -# Don't forget to: -# gcloud config set project PROJECT_ID -# gcloud auth login LOGIN -# gcloud auth application-default login +# Get Google Gemini API key from https://ai.google.dev/gemini-api/docs/api-key # -#gemini-project-id= -#gemini-location=us-west1 +#gemini-api-key= #gemini-max-tokens=1024 diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 1e6649b..980f2a6 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -77,20 +77,20 @@ public class MobibotBuild extends Project { new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); - var log4j = version(2, 23, 1); - var kotlin = version(2, 0, 10); + var log4j = version(2, 24, 0); + var kotlin = version(2, 0, 20); + var langchain = version(0, 34, 0); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) - .include(dependency("org.apache.commons", "commons-lang3", "3.16.0")) + .include(dependency("org.apache.commons", "commons-lang3", "3.17.0")) .include(dependency("org.apache.commons", "commons-text", "1.12.0")) .include(dependency("commons-codec", "commons-codec", "1.17.1")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google .include(dependency("com.google.code.gson", "gson", "2.11.0")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) - .include(dependency("com.google.cloud", "google-cloud-vertexai", "1.7.0")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) @@ -99,10 +99,15 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.1")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.15")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.16")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) + // LangChain4J + .include(dependency("dev.langchain4j", "langchain4j-open-ai", langchain)) + .include(dependency("dev.langchain4j", "langchain4j-google-ai-gemini", langchain)) + .include(dependency("dev.langchain4j", "langchain4j-core", langchain)) + .include(dependency("dev.langchain4j", "langchain4j", langchain)) // Misc. .include(dependency("com.rometools", "rome", "2.1.0")) .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) @@ -118,8 +123,8 @@ public class MobibotBuild extends Project { scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 3))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 3))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 0))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 0))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 88bed54..1e49a88 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -396,11 +396,11 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro // Load the modules addons.add(Calc()) - addons.add(ChatGpt()) + addons.add(ChatGpt2()) addons.add(CryptoPrices()) addons.add(CurrencyConverter()) addons.add(Dice()) - addons.add(Gemini()) + addons.add(Gemini2()) addons.add(GoogleSearch()) addons.add(Info(tell, seen)) addons.add(Joke()) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 3abf162..5b5e5d2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -14,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240712110931" + const val VERSION = "0.8.0-rc+20240908190240" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1720807771484L), ZoneId.systemDefault() + Instant.ofEpochMilli(1725847361020L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt similarity index 62% rename from src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt index c7ba4ba..854bb9f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt @@ -31,23 +31,16 @@ package net.thauvin.erik.mobibot.modules -import net.thauvin.erik.mobibot.Constants +import dev.langchain4j.model.openai.OpenAiChatModel +import dev.langchain4j.model.openai.OpenAiChatModelName import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage -import org.json.JSONArray -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.URI -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse -class ChatGpt : AbstractModule() { - val logger: Logger = LoggerFactory.getLogger(ChatGpt::class.java) +class ChatGpt2 : AbstractModule() { + val logger: Logger = LoggerFactory.getLogger(ChatGpt2::class.java) override val name = CHATGPT_NAME @@ -93,9 +86,6 @@ class ChatGpt : AbstractModule() { */ const val MAX_TOKENS_PROP = "chatgpt-max-tokens" - // ChatGPT API URL - private const val API_URL = "https://api.openai.com/v1/chat/completions" - // ChatGPT command private const val CHATGPT_CMD = "chatgpt" @@ -103,48 +93,15 @@ class ChatGpt : AbstractModule() { @Throws(ModuleException::class) fun chat(query: String, apiKey: String?, maxTokens: Int): String { if (!apiKey.isNullOrEmpty()) { - val jsonObject = JSONObject() - jsonObject.put("model", "gpt-3.5-turbo-1106") - jsonObject.put("max_tokens", maxTokens) - val message = JSONObject() - message.put("role", "user") - message.put("content", query) - val messages = JSONArray() - messages.put(message) - jsonObject.put("messages", messages) - - val request = HttpRequest.newBuilder() - .uri(URI.create(API_URL)) - .header("Content-Type", "application/json") - .header("Authorization", "Bearer $apiKey") - .header("User-Agent", Constants.USER_AGENT) - .POST(HttpRequest.BodyPublishers.ofString(jsonObject.toString())) - .build() try { - val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()) - if (response.statusCode() == 200) { - try { - val jsonResponse = JSONObject(response.body()) - val choices = jsonResponse.getJSONArray("choices") - return choices.getJSONObject(0).getJSONObject("message").getString("content").trim() - } catch (e: JSONException) { - throw ModuleException( - "$CHATGPT_CMD($query): JSON", - "A JSON error has occurred while conversing with $CHATGPT_NAME.", - e - ) - } - } else { - if (response.statusCode() == 429) { - throw ModuleException( - "$CHATGPT_CMD($query): Rate limit reached", - "Rate limit reached. Please try again later." - ) - } else { - throw IOException("HTTP Status Code: " + response.statusCode()) - } - } - } catch (e: IOException) { + val model = OpenAiChatModel.builder() + .apiKey(apiKey) + .modelName(OpenAiChatModelName.GPT_4_O) + .maxTokens(maxTokens) + .build() + + return model.generate(query) + } catch (e: Exception) { throw ModuleException( "$CHATGPT_CMD($query): IO", "An IO error has occurred while conversing with $CHATGPT_NAME.", diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 4fb7555..3c8ea92 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -217,6 +217,6 @@ class CurrencyConverter : AbstractModule() { init { commands.add(CURRENCY_CMD) initProperties(API_KEY_PROP) - loadSymbols(properties[ChatGpt.API_KEY_PROP]) + loadSymbols(properties[ChatGpt2.API_KEY_PROP]) } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt similarity index 58% rename from src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt rename to src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt index 2e4ed91..812aed3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt @@ -31,12 +31,7 @@ package net.thauvin.erik.mobibot.modules -import com.google.cloud.vertexai.VertexAI -import com.google.cloud.vertexai.api.GenerationConfig -import com.google.cloud.vertexai.api.HarmCategory -import com.google.cloud.vertexai.api.SafetySetting -import com.google.cloud.vertexai.generativeai.GenerativeModel -import com.google.cloud.vertexai.generativeai.ResponseHandler +import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel import net.thauvin.erik.mobibot.Utils import net.thauvin.erik.mobibot.Utils.sendMessage import org.pircbotx.hooks.types.GenericMessageEvent @@ -45,8 +40,8 @@ import org.slf4j.LoggerFactory import java.util.* -class Gemini : AbstractModule() { - private val logger: Logger = LoggerFactory.getLogger(Gemini::class.java) +class Gemini2 : AbstractModule() { + private val logger: Logger = LoggerFactory.getLogger(Gemini2::class.java) override val name = GEMINI_NAME @@ -55,8 +50,7 @@ class Gemini : AbstractModule() { try { val answer = chat( args.trim(), - properties[PROJECT_ID_PROP], - properties[LOCATION_PROP], + properties[GEMINI_API_KEY], properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt() ) if (!answer.isNullOrEmpty()) { @@ -82,17 +76,12 @@ class Gemini : AbstractModule() { const val GEMINI_NAME = "Gemini" /** - * The Google cloud project ID property. + * The API key */ - const val PROJECT_ID_PROP = "gemini-project-id" + const val GEMINI_API_KEY = "gemini-api-key" /** - * The Vertex AI location property. - */ - const val LOCATION_PROP = "gemini-location" - - /** - * The max number of tokens property. + * The max number of output tokens property. */ const val MAX_TOKENS_PROP = "gemini-max-tokens" @@ -103,40 +92,18 @@ class Gemini : AbstractModule() { @Throws(ModuleException::class) fun chat( query: String, - projectId: String?, - location: String?, - maxToken: Int + apiKey: String?, + maxTokens: Int ): String? { - if (!projectId.isNullOrEmpty() && !location.isNullOrEmpty()) { + if (!apiKey.isNullOrEmpty()) { try { - VertexAI(projectId, location).use { vertexAI -> - val generationConfig = GenerationConfig.newBuilder().setMaxOutputTokens(maxToken).build() - val safetySettings = listOf( - SafetySetting.newBuilder() - .setCategory(HarmCategory.HARM_CATEGORY_HATE_SPEECH) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) - .build(), - SafetySetting.newBuilder() - .setCategory(HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) - .build(), - SafetySetting.newBuilder() - .setCategory(HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) - .build(), - SafetySetting.newBuilder() - .setCategory(HarmCategory.HARM_CATEGORY_HARASSMENT) - .setThreshold(SafetySetting.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE) - .build() - ) - val model = GenerativeModel.Builder().setModelName("gemini-1.5-flash-001") - .setGenerationConfig(generationConfig) - .setVertexAi(vertexAI).build() - .withSafetySettings(safetySettings) + val gemini = GoogleAiGeminiChatModel.builder() + .apiKey(apiKey) + .modelName("gemini-1.5-flash") + .maxOutputTokens(maxTokens) + .build() - val response = model.generateContent(query) - return ResponseHandler.getText(response) - } + return gemini.generate(query) } catch (e: Exception) { throw ModuleException( "$GEMINI_CMD($query): IO", @@ -159,7 +126,6 @@ class Gemini : AbstractModule() { add(Utils.helpFormat("%c $GEMINI_CMD explain quantum computing in simple terms")) add(Utils.helpFormat("%c $GEMINI_CMD how do I make an HTTP request in Javascript?")) } - initProperties(PROJECT_ID_PROP, LOCATION_PROP, MAX_TOKENS_PROP) + initProperties(GEMINI_API_KEY, MAX_TOKENS_PROP) } - } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt similarity index 84% rename from src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt index 1809a9f..4332a52 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGptTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt @@ -39,10 +39,10 @@ import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.LocalProperties import kotlin.test.Test -class ChatGptTest : LocalProperties() { +class ChatGpt2Test : LocalProperties() { @Test fun testApiKey() { - assertFailure { ChatGpt.chat("1 gallon to liter", "", 0) } + assertFailure { ChatGpt2.chat("1 gallon to liter", "", 0) } .isInstanceOf(ModuleException::class.java) .hasNoCause() } @@ -51,7 +51,7 @@ class ChatGptTest : LocalProperties() { fun testChatOnCoverage() { if (System.getenv("CI") == null || System.getenv("COVERAGE_JDK") != null) { assertThat( - ChatGpt.chat("how do I encode a URL in java?", getProperty(ChatGpt.API_KEY_PROP), 60) + ChatGpt2.chat("how do I encode a URL in java?", getProperty(ChatGpt2.API_KEY_PROP), 60) ).contains("URLEncoder") } } @@ -59,12 +59,12 @@ class ChatGptTest : LocalProperties() { @Test @DisableOnCi fun testChat() { - val apiKey = getProperty(ChatGpt.API_KEY_PROP) + val apiKey = getProperty(ChatGpt2.API_KEY_PROP) assertThat( - ChatGpt.chat("how do I make an HTTP request in Javascript?", apiKey, 100) + ChatGpt2.chat("how do I make an HTTP request in Javascript?", apiKey, 200) ).contains("XMLHttpRequest") - assertFailure { ChatGpt.chat("1 liter to gallon", apiKey, -1) } + assertFailure { ChatGpt2.chat("1 liter to gallon", apiKey, -1) } .isInstanceOf(ModuleException::class.java) } } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt similarity index 79% rename from src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt rename to src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt index 1f0202f..226ff6a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GeminiTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt @@ -37,10 +37,10 @@ import net.thauvin.erik.mobibot.DisableOnCi import net.thauvin.erik.mobibot.LocalProperties import kotlin.test.Test -class GeminiTest : LocalProperties() { +class Gemini2Test : LocalProperties() { @Test fun testApiKey() { - assertFailure { Gemini.chat("1 gallon to liter", "", "", 1024) } + assertFailure { Gemini2.chat("1 gallon to liter", "", 0) } .isInstanceOf(ModuleException::class.java) .hasNoCause() } @@ -48,19 +48,18 @@ class GeminiTest : LocalProperties() { @Test @DisableOnCi fun chatPrompt() { - val projectId = getProperty(Gemini.PROJECT_ID_PROP) - val location = getProperty(Gemini.LOCATION_PROP) - val maxTokens = getProperty(Gemini.MAX_TOKENS_PROP).toInt() + val apiKey = getProperty(Gemini2.GEMINI_API_KEY) + val maxTokens = getProperty(Gemini2.MAX_TOKENS_PROP).toInt() assertThat( - Gemini.chat("how do I make an HTTP request in Javascript?", projectId, location, maxTokens) + Gemini2.chat("how do I make an HTTP request in Javascript?", apiKey, maxTokens) ).isNotNull().contains("XMLHttpRequest") assertThat( - Gemini.chat("how do I encode a URL in java?", projectId, location, 60) + Gemini2.chat("how do I encode a URL in java?", apiKey, 60) ).isNotNull().contains("URLEncoder") - assertFailure { Gemini.chat("1 liter to gallon", projectId, "blah", 40) } + assertFailure { Gemini2.chat("1 liter to gallon", "foo", 40) } .isInstanceOf(ModuleException::class.java) } } diff --git a/website/index.html b/website/index.html index fffe650..370c3cf 100644 --- a/website/index.html +++ b/website/index.html @@ -38,10 +38,10 @@

  31. Apache Commons Net
  32. CryptoPrice
  33. exp4j
  34. -
  35. Google Vertex AI
  36. JokeAPI
  37. jsoup
  38. kotlinx-cli
  39. +
  40. LangChain4J
  41. OkHttp
  42. OWM JAPIs
  43. Pinboard Poster
  44. From 863de0c21997ce569a4671f337fdea9114bad49a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 10 Sep 2024 00:08:51 -0700 Subject: [PATCH 812/858] Bumped Kotlin extension to version 1.0.2 --- lib/bld/bld-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index ed0fdb7..547a2bb 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -4,7 +4,7 @@ bld.downloadLocation= bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.7 bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.1 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.2 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=2.1.0 From ff98e39b0c72bff892158f751cb1d0cd6e3806d4 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 10 Sep 2024 00:09:47 -0700 Subject: [PATCH 813/858] Test against Kotlin 2.0.20 in CI workflows --- .circleci/config.yml | 2 +- .github/workflows/bld.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 17b50f1..8dabc3f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ commands: - sdkman/setup-sdkman - sdkman/sdkman-install: candidate: kotlin - version: 2.0.0 + version: 2.0.20 - run: name: Download dependencies command: ./bld download diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index a3c4951..f76f164 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,6 +1,6 @@ name: bld-ci -on: [ push, pull_request, workflow_dispatch ] +on: [push, pull_request, workflow_dispatch] env: ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -22,8 +22,8 @@ jobs: strategy: matrix: - java-version: [ 17, 21, 22 ] - kotlin-version: [ 1.9.24, 2.0.0 ] + java-version: [17, 21, 22] + kotlin-version: [1.9.24, 2.0.20] steps: - name: Checkout source repository From 97e1e5e73be534f0f638829c11080e3bbfa590b8 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 18 Sep 2024 20:28:58 -0700 Subject: [PATCH 814/858] Bumped Kotlin Coroutines to version 1.9.0 --- pom.xml | 4 ++-- src/bld/java/net/thauvin/erik/MobibotBuild.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 5d1b89a..a50df02 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20240908192921 + 0.8.0-rc+20240918181213 mobibot @@ -78,7 +78,7 @@ org.jetbrains.kotlinx kotlinx-coroutines-core - 1.8.1 + 1.9.0 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 980f2a6..6c4a09c 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -45,7 +45,6 @@ import rife.tools.exceptions.FileUtilsErrorException; import java.io.File; import java.io.IOException; -import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -96,7 +95,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) - .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.1")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.9.0")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging .include(dependency("org.slf4j", "slf4j-api", "2.0.16")) From 4b908d596ef08f712a9846158a0ed8ec7ef2ad7d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 18 Sep 2024 20:37:11 -0700 Subject: [PATCH 815/858] Converted deploy script to fish --- deploy.fish | 11 +++++++++++ deploy.sh | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) create mode 100755 deploy.fish delete mode 100755 deploy.sh diff --git a/deploy.fish b/deploy.fish new file mode 100755 index 0000000..d69ee4e --- /dev/null +++ b/deploy.fish @@ -0,0 +1,11 @@ +#!/usr/bin/env fish + +./bld clean jar deploy +if test $status -eq 0 + echo "cd /home/mobibot/mobitopia/mobibot +lcd deploy +put *.jar +cd lib +rm *.jar +put lib/*.jar" | sftp nix4 +end diff --git a/deploy.sh b/deploy.sh deleted file mode 100755 index d1c1f23..0000000 --- a/deploy.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -./bld clean jar deploy -[ $? -eq 0 ] && sftp nix4.thauvin.us < Date: Wed, 27 Nov 2024 22:24:45 -0800 Subject: [PATCH 816/858] Updated dependencies Bumped Kotlin to version 2.1.0 Bumped Log4J to version 2.24.2 Bumped LangChain4J to version 0.36.2 Bumped Jsoup to version 1.18.2 Bumped UrlEncoder to version 1.6.0 Bumped JUnit to version 5.11.3 --- .github/workflows/bld.yml | 4 ++-- .idea/kotlinc.xml | 16 ++++++++++++++++ README.md | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 14 +++++++------- 4 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 .idea/kotlinc.xml diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index f76f164..cb0ad5d 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -22,8 +22,8 @@ jobs: strategy: matrix: - java-version: [17, 21, 22] - kotlin-version: [1.9.24, 2.0.20] + java-version: [17, 21, 23] + kotlin-version: [1.9.24, 2.0.20, 2.1.0] steps: - name: Checkout source repository diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..0273acf --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index e438c94..b31ec67 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.0.20-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-2.1.0-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/2.1.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6c4a09c..3858951 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -76,9 +76,9 @@ public class MobibotBuild extends Project { new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); - var log4j = version(2, 24, 0); - var kotlin = version(2, 0, 20); - var langchain = version(0, 34, 0); + var log4j = version(2, 24, 2); + var kotlin = version(2, 1, 0); + var langchain = version(0, 36, 2); scope(compile) // PircBotX .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) @@ -113,17 +113,17 @@ public class MobibotBuild extends Project { .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) .include(dependency("org.json", "json", "20240303")) - .include(dependency("org.jsoup", "jsoup", "1.18.1")) + .include(dependency("org.jsoup", "jsoup", "1.18.2")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.2-SNAPSHOT")) .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.2-SNAPSHOT")) - .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.5.0")); + .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 0))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 0))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 3))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 3))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); From f27501634c7d6621284186c6bbebb53427ce03fb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 27 Nov 2024 22:26:46 -0800 Subject: [PATCH 817/858] Ensured HttpUrlConnection is properly disconnected --- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 6a8e4ff..92b4883 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -421,14 +421,22 @@ object Utils { @Throws(IOException::class) fun URL.reader(): UrlReaderResponse { val connection = this.openConnection() as HttpURLConnection - connection.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" - ) - return if (connection.responseCode.isHttpSuccess()) { - UrlReaderResponse(connection.responseCode, connection.inputStream.bufferedReader().use { it.readText() }) - } else { - UrlReaderResponse(connection.responseCode, connection.errorStream.bufferedReader().use { it.readText() }) + try { + connection.setRequestProperty( + "User-Agent", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + ) + return if (connection.responseCode.isHttpSuccess()) { + UrlReaderResponse( + connection.responseCode, + connection.inputStream.bufferedReader().use { it.readText() }) + } else { + UrlReaderResponse( + connection.responseCode, + connection.errorStream.bufferedReader().use { it.readText() }) + } + } finally { + connection.disconnect() } } From 83b4b18be743593ddf640140bad7d0f5c1e45551 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 27 Nov 2024 22:29:31 -0800 Subject: [PATCH 818/858] Added pom-root command --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 3858951..897a966 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -200,6 +200,12 @@ public class MobibotBuild extends Project { .execute(); } + @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory") + public void pomRoot() throws FileUtilsErrorException { + PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(), + new File(workDirectory, "pom.xml")); + } + @BuildCommand(value = "release-info", summary = "Generates the ReleaseInfo class") public void releaseInfo() throws Exception { new GeneratedVersionOperation() @@ -211,10 +217,4 @@ public class MobibotBuild extends Project { .extension(".kt") .execute(); } - - @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory") - public void pomRoot() throws FileUtilsErrorException { - PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(), - new File(workDirectory, "pom.xml")); - } } From 64bd5c92afdbd834f5833738d39f5e4ba371c49d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 27 Nov 2024 22:32:12 -0800 Subject: [PATCH 819/858] Don't clean/delete deploy dir if not there --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 897a966..c95d779 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -141,7 +141,10 @@ public class MobibotBuild extends Project { @Override public void clean() throws Exception { - FileUtils.deleteDirectory(new File("deploy")); + var deploy = new File("deploy"); + if (deploy.exists()) { + FileUtils.deleteDirectory(deploy); + } super.clean(); } From 700369d7c9b363c5ee5e992f2820269471b2bbf5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 21 Dec 2024 06:59:40 -0800 Subject: [PATCH 820/858] Updated dependencies Bumped JUnit to version 5.11.4 Bumped Log4j to version 2.24.3 Bumped Commons Text to version 1.13.0 Bumped Jsoup to version 1.18.3 Bumped Kotlin Coroutines to version 1.10.1 Bumped Kotlin extension to version 1.0.3 --- lib/bld/bld-wrapper.properties | 2 +- .../java/net/thauvin/erik/MobibotBuild.java | 20 ++++++++++--------- .../thauvin/erik/mobibot/modules/Gemini2.kt | 1 - 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 547a2bb..ae6925d 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -4,7 +4,7 @@ bld.downloadLocation= bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.7 bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.2 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.3 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=2.1.0 diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index c95d779..6800ec1 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -76,7 +76,7 @@ public class MobibotBuild extends Project { new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); - var log4j = version(2, 24, 2); + var log4j = version(2, 24, 3); var kotlin = version(2, 1, 0); var langchain = version(0, 36, 2); scope(compile) @@ -84,7 +84,7 @@ public class MobibotBuild extends Project { .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) .include(dependency("org.apache.commons", "commons-lang3", "3.17.0")) - .include(dependency("org.apache.commons", "commons-text", "1.12.0")) + .include(dependency("org.apache.commons", "commons-text", "1.13.0")) .include(dependency("commons-codec", "commons-codec", "1.17.1")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google @@ -95,7 +95,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) - .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.9.0")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.10.1")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging .include(dependency("org.slf4j", "slf4j-api", "2.0.16")) @@ -113,7 +113,7 @@ public class MobibotBuild extends Project { .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) .include(dependency("org.json", "json", "20240303")) - .include(dependency("org.jsoup", "jsoup", "1.18.2")) + .include(dependency("org.jsoup", "jsoup", "1.18.3")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) .include(dependency("net.thauvin.erik", "jokeapi", "0.9.2-SNAPSHOT")) @@ -122,8 +122,8 @@ public class MobibotBuild extends Project { scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 3))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 3))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 4))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 4))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); @@ -152,9 +152,11 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - new CompileKotlinOperation() - .fromProject(this) - .execute(); + var op = new CompileKotlinOperation() + .kotlinHome("/opt/kotlinc/") + .fromProject(this); + op.compileOptions().verbose(true); + op.execute(); } @Override diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt index 812aed3..365fe60 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt @@ -39,7 +39,6 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import java.util.* - class Gemini2 : AbstractModule() { private val logger: Logger = LoggerFactory.getLogger(Gemini2::class.java) From 4f0f4214c531fcd72fd6f18159069bc467628ac1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 21 Dec 2024 07:14:10 -0800 Subject: [PATCH 821/858] Updated POM --- pom.xml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index a50df02..8c805f1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20240918181213 + 0.8.0-rc+20241221064558 mobibot @@ -24,7 +24,7 @@ org.apache.commons commons-text - 1.12.0 + 1.13.0 compile @@ -54,31 +54,31 @@ org.jetbrains.kotlin kotlin-stdlib - 2.0.20 + 2.1.0 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.0.20 + 2.1.0 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.0.20 + 2.1.0 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.0.20 + 2.1.0 compile org.jetbrains.kotlinx kotlinx-coroutines-core - 1.9.0 + 1.10.1 compile @@ -96,43 +96,43 @@ org.apache.logging.log4j log4j-api - 2.24.0 + 2.24.3 compile org.apache.logging.log4j log4j-core - 2.24.0 + 2.24.3 compile org.apache.logging.log4j log4j-slf4j2-impl - 2.24.0 + 2.24.3 compile dev.langchain4j langchain4j-open-ai - 0.34.0 + 0.36.2 compile dev.langchain4j langchain4j-google-ai-gemini - 0.34.0 + 0.36.2 compile dev.langchain4j langchain4j-core - 0.34.0 + 0.36.2 compile dev.langchain4j langchain4j - 0.34.0 + 0.36.2 compile @@ -168,7 +168,7 @@ org.jsoup jsoup - 1.18.1 + 1.18.3 compile @@ -192,7 +192,7 @@ net.thauvin.erik.urlencoder urlencoder-lib-jvm - 1.5.0 + 1.6.0 compile From 67de345c2590401c0803fb1fbddffbdadb21359e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 21 Dec 2024 07:21:48 -0800 Subject: [PATCH 822/858] Fixed kotlin coverage version --- .github/workflows/bld.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index cb0ad5d..cf1ac97 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -7,7 +7,7 @@ env: CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }} CI_NAME: "GitHub CI" COVERAGE_JDK: "21" - COVERAGE_KOTLIN: "2.0.0" + COVERAGE_KOTLIN: "2.1.0" EXCHANGERATE_API_KEY: ${{ secrets.EXCHANGERATE_API_KEY }} KOTLIN_HOME: /usr/share/kotlinc MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} From 16ac7937fa001f90cb0f0e41089e467deccaa699 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 2 Jan 2025 17:58:37 -0800 Subject: [PATCH 823/858] build: Bump org.json to version 20241224 --- pom.xml | 6 +++--- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 8c805f1..5b7ef1c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20241221064558 + 0.8.0-rc+20250102175731 mobibot @@ -162,7 +162,7 @@ org.json json - 20240303 + 20241224 compile @@ -180,7 +180,7 @@ net.thauvin.erik jokeapi - 0.9.2-SNAPSHOT + 1.0.0-SNAPSHOT compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6800ec1..192b596 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -112,7 +112,7 @@ public class MobibotBuild extends Project { .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) - .include(dependency("org.json", "json", "20240303")) + .include(dependency("org.json", "json", "20241224")) .include(dependency("org.jsoup", "jsoup", "1.18.3")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) From f05ab9751361758d695f12d2a6afd24dd4003fb6 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 2 Jan 2025 17:59:13 -0800 Subject: [PATCH 824/858] build: Bump JokeApi to version 1.0.0-SNAPSHOT --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 192b596..a7164f8 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -116,7 +116,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jsoup", "jsoup", "1.18.3")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) - .include(dependency("net.thauvin.erik", "jokeapi", "0.9.2-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "jokeapi", "1.0.0-SNAPSHOT")) .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.2-SNAPSHOT")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0")); scope(test) From f58951846635baa4532115ed8d4edd586aaa300e Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 2 Jan 2025 18:00:17 -0800 Subject: [PATCH 825/858] chore: Update copyright notices for 2025 --- LICENSE.txt | 2 +- .../java/net/thauvin/erik/MobibotBuild.java | 2 +- .../kotlin/net/thauvin/erik/mobibot/Addons.kt | 2 +- .../net/thauvin/erik/mobibot/Constants.kt | 2 +- .../net/thauvin/erik/mobibot/FeedReader.kt | 2 +- .../net/thauvin/erik/mobibot/Mobibot.kt | 2 +- .../net/thauvin/erik/mobibot/Pinboard.kt | 2 +- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 35 +++++++++++++++++-- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- .../erik/mobibot/commands/AbstractCommand.kt | 2 +- .../erik/mobibot/commands/ChannelFeed.kt | 2 +- .../thauvin/erik/mobibot/commands/Cycle.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Die.kt | 2 +- .../thauvin/erik/mobibot/commands/Ignore.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Info.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Me.kt | 2 +- .../thauvin/erik/mobibot/commands/Modules.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Msg.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Nick.kt | 2 +- .../thauvin/erik/mobibot/commands/Recap.kt | 2 +- .../net/thauvin/erik/mobibot/commands/Say.kt | 2 +- .../thauvin/erik/mobibot/commands/Users.kt | 2 +- .../thauvin/erik/mobibot/commands/Versions.kt | 2 +- .../erik/mobibot/commands/links/Comment.kt | 2 +- .../mobibot/commands/links/LinksManager.kt | 2 +- .../erik/mobibot/commands/links/Posting.kt | 2 +- .../erik/mobibot/commands/links/Tags.kt | 2 +- .../erik/mobibot/commands/links/View.kt | 2 +- .../mobibot/commands/seen/NickComparator.kt | 2 +- .../erik/mobibot/commands/seen/Seen.kt | 2 +- .../erik/mobibot/commands/seen/SeenNick.kt | 2 +- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../erik/mobibot/commands/tell/TellManager.kt | 2 +- .../erik/mobibot/commands/tell/TellMessage.kt | 2 +- .../thauvin/erik/mobibot/entries/Entries.kt | 2 +- .../erik/mobibot/entries/EntriesUtils.kt | 2 +- .../erik/mobibot/entries/EntryComment.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 2 +- .../erik/mobibot/entries/FeedsManager.kt | 2 +- .../erik/mobibot/modules/AbstractModule.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Calc.kt | 2 +- .../thauvin/erik/mobibot/modules/ChatGpt2.kt | 4 +-- .../erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CurrencyConverter.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Dice.kt | 2 +- .../thauvin/erik/mobibot/modules/Gemini2.kt | 4 +-- .../erik/mobibot/modules/GoogleSearch.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Joke.kt | 2 +- .../thauvin/erik/mobibot/modules/Lookup.kt | 2 +- .../thauvin/erik/mobibot/modules/Mastodon.kt | 2 +- .../erik/mobibot/modules/ModuleException.kt | 2 +- .../net/thauvin/erik/mobibot/modules/Ping.kt | 2 +- .../erik/mobibot/modules/RockPaperScissors.kt | 2 +- .../erik/mobibot/modules/StockQuote.kt | 2 +- .../net/thauvin/erik/mobibot/modules/War.kt | 2 +- .../thauvin/erik/mobibot/modules/Weather2.kt | 2 +- .../erik/mobibot/modules/WolframAlpha.kt | 2 +- .../thauvin/erik/mobibot/modules/WorldTime.kt | 2 +- .../thauvin/erik/mobibot/msg/ErrorMessage.kt | 2 +- .../net/thauvin/erik/mobibot/msg/Message.kt | 2 +- .../thauvin/erik/mobibot/msg/NoticeMessage.kt | 2 +- .../erik/mobibot/msg/PrivateMessage.kt | 2 +- .../thauvin/erik/mobibot/msg/PublicMessage.kt | 2 +- .../erik/mobibot/social/SocialManager.kt | 2 +- .../erik/mobibot/social/SocialModule.kt | 2 +- .../erik/mobibot/social/SocialTimer.kt | 2 +- .../net/thauvin/erik/mobibot/AddonsTest.kt | 2 +- .../net/thauvin/erik/mobibot/DisableOnCi.kt | 2 +- .../erik/mobibot/DisableOnCiCondition.kt | 2 +- .../erik/mobibot/ExceptionSanitizer.kt | 2 +- .../thauvin/erik/mobibot/FeedReaderTest.kt | 2 +- .../thauvin/erik/mobibot/LocalProperties.kt | 2 +- .../net/thauvin/erik/mobibot/PinboardTest.kt | 2 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 2 +- .../thauvin/erik/mobibot/commands/InfoTest.kt | 2 +- .../erik/mobibot/commands/RecapTest.kt | 2 +- .../commands/links/LinksManagerTest.kt | 2 +- .../erik/mobibot/commands/links/ViewTest.kt | 2 +- .../erik/mobibot/commands/seen/SeenTest.kt | 2 +- .../mobibot/commands/tell/TellMessageTest.kt | 2 +- .../commands/tell/TellMessagesMgrTest.kt | 2 +- .../erik/mobibot/entries/EntriesUtilsTest.kt | 2 +- .../erik/mobibot/entries/EntryLinkTest.kt | 2 +- .../erik/mobibot/entries/FeedMgrTest.kt | 2 +- .../thauvin/erik/mobibot/modules/CalcTest.kt | 2 +- .../erik/mobibot/modules/ChatGpt2Test.kt | 4 +-- .../erik/mobibot/modules/CryptoPricesTest.kt | 2 +- .../mobibot/modules/CurrencyConverterTest.kt | 2 +- .../thauvin/erik/mobibot/modules/DiceTest.kt | 2 +- .../erik/mobibot/modules/Gemini2Test.kt | 4 +-- .../erik/mobibot/modules/GoogleSearchTest.kt | 2 +- .../thauvin/erik/mobibot/modules/JokeTest.kt | 2 +- .../erik/mobibot/modules/LookupTest.kt | 2 +- .../erik/mobibot/modules/MastodonTest.kt | 2 +- .../mobibot/modules/ModuleExceptionTest.kt | 2 +- .../thauvin/erik/mobibot/modules/PingTest.kt | 2 +- .../mobibot/modules/RockPaperScissorsTest.kt | 2 +- .../erik/mobibot/modules/StockQuoteTest.kt | 2 +- .../erik/mobibot/modules/Weather2Test.kt | 2 +- .../erik/mobibot/modules/WolframAlphaTest.kt | 2 +- .../erik/mobibot/modules/WordTimeTest.kt | 2 +- .../thauvin/erik/mobibot/msg/MessageTest.kt | 2 +- 102 files changed, 138 insertions(+), 107 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 16e13ca..54e8774 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) +Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index a7164f8..6aa683c 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -1,7 +1,7 @@ /* * MobibotBuild.java * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt index f3ae0a3..ed34897 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt @@ -1,7 +1,7 @@ /* * Addons.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index 8716e14..d00939e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -1,7 +1,7 @@ /* * Constants.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt index a91c6dc..3ab8d1f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt @@ -1,7 +1,7 @@ /* * FeedReader.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 1e49a88..691a339 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -1,7 +1,7 @@ /* * Mobibot.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt index 88e475e..f9076c9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt @@ -1,7 +1,7 @@ /* * Pinboard.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 5b5e5d2..7571a9f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -1,3 +1,34 @@ +/* + * ReleaseInfo.kt + * + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) + * + * 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. + */ + /* * This file is automatically generated * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -14,12 +45,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20240908190240" + const val VERSION = "0.8.0-rc+20250102175731" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1725847361020L), ZoneId.systemDefault() + Instant.ofEpochMilli(1735869451156L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 92b4883..fc5ee7d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -1,7 +1,7 @@ /* * Utils.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt index 5f79fab..4642f42 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/AbstractCommand.kt @@ -1,7 +1,7 @@ /* * AbstractCommand.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt index 873d31f..0075293 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/ChannelFeed.kt @@ -1,7 +1,7 @@ /* * ChannelFeed.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt index 88b6c6d..cefcde3 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt @@ -1,7 +1,7 @@ /* * Cycle.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt index e4e111d..d7577af 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Die.kt @@ -1,7 +1,7 @@ /* * Die.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt index 6dfff07..13b20b0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt @@ -1,7 +1,7 @@ /* * Ignore.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt index 4fae22f..8e244cc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt @@ -1,7 +1,7 @@ /* * Info.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt index 3495b28..afa9046 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt @@ -1,7 +1,7 @@ /* * Me.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt index 314ce99..8668bf7 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt @@ -1,7 +1,7 @@ /* * Modules.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt index bddd56c..14d8d8e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Msg.kt @@ -1,7 +1,7 @@ /* * Msg.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt index 59b474a..21c96b5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt @@ -1,7 +1,7 @@ /* * Nick.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 886b26c..500fd85 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -1,7 +1,7 @@ /* * Recap.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt index 90f3c04..b9d410d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt @@ -1,7 +1,7 @@ /* * Say.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt index 54a85b2..960b8aa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt @@ -1,7 +1,7 @@ /* * Users.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt index f422566..62cb044 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Versions.kt @@ -1,7 +1,7 @@ /* * Versions.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt index 2fade4e..f0d9d0c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Comment.kt @@ -1,7 +1,7 @@ /* * Comment.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt index ea18613..e688092 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManager.kt @@ -1,7 +1,7 @@ /* * LinksManager.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt index 2d24ba7..a47021b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Posting.kt @@ -1,7 +1,7 @@ /* * Posting.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt index 1a582a9..0d73f6e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/Tags.kt @@ -1,7 +1,7 @@ /* * Tags.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index e6971ef..6891c2d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -1,7 +1,7 @@ /* * View.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt index e51a6bf..f44b357 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/NickComparator.kt @@ -1,7 +1,7 @@ /* * NickComparator.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt index 1ad12f6..8af98dc 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/Seen.kt @@ -1,7 +1,7 @@ /* * Seen.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt index 69923db..21d7cb9 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenNick.kt @@ -1,7 +1,7 @@ /* * SeenNick.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 790ce0e..c204062 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -1,7 +1,7 @@ /* * Tell.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt index f34af98..f193a3c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellManager.kt @@ -1,7 +1,7 @@ /* * TellManager.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt index ac15595..1f55687 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessage.kt @@ -1,7 +1,7 @@ /* * TellMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt index 676fbee..4e187d4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/Entries.kt @@ -1,7 +1,7 @@ /* * Entries.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt index ea536bc..1588704 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtils.kt @@ -1,7 +1,7 @@ /* * EntriesUtils.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt index d9d5587..1826101 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryComment.kt @@ -1,7 +1,7 @@ /* * EntryComment.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index c88f8a4..a807f07 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -1,7 +1,7 @@ /* * EntryLink.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt index 4dc8e9e..a881204 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/FeedsManager.kt @@ -1,7 +1,7 @@ /* * FeedsManager.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt index 17dbde2..1ced830 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/AbstractModule.kt @@ -1,7 +1,7 @@ /* * AbstractModule.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt index b5fe5b1..7fd320f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Calc.kt @@ -1,7 +1,7 @@ /* * Calc.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt index 854bb9f..9d654c8 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt @@ -1,7 +1,7 @@ /* - * ChatGpt.kt + * ChatGpt2.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index fe95fe0..2186d73 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -1,7 +1,7 @@ /* * CryptoPrices.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index 3c8ea92..2ff4715 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -1,7 +1,7 @@ /* * CurrencyConverter.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt index b6d69ec..5c1dd09 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Dice.kt @@ -1,7 +1,7 @@ /* * Dice.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt index 365fe60..385e96e 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt @@ -1,7 +1,7 @@ /* - * Gemini.kt + * Gemini2.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt index 1611130..26f3e71 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearch.kt @@ -1,7 +1,7 @@ /* * GoogleSearch.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt index 1c6a5eb..e792ed4 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Joke.kt @@ -1,7 +1,7 @@ /* * Joke.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt index baeebba..f700757 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Lookup.kt @@ -1,7 +1,7 @@ /* * Lookup.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt index 166e39f..d4c2614 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Mastodon.kt @@ -1,7 +1,7 @@ /* * Mastodon.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 32240ca..26d374a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -1,7 +1,7 @@ /* * ModuleException.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt index 4b56df5..ca18216 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Ping.kt @@ -1,7 +1,7 @@ /* * Ping.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt index 13afbb7..b8c81f1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissors.kt @@ -1,7 +1,7 @@ /* * RockPaperScissors.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt index 13b9329..d71c91a 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/StockQuote.kt @@ -1,7 +1,7 @@ /* * StockQuote.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt index e0fd947..70ac4ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/War.kt @@ -1,7 +1,7 @@ /* * War.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt index 21bf0ae..074edd0 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Weather2.kt @@ -1,7 +1,7 @@ /* * Weather2.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt index ba88711..2e2e7ec 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WolframAlpha.kt @@ -1,7 +1,7 @@ /* * WolframAlpha.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt index 8978275..afc0a5f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/WorldTime.kt @@ -1,7 +1,7 @@ /* * WorldTime.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt index abcd043..56e7b92 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/ErrorMessage.kt @@ -1,7 +1,7 @@ /* * ErrorMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt index 4b788ba..1a6e58b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/Message.kt @@ -1,7 +1,7 @@ /* * Message.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt index ea11c9c..f06ce89 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/NoticeMessage.kt @@ -1,7 +1,7 @@ /* * NoticeMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt index 27b1cbd..ef0eeb1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PrivateMessage.kt @@ -1,7 +1,7 @@ /* * PrivateMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt index ae909f6..be6583f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/msg/PublicMessage.kt @@ -1,7 +1,7 @@ /* * PublicMessage.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt index c785106..de9653d 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialManager.kt @@ -1,7 +1,7 @@ /* * SocialManager.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt index a13b429..d45cf5c 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialModule.kt @@ -1,7 +1,7 @@ /* * SocialModule.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt index e722ed8..aadebf5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/social/SocialTimer.kt @@ -1,7 +1,7 @@ /* * SocialTimer.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt index 4555884..27163fb 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/AddonsTest.kt @@ -1,7 +1,7 @@ /* * AddonsTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt index 8d73a5e..75a1cf9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCi.kt @@ -1,7 +1,7 @@ /* * DisableOnCi.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt index b0f4771..d887b55 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/DisableOnCiCondition.kt @@ -1,7 +1,7 @@ /* * DisableOnCiCondition.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt index d1ca71d..3a4adb0 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/ExceptionSanitizer.kt @@ -1,7 +1,7 @@ /* * ExceptionSanitizer.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt index 1cb2645..0c3d1c6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/FeedReaderTest.kt @@ -1,7 +1,7 @@ /* * FeedReaderTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt index c290262..646a0ea 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/LocalProperties.kt @@ -1,7 +1,7 @@ /* * LocalProperties.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt index 7e56912..dafb862 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/PinboardTest.kt @@ -1,7 +1,7 @@ /* * PinboardTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 49ee7fa..bd05f70 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -1,7 +1,7 @@ /* * UtilsTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt index add701e..f332005 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/InfoTest.kt @@ -1,7 +1,7 @@ /* * InfoTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt index 875342b..ef6f461 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -1,7 +1,7 @@ /* * RecapTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt index 59338f4..676c5b6 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksManagerTest.kt @@ -1,7 +1,7 @@ /* * LinksManagerTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt index 9eb433a..abf8224 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -1,7 +1,7 @@ /* * ViewTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt index 6d5ba78..7b946dc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/seen/SeenTest.kt @@ -1,7 +1,7 @@ /* * SeenTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt index 33b3598..443c1f9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessageTest.kt @@ -1,7 +1,7 @@ /* * TellMessageTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt index d7bde4c..6d3bb6b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/tell/TellMessagesMgrTest.kt @@ -1,7 +1,7 @@ /* * TellMessagesMgrTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt index 983e225..f67a057 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntriesUtilsTest.kt @@ -1,7 +1,7 @@ /* * EntriesUtilsTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt index 577f425..3479108 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/EntryLinkTest.kt @@ -1,7 +1,7 @@ /* * EntryLinkTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt index d2b959e..5803092 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/entries/FeedMgrTest.kt @@ -1,7 +1,7 @@ /* * FeedMgrTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt index e7ef601..140b8a1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CalcTest.kt @@ -1,7 +1,7 @@ /* * CalcTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt index 4332a52..c4699a5 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt @@ -1,7 +1,7 @@ /* - * ChatGptTest.kt + * ChatGpt2Test.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 0e2ef84..34b8369 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -1,7 +1,7 @@ /* * CryptoPricesTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt index 5ebb7dc..c1c0efc 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverterTest.kt @@ -1,7 +1,7 @@ /* * CurrencyConverterTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt index 007f22f..e34de7b 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/DiceTest.kt @@ -1,7 +1,7 @@ /* * DiceTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt index 226ff6a..269874a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Gemini2Test.kt @@ -1,7 +1,7 @@ /* - * GeminiTest.kt + * Gemini2Test.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt index 752b49e..f9b0832 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/GoogleSearchTest.kt @@ -1,7 +1,7 @@ /* * GoogleSearchTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt index 118d736..cf6d03c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/JokeTest.kt @@ -1,7 +1,7 @@ /* * JokeTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt index 048d91a..abb9235 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/LookupTest.kt @@ -1,7 +1,7 @@ /* * LookupTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt index 11d4a51..a9e1d43 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/MastodonTest.kt @@ -1,7 +1,7 @@ /* * MastodonTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt index ff706fd..6c3c54c 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ModuleExceptionTest.kt @@ -1,7 +1,7 @@ /* * ModuleExceptionTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt index 5532e30..f51e203 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/PingTest.kt @@ -1,7 +1,7 @@ /* * PingTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt index 0e4b4ec..519037a 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/RockPaperScissorsTest.kt @@ -1,7 +1,7 @@ /* * RockPaperScissorsTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt index 3fe9189..955a267 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/StockQuoteTest.kt @@ -1,7 +1,7 @@ /* * StockQuoteTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt index f4f85f8..5d04560 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/Weather2Test.kt @@ -1,7 +1,7 @@ /* * Weather2Test.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt index 4f827bb..099f3f9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WolframAlphaTest.kt @@ -1,7 +1,7 @@ /* * WolframAlphaTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt index 1211ea4..396efaf 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/WordTimeTest.kt @@ -1,7 +1,7 @@ /* * WordTimeTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt index 44054f1..6e85ed1 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/msg/MessageTest.kt @@ -1,7 +1,7 @@ /* * MessageTest.kt * - * Copyright 2004-2024 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: From 1a1195ca285df29351f72c882aa9ea8e4e4bda46 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 05:06:57 -0700 Subject: [PATCH 826/858] Bump bld to version 2.2.1 --- .idea/libraries/bld.xml | 4 ++-- README.md | 2 +- lib/bld/bld-wrapper.jar | Bin 30440 -> 30440 bytes lib/bld/bld-wrapper.properties | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index 5c4010c..153a060 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/README.md b/README.md index b31ec67..8d62a9f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.1.0-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/2.1.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 5425f1c05b18a00f2f1d69df4fc5885b310c372a..73cde27f29a182647776c29655512b0bfa0749f0 100644 GIT binary patch delta 203 zcmaFymhr_~M!o=VW)=|!4h{|mzs(U*6ZvXcK=j6b6B{sNa-Y4%=Jz(UnZUfwefC{Y zmW&gd9z;y7;2a}_^`p=LB0E{9L{8*i9|;9^1_lOJAP(?mWD;RO*f%+^ q#0g^0mJ$yzV^V1_L?9f-SXb%-5&2dc3zaJK;{dr5Wb)*-W!3VxO6ZvXcK=j6b6B{sNa-Y4%=Jz(UnZUfwefC{Y zmW&gd9z;y7;2a}_^`p=LB0E{9L{8<3F#|(@H#^70qR3Eo1_lOJAP(?mWD;RO*f%+^ q#0g^0mJ$yzV^V1_L?9f-SXb%-5&2dc3zaJK;{dr5Wb)*-W!3<}MnA^@ diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index ae6925d..478b956 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -7,4 +7,4 @@ bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.3 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=2.1.0 +bld.version=2.2.1 From 765279c88718837b52d84792e979ad29b3a59d33 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 05:08:37 -0700 Subject: [PATCH 827/858] Bump Kotlin to version 2.1.10 --- .circleci/config.yml | 2 +- .github/workflows/bld.yml | 2 +- README.md | 2 +- lib/bld/bld-wrapper.properties | 4 +--- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8dabc3f..8814d91 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ commands: - sdkman/setup-sdkman - sdkman/sdkman-install: candidate: kotlin - version: 2.0.20 + version: 2.1.10 - run: name: Download dependencies command: ./bld download diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index cf1ac97..48f1a71 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: java-version: [17, 21, 23] - kotlin-version: [1.9.24, 2.0.20, 2.1.0] + kotlin-version: [1.9.25, 2.0.20, 2.1.10] steps: - name: Checkout source repository diff --git a/README.md b/README.md index 8d62a9f..84d0ebc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![bld](https://img.shields.io/badge/2.1.0-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![Kotlin](https://img.shields.io/badge/kotlin-2.1.10-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 478b956..0a01ba1 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,10 +1,8 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.7 -bld.extension-gv=com.uwyn.rife2:bld-generated-version:0.9.9 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.8 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.3 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.4 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=2.2.1 diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6aa683c..6a9386a 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -77,7 +77,7 @@ public class MobibotBuild extends Project { SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 24, 3); - var kotlin = version(2, 1, 0); + var kotlin = version(2, 1, 10); var langchain = version(0, 36, 2); scope(compile) // PircBotX From 4d2f1e7978315c4b3aad5d41f81d7e295138a1fb Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 05:15:01 -0700 Subject: [PATCH 828/858] Update dependencies Bump Detekt extension to version 0.9.9 Bump Generated Version extension t version 1.0.0 Bump Commons Codec to version 1.18.8 Bump Gson to version 2.12.1 Bump SLF4J to version 2.0.17 Bump org.json to version 20250107 Bump Jsoup to version 1.19.1 Bump Junit to version 5.12.1 --- lib/bld/bld-wrapper.properties | 4 +++- pom.xml | 24 +++++++++---------- .../java/net/thauvin/erik/MobibotBuild.java | 19 ++++++++------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 0a01ba1..c1b1941 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,7 +1,9 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.3 +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.9 +bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.0 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.4 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= diff --git a/pom.xml b/pom.xml index 5b7ef1c..3543235 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20250102175731 + 0.8.0-rc+20250318045311 mobibot @@ -30,7 +30,7 @@ commons-codec commons-codec - 1.17.1 + 1.18.0 compile @@ -42,7 +42,7 @@ com.google.code.gson gson - 2.11.0 + 2.12.1 compile @@ -54,25 +54,25 @@ org.jetbrains.kotlin kotlin-stdlib - 2.1.0 + 2.1.10 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.1.0 + 2.1.10 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.1.0 + 2.1.10 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.1.0 + 2.1.10 compile @@ -90,7 +90,7 @@ org.slf4j slf4j-api - 2.0.16 + 2.0.17 compile @@ -162,13 +162,13 @@ org.json json - 20241224 + 20250107 compile org.jsoup jsoup - 1.18.3 + 1.19.1 compile @@ -180,13 +180,13 @@ net.thauvin.erik jokeapi - 1.0.0-SNAPSHOT + 1.0.0 compile net.thauvin.erik pinboard-poster - 1.1.2-SNAPSHOT + 1.2.0 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6a9386a..9ffb588 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -85,10 +85,10 @@ public class MobibotBuild extends Project { // Commons (mostly for PircBotX) .include(dependency("org.apache.commons", "commons-lang3", "3.17.0")) .include(dependency("org.apache.commons", "commons-text", "1.13.0")) - .include(dependency("commons-codec", "commons-codec", "1.17.1")) + .include(dependency("commons-codec", "commons-codec", "1.18.0")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google - .include(dependency("com.google.code.gson", "gson", "2.11.0")) + .include(dependency("com.google.code.gson", "gson", "2.12.1")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) @@ -98,7 +98,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.10.1")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging - .include(dependency("org.slf4j", "slf4j-api", "2.0.16")) + .include(dependency("org.slf4j", "slf4j-api", "2.0.17")) .include(dependency("org.apache.logging.log4j", "log4j-api", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-core", log4j)) .include(dependency("org.apache.logging.log4j", "log4j-slf4j2-impl", log4j)) @@ -112,18 +112,19 @@ public class MobibotBuild extends Project { .include(dependency("com.squareup.okhttp3", "okhttp", "4.12.0")) .include(dependency("net.aksingh", "owm-japis", "2.5.3.0")) .include(dependency("net.objecthunter", "exp4j", "0.4.8")) - .include(dependency("org.json", "json", "20241224")) - .include(dependency("org.jsoup", "jsoup", "1.18.3")) + .include(dependency("org.json", "json", "20250107")) + .include(dependency("org.jsoup", "jsoup", "1.19.1")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) - .include(dependency("net.thauvin.erik", "jokeapi", "1.0.0-SNAPSHOT")) - .include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.2-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "jokeapi", "1.0.0")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.2.0")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 11, 4))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 11, 4))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 12, 1))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); From bfc468de5bd10bfa0631f04ad81551f18f19a7d7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 05:16:20 -0700 Subject: [PATCH 829/858] Bump JDK from version 20 to 21 --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8814d91..c781fdc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,11 +46,11 @@ jobs: steps: - build_and_test - bld_jdk20: + bld_jdk21: <<: *defaults docker: - - image: cimg/openjdk:20.0 + - image: cimg/openjdk:21.0 steps: - build_and_test @@ -59,4 +59,4 @@ workflows: bld: jobs: - bld_jdk17 - - bld_jdk20 + - bld_jdk21 From 00178b4e168eb4702c5e9d0f6f830d752b814e75 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 14:55:36 -0700 Subject: [PATCH 830/858] Update User-Agent --- src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt | 3 +-- src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt index d00939e..0dea8d5 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Constants.kt @@ -62,8 +62,7 @@ object Constants { /** * User-Agent */ - const val USER_AGENT = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" + const val USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0" /** * The help command. diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index fc5ee7d..97797ef 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -424,7 +424,7 @@ object Utils { try { connection.setRequestProperty( "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0" + Constants.USER_AGENT ) return if (connection.responseCode.isHttpSuccess()) { UrlReaderResponse( From daf5cad807417cb65735de31cdec23dcfa612bbd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 23:14:34 -0700 Subject: [PATCH 831/858] JDK 24 --- .github/workflows/bld.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 48f1a71..3511dc4 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - java-version: [17, 21, 23] + java-version: [17, 21, 24] kotlin-version: [1.9.25, 2.0.20, 2.1.10] steps: From e974f3f4bc08484733bf7679968b0e57d80b6a45 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 23:55:13 -0700 Subject: [PATCH 832/858] Bump to GPT4 --- src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt index 9d654c8..fbf0e94 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2.kt @@ -96,7 +96,7 @@ class ChatGpt2 : AbstractModule() { try { val model = OpenAiChatModel.builder() .apiKey(apiKey) - .modelName(OpenAiChatModelName.GPT_4_O) + .modelName(OpenAiChatModelName.GPT_4) .maxTokens(maxTokens) .build() From 068ba7b6e5290257326d1989fe9ac1a97feec408 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 18 Mar 2025 23:55:29 -0700 Subject: [PATCH 833/858] Bump to Gemini 2.0 Flash --- src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt index 385e96e..e85ea7b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini2.kt @@ -98,7 +98,7 @@ class Gemini2 : AbstractModule() { try { val gemini = GoogleAiGeminiChatModel.builder() .apiKey(apiKey) - .modelName("gemini-1.5-flash") + .modelName("gemini-2.0-flash") .maxOutputTokens(maxTokens) .build() From 23797a2a04d7da96c6413c6ae30fe0b5f73e5bea Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 20 Mar 2025 10:38:09 -0700 Subject: [PATCH 834/858] Bump Kotlin to version 2.1.20 --- README.md | 2 +- pom.xml | 10 +++++----- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 84d0ebc..d43ba67 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mobibot [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Kotlin](https://img.shields.io/badge/kotlin-2.1.10-7f52ff.svg)](https://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-2.1.20-7f52ff.svg)](https://kotlinlang.org) [![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/bld.yml) diff --git a/pom.xml b/pom.xml index 3543235..c278f8f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20250318045311 + 0.8.0-rc+20250320103735 mobibot @@ -54,25 +54,25 @@ org.jetbrains.kotlin kotlin-stdlib - 2.1.10 + 2.1.20 compile org.jetbrains.kotlin kotlin-stdlib-common - 2.1.10 + 2.1.20 compile org.jetbrains.kotlin kotlin-stdlib-jdk7 - 2.1.10 + 2.1.20 compile org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.1.10 + 2.1.20 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 9ffb588..1ac9eab 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -77,7 +77,7 @@ public class MobibotBuild extends Project { SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 24, 3); - var kotlin = version(2, 1, 10); + var kotlin = version(2, 1, 20); var langchain = version(0, 36, 2); scope(compile) // PircBotX From 8537c8a436b449df90eaf5a1895a1f5381231c9c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 20 Mar 2025 10:40:29 -0700 Subject: [PATCH 835/858] Add Kotlin compile options for JDK 24 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 1ac9eab..8a8edd9 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -38,6 +38,7 @@ import rife.bld.extension.CompileKotlinOperation; import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; +import rife.bld.extension.kotlin.CompileOptions; import rife.bld.operations.exceptions.ExitStatusException; import rife.bld.publish.PomBuilder; import rife.tools.FileUtils; @@ -153,10 +154,12 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); + final var options = new CompileOptions(); + options.verbose(true).jvmOptions().add("--enable-native-access=ALL-UNNAMED"); var op = new CompileKotlinOperation() .kotlinHome("/opt/kotlinc/") + .compileOptions(options) .fromProject(this); - op.compileOptions().verbose(true); op.execute(); } From 5f1eb63c4fdb798d9a25c5e706ed61f7af6d6659 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 20 Mar 2025 10:54:32 -0700 Subject: [PATCH 836/858] Combine Kotlin compile options --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 8a8edd9..65e7e0c 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -154,8 +154,7 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - final var options = new CompileOptions(); - options.verbose(true).jvmOptions().add("--enable-native-access=ALL-UNNAMED"); + var options = new CompileOptions().verbose(true).jvmOptions("--enable-native-access=ALL-UNNAMED"); var op = new CompileKotlinOperation() .kotlinHome("/opt/kotlinc/") .compileOptions(options) From 4b7f41660fdd717aa2312c476f0ab90a70f9fcb0 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 21 Mar 2025 23:36:28 -0700 Subject: [PATCH 837/858] Bump Kotlin extension to version 1.1.0-SNAPSHOT --- lib/bld/bld-wrapper.properties | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index c1b1941..4986847 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -4,7 +4,7 @@ bld.downloadLocation= bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.9 bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.0 bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9 -bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.4 +bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= bld.version=2.2.1 diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 65e7e0c..42436b7 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -39,6 +39,7 @@ import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; import rife.bld.extension.kotlin.CompileOptions; +import rife.bld.extension.kotlin.JvmOptions; import rife.bld.operations.exceptions.ExitStatusException; import rife.bld.publish.PomBuilder; import rife.tools.FileUtils; @@ -154,12 +155,12 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - var options = new CompileOptions().verbose(true).jvmOptions("--enable-native-access=ALL-UNNAMED"); - var op = new CompileKotlinOperation() - .kotlinHome("/opt/kotlinc/") + var options = new CompileOptions().verbose(true); + options.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); + new CompileKotlinOperation() .compileOptions(options) - .fromProject(this); - op.execute(); + .fromProject(this) + .execute(); } @Override From 45f602fbe2af7cbfccd1f2b2020539e2ee6f6d87 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 21 Mar 2025 23:37:17 -0700 Subject: [PATCH 838/858] Update snapshot dependencies Bump Pinboard Poster to version 1.2.1-SNAPSHOT Bump JokeApi to version 1.0.1-SNAPSHOT --- pom.xml | 6 +++--- src/bld/java/net/thauvin/erik/MobibotBuild.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index c278f8f..a3a9f14 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20250320103735 + 0.8.0-rc+20250321233454 mobibot @@ -180,13 +180,13 @@ net.thauvin.erik jokeapi - 1.0.0 + 1.0.1-SNAPSHOT compile net.thauvin.erik pinboard-poster - 1.2.0 + 1.2.1-SNAPSHOT compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 42436b7..c2c5f38 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -118,8 +118,8 @@ public class MobibotBuild extends Project { .include(dependency("org.jsoup", "jsoup", "1.19.1")) // Thauvin .include(dependency("net.thauvin.erik", "cryptoprice", "1.0.3-SNAPSHOT")) - .include(dependency("net.thauvin.erik", "jokeapi", "1.0.0")) - .include(dependency("net.thauvin.erik", "pinboard-poster", "1.2.0")) + .include(dependency("net.thauvin.erik", "jokeapi", "1.0.1-SNAPSHOT")) + .include(dependency("net.thauvin.erik", "pinboard-poster", "1.2.1-SNAPSHOT")) .include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0")); scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) From 6f1aa72ad018034120c49c4d8f69808a72ac51dd Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:15:10 -0700 Subject: [PATCH 839/858] Add build/test job for Windows --- .github/workflows/bld.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 3511dc4..2627b79 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: java-version: [17, 21, 24] - kotlin-version: [1.9.25, 2.0.20, 2.1.10] + kotlin-version: [1.9.25, 2.0.20, 2.1.20] steps: - name: Checkout source repository @@ -56,3 +56,27 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + build-bld-windows: + runs-on: windows-latest + + steps: + - name: Checkout source repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: "zulu" + java-version: "17" + + - name: Download dependencies + run: bld.bat download + + - name: Compile source + run: bld.bat compile + + - name: Run tests + run: bld.bat jacoco From 1f5903c5e92a2cb3829a4737f5517ab83bba3353 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:19:17 -0700 Subject: [PATCH 840/858] Fix bld executable name under Windows --- .github/workflows/bld.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 2627b79..1d57288 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -72,11 +72,13 @@ jobs: distribution: "zulu" java-version: "17" + - run: dir + - name: Download dependencies - run: bld.bat download + run: bld download - name: Compile source - run: bld.bat compile + run: bld compile - name: Run tests - run: bld.bat jacoco + run: bld jacoco From 93d761b13bd7289aebf62f1c4a671c7232229420 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:24:22 -0700 Subject: [PATCH 841/858] Change run to script in Windows job --- .github/workflows/bld.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 1d57288..6d9f43f 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,6 +1,6 @@ name: bld-ci -on: [push, pull_request, workflow_dispatch] +on: [ push, pull_request, workflow_dispatch ] env: ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -22,8 +22,8 @@ jobs: strategy: matrix: - java-version: [17, 21, 24] - kotlin-version: [1.9.25, 2.0.20, 2.1.20] + java-version: [ 17, 21, 24 ] + kotlin-version: [ 1.9.25, 2.0.20, 2.1.20 ] steps: - name: Checkout source repository @@ -72,13 +72,11 @@ jobs: distribution: "zulu" java-version: "17" - - run: dir - - name: Download dependencies - run: bld download + script: bld.bat download - name: Compile source - run: bld compile + script: bld.bat compile - name: Run tests - run: bld jacoco + script: bld.bat jacoco From 599d819dc9a9d6d2f04730262ba40ff0e797d917 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:28:21 -0700 Subject: [PATCH 842/858] Add call to Windows run steps --- .github/workflows/bld.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 6d9f43f..4f69f88 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,6 +1,6 @@ name: bld-ci -on: [ push, pull_request, workflow_dispatch ] +on: [push, pull_request, workflow_dispatch] env: ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }} @@ -22,8 +22,8 @@ jobs: strategy: matrix: - java-version: [ 17, 21, 24 ] - kotlin-version: [ 1.9.25, 2.0.20, 2.1.20 ] + java-version: [17, 21, 24] + kotlin-version: [1.9.25, 2.0.20, 2.1.20] steps: - name: Checkout source repository @@ -73,10 +73,10 @@ jobs: java-version: "17" - name: Download dependencies - script: bld.bat download + run: call bld.bat download - name: Compile source - script: bld.bat compile + run: call bld.bat compile - name: Run tests - script: bld.bat jacoco + run: call bld.bat jacoco From 48b92c33fdd8d2f973f346e1c0da7c29c16915c2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:35:11 -0700 Subject: [PATCH 843/858] Set the shell cmd under Windows --- .github/workflows/bld.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 4f69f88..13359e8 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -73,10 +73,13 @@ jobs: java-version: "17" - name: Download dependencies - run: call bld.bat download + run: bld.bat download + shell: cmd - name: Compile source - run: call bld.bat compile + run: bld.bat compile + shell: cmd - name: Run tests - run: call bld.bat jacoco + run: bld.bat jacoco + shell: cmd From 7e58e5840f685ef66908640342fb5f2b5a75219d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:45:24 -0700 Subject: [PATCH 844/858] Revert "Add build/test job for Windows" This reverts commit 6f1aa72a --- .github/workflows/bld.yml | 29 +-------------- .../net/thauvin/erik/mobibot/ReleaseInfo.kt | 35 ++----------------- .../erik/mobibot/commands/tell/Tell.kt | 2 +- 3 files changed, 4 insertions(+), 62 deletions(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 13359e8..3511dc4 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: java-version: [17, 21, 24] - kotlin-version: [1.9.25, 2.0.20, 2.1.20] + kotlin-version: [1.9.25, 2.0.20, 2.1.10] steps: - name: Checkout source repository @@ -56,30 +56,3 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - build-bld-windows: - runs-on: windows-latest - - steps: - - name: Checkout source repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: "zulu" - java-version: "17" - - - name: Download dependencies - run: bld.bat download - shell: cmd - - - name: Compile source - run: bld.bat compile - shell: cmd - - - name: Run tests - run: bld.bat jacoco - shell: cmd diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt index 7571a9f..42a61aa 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/ReleaseInfo.kt @@ -1,34 +1,3 @@ -/* - * ReleaseInfo.kt - * - * Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net) - * - * 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. - */ - /* * This file is automatically generated * Do not modify! -- ALL CHANGES WILL BE ERASED! @@ -45,12 +14,12 @@ import java.time.ZoneId */ object ReleaseInfo { const val PROJECT = "mobibot" - const val VERSION = "0.8.0-rc+20250102175731" + const val VERSION = "0.8.0-rc+20250322004101" @JvmField @Suppress("MagicNumber") val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(1735869451156L), ZoneId.systemDefault() + Instant.ofEpochMilli(1742629261438L), ZoneId.systemDefault() ) const val WEBSITE = "https://mobitopia.org/mobibot/" diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index c204062..26fe803 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -287,7 +287,7 @@ class Tell(private val serialObject: String) : AbstractCommand() { // All keyword private const val TELL_ALL_KEYWORD = "all" - //T he delete command. + // The delete command. private const val TELL_DEL_KEYWORD = "del" } From 6173639cdd9cc2fb3b093aa2fdfa600dafd12899 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 22 Mar 2025 00:47:11 -0700 Subject: [PATCH 845/858] Bump Kotlin matrix version to 2.1.20 --- .github/workflows/bld.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 3511dc4..8e7d939 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: java-version: [17, 21, 24] - kotlin-version: [1.9.25, 2.0.20, 2.1.10] + kotlin-version: [1.9.25, 2.0.20, 2.1.20] steps: - name: Checkout source repository From 75ccf1a0ed649f50881e32438a8b154238ecba92 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 23 Mar 2025 20:02:55 -0700 Subject: [PATCH 846/858] Set progressive compiling --- .../java/net/thauvin/erik/MobibotBuild.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index c2c5f38..940ff0d 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -52,6 +52,9 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.jar.Attributes; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; @@ -139,6 +142,15 @@ public class MobibotBuild extends Project { } public static void main(String[] args) { + var level = Level.ALL; + var logger = Logger.getLogger("rife.bld.extension"); + var consoleHandler = new ConsoleHandler(); + + consoleHandler.setLevel(level); + logger.addHandler(consoleHandler); + logger.setLevel(level); + logger.setUseParentHandlers(false); + new MobibotBuild().start(args); } @@ -155,12 +167,11 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - var options = new CompileOptions().verbose(true); - options.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); - new CompileKotlinOperation() - .compileOptions(options) - .fromProject(this) - .execute(); + var options = new CompileOptions().progressive(true).verbose(true); + var op = new CompileKotlinOperation().compileOptions(options).fromProject(this); + + op.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); + op.execute(); } @Override From eed8277ed630a24361f65b5f70593b0229bddef5 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 26 Mar 2025 23:32:56 -0700 Subject: [PATCH 847/858] Add API response logging --- .../thauvin/erik/mobibot/modules/CryptoPrices.kt | 2 +- .../erik/mobibot/modules/CryptoPricesTest.kt | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt index 2186d73..3334a90 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CryptoPrices.kt @@ -52,7 +52,7 @@ class CryptoPrices : AbstractModule() { /** * Returns the cryptocurrency market price from - * [Coinbase](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices#get-spot-price). + * [Coinbase](https://docs.cdp.coinbase.com/coinbase-app/docs/api-prices#get-spot-price). */ override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { if (CURRENCIES.isEmpty()) { diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt index 34b8369..94a40d9 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/CryptoPricesTest.kt @@ -39,6 +39,9 @@ import net.thauvin.erik.crypto.CryptoPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies +import org.junit.jupiter.api.BeforeAll +import java.util.logging.ConsoleHandler +import java.util.logging.Level import kotlin.test.Test class CryptoPricesTest { @@ -69,4 +72,16 @@ class CryptoPricesTest { assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar") assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro") } + + companion object { + @JvmStatic + @BeforeAll + fun beforeAll() { + with(CryptoPrice.logger) { + addHandler(ConsoleHandler().apply { level = Level.FINE }) + level = Level.FINE + useParentHandlers = false + } + } + } } From 310687cdce336af58c2be739196fefc775884d68 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 26 Mar 2025 23:33:32 -0700 Subject: [PATCH 848/858] Update to latest extensions snapshots --- lib/bld/bld-wrapper.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 4986847..976017e 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,9 +1,9 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.9 -bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.0 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9 +bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.10-SNAPSHOT +bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.1-SNAPSHOT +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10-SNAPSHOT bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= From fedebc7ed41e59c7decb0ede800992060f140e66 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 26 Mar 2025 23:34:14 -0700 Subject: [PATCH 849/858] Cleanup compile command --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 940ff0d..4f6868d 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -39,7 +39,6 @@ import rife.bld.extension.DetektOperation; import rife.bld.extension.GeneratedVersionOperation; import rife.bld.extension.JacocoReportOperation; import rife.bld.extension.kotlin.CompileOptions; -import rife.bld.extension.kotlin.JvmOptions; import rife.bld.operations.exceptions.ExitStatusException; import rife.bld.publish.PomBuilder; import rife.tools.FileUtils; @@ -167,11 +166,10 @@ public class MobibotBuild extends Project { @Override public void compile() throws Exception { releaseInfo(); - var options = new CompileOptions().progressive(true).verbose(true); - var op = new CompileKotlinOperation().compileOptions(options).fromProject(this); - - op.jvmOptions().enableNativeAccess(JvmOptions.ALL_UNNAMED); - op.execute(); + new CompileKotlinOperation() + .compileOptions(new CompileOptions().progressive(true).verbose(true)) + .fromProject(this) + .execute(); } @Override From abc33d05f734c95860cd4717969551019ff06ea2 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:28:38 -0700 Subject: [PATCH 850/858] Bump JUnit to version 5.12.2 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 4f6868d..ec448d7 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -126,9 +126,9 @@ public class MobibotBuild extends Project { scope(test) .include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1))) .include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin)) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))) - .include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 12, 1))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 12, 2))); List jars = new ArrayList<>(); runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName())); From 82b40a49b273e469393e40d5f83852a8ce48a541 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:29:32 -0700 Subject: [PATCH 851/858] Update extensions Bump JaCoCo Reports to version 0.9.10 Bump Generated Version to version 1.0.1 --- lib/bld/bld-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 976017e..8b96558 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -2,8 +2,8 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.10-SNAPSHOT -bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.1-SNAPSHOT -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10-SNAPSHOT +bld.extension-gv=com.uwyn.rife2:bld-generated-version:1.0.1 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10 bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= From 491f451acb90d7d74f926d5c5c6cc9e83811f867 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:30:07 -0700 Subject: [PATCH 852/858] Bump Commons Text to version 1.13.1 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index ec448d7..63957d4 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -88,7 +88,7 @@ public class MobibotBuild extends Project { .include(dependency("com.github.pircbotx", "pircbotx", "2.3.1")) // Commons (mostly for PircBotX) .include(dependency("org.apache.commons", "commons-lang3", "3.17.0")) - .include(dependency("org.apache.commons", "commons-text", "1.13.0")) + .include(dependency("org.apache.commons", "commons-text", "1.13.1")) .include(dependency("commons-codec", "commons-codec", "1.18.0")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google From ef9bbe6e1fd27984f40f0a6639bf64c8f982cec1 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:30:30 -0700 Subject: [PATCH 853/858] Bump Gson to version 2.13.0 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 63957d4..b152cd3 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -92,7 +92,7 @@ public class MobibotBuild extends Project { .include(dependency("commons-codec", "commons-codec", "1.18.0")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google - .include(dependency("com.google.code.gson", "gson", "2.12.1")) + .include(dependency("com.google.code.gson", "gson", "2.13.0")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin)) From 9e49da5d56187b788363bb07a3be715af9413684 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 13 Apr 2025 13:30:58 -0700 Subject: [PATCH 854/858] Bump Kotlinx Coroutines to version 1.10.2 --- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index b152cd3..6ed1d06 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -99,7 +99,7 @@ public class MobibotBuild extends Project { .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin)) .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin)) - .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.10.1")) + .include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.10.2")) .include(dependency("org.jetbrains.kotlinx", "kotlinx-cli-jvm", "0.3.6")) // Logging .include(dependency("org.slf4j", "slf4j-api", "2.0.17")) From 749334493bca7090c1fcb549a064802995c30e4a Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 18 Apr 2025 15:55:57 -0700 Subject: [PATCH 855/858] Add JitPack token --- .circleci/config.yml | 2 +- .github/workflows/bld.yml | 2 +- .gitlab-ci.yml | 2 +- bitbucket-pipelines.yml | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c781fdc..9586fcd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ commands: version: 2.1.10 - run: name: Download dependencies - command: ./bld download + command: ./bld -Djitpack.token=$JITPACK_TOKEN download - run: name: Compile source command: ./bld compile diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 8e7d939..5b88e5a 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -41,7 +41,7 @@ jobs: run: ./bld download - name: Compile source - run: ./bld compile + run: ./bld -Djitpack.token=${{ secrets.JITPACK_TOKEN }} compile - name: Run tests run: ./bld jacoco diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 10b9b0f..a2a4384 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,6 +19,6 @@ before_script: test: stage: test script: - - ./bld download + - ./bld -Djitpack.token=$JITPACK_TOKEN download - ./bld compile - ./bld test diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index ace99d2..132ed8d 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -15,6 +15,6 @@ pipelines: - sdk install kotlin - source "$HOME/.sdkman/bin/sdkman-init.sh" # Download, compile and test with bld - - ./bld download + - ./bld -Djitpack.token=$JITPACK_TOKEN download - ./bld compile - ./bld test diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6ed1d06..e4da3b1 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -67,7 +67,7 @@ public class MobibotBuild extends Project { pkg = "net.thauvin.erik.mobibot"; name = "mobibot"; version = version(0, 8, 0, "rc+" + - DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); mainClass = pkg + ".Mobibot"; @@ -77,7 +77,7 @@ public class MobibotBuild extends Project { repositories = List.of( MAVEN_LOCAL, MAVEN_CENTRAL, - new Repository("https://jitpack.io"), + new Repository("https://jitpack.io").withCredentials(property("jitpack.token"), "."), SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 24, 3); From 31e882eb42741adeea637a34b48d573d31ee860d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 18 Apr 2025 17:02:45 -0700 Subject: [PATCH 856/858] Remove test on coverage --- .../net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt index c4699a5..ee3e534 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/modules/ChatGpt2Test.kt @@ -47,15 +47,6 @@ class ChatGpt2Test : LocalProperties() { .hasNoCause() } - @Test - fun testChatOnCoverage() { - if (System.getenv("CI") == null || System.getenv("COVERAGE_JDK") != null) { - assertThat( - ChatGpt2.chat("how do I encode a URL in java?", getProperty(ChatGpt2.API_KEY_PROP), 60) - ).contains("URLEncoder") - } - } - @Test @DisableOnCi fun testChat() { From 86522d6b3ef8b5a2a46aca4220c232292c702b7c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 18 Apr 2025 17:24:30 -0700 Subject: [PATCH 857/858] Revert "Add JitPack token" This reverts commit 749334493bca7090c1fcb549a064802995c30e4a. --- .circleci/config.yml | 2 +- .github/workflows/bld.yml | 2 +- .gitlab-ci.yml | 2 +- bitbucket-pipelines.yml | 2 +- src/bld/java/net/thauvin/erik/MobibotBuild.java | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9586fcd..c781fdc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ commands: version: 2.1.10 - run: name: Download dependencies - command: ./bld -Djitpack.token=$JITPACK_TOKEN download + command: ./bld download - run: name: Compile source command: ./bld compile diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 5b88e5a..8e7d939 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -41,7 +41,7 @@ jobs: run: ./bld download - name: Compile source - run: ./bld -Djitpack.token=${{ secrets.JITPACK_TOKEN }} compile + run: ./bld compile - name: Run tests run: ./bld jacoco diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a2a4384..10b9b0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,6 +19,6 @@ before_script: test: stage: test script: - - ./bld -Djitpack.token=$JITPACK_TOKEN download + - ./bld download - ./bld compile - ./bld test diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 132ed8d..ace99d2 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -15,6 +15,6 @@ pipelines: - sdk install kotlin - source "$HOME/.sdkman/bin/sdkman-init.sh" # Download, compile and test with bld - - ./bld -Djitpack.token=$JITPACK_TOKEN download + - ./bld download - ./bld compile - ./bld test diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index e4da3b1..6ed1d06 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -67,7 +67,7 @@ public class MobibotBuild extends Project { pkg = "net.thauvin.erik.mobibot"; name = "mobibot"; version = version(0, 8, 0, "rc+" + - DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())); mainClass = pkg + ".Mobibot"; @@ -77,7 +77,7 @@ public class MobibotBuild extends Project { repositories = List.of( MAVEN_LOCAL, MAVEN_CENTRAL, - new Repository("https://jitpack.io").withCredentials(property("jitpack.token"), "."), + new Repository("https://jitpack.io"), SONATYPE_SNAPSHOTS_LEGACY); var log4j = version(2, 24, 3); From b4754f13115e16a10073bdef12eeacd0152174af Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 24 Apr 2025 11:32:11 -0700 Subject: [PATCH 858/858] Bump Gson to version 2.13.1 --- pom.xml | 8 ++++---- src/bld/java/net/thauvin/erik/MobibotBuild.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a3a9f14..58768ef 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.thauvin.erik.mobibot mobibot - 0.8.0-rc+20250321233454 + 0.8.0-rc+20250424113056 mobibot @@ -24,7 +24,7 @@ org.apache.commons commons-text - 1.13.0 + 1.13.1 compile @@ -42,7 +42,7 @@ com.google.code.gson gson - 2.12.1 + 2.13.1 compile @@ -78,7 +78,7 @@ org.jetbrains.kotlinx kotlinx-coroutines-core - 1.10.1 + 1.10.2 compile diff --git a/src/bld/java/net/thauvin/erik/MobibotBuild.java b/src/bld/java/net/thauvin/erik/MobibotBuild.java index 6ed1d06..08367ef 100644 --- a/src/bld/java/net/thauvin/erik/MobibotBuild.java +++ b/src/bld/java/net/thauvin/erik/MobibotBuild.java @@ -92,7 +92,7 @@ public class MobibotBuild extends Project { .include(dependency("commons-codec", "commons-codec", "1.18.0")) .include(dependency("commons-net", "commons-net", "3.11.1")) // Google - .include(dependency("com.google.code.gson", "gson", "2.13.0")) + .include(dependency("com.google.code.gson", "gson", "2.13.1")) .include(dependency("com.google.guava", "guava", "33.2.1-jre")) // Kotlin .include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin))

    Q 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 diff --git a/lib/delicious-1.14.jar b/lib/delicious-1.14.jar deleted file mode 100644 index bf441fa52faa2f93f05d8c4ca5334e10189f29ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23859 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 diff --git a/lib/google.jar b/lib/google.jar deleted file mode 100644 index 391aba604eb6025b53c4279ba9d685680976c87d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20290 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 TZ#qhKMbU|`^SI;qJl0Qt;DG{J diff --git a/lib/googleapi.jar b/lib/googleapi.jar deleted file mode 100644 index 2aade51c1638e5ea89290352a6e9056a0a650c51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 624236 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