From 3a1c7059911ae515c58e2565991bd9bfa48c7fd1 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:09:31 +0100 Subject: [PATCH 01/66] bootstrap rewrite --- .gitignore | 34 +- LICENSE | 875 ++++-------------- README.md | 69 +- commandEmbeds.json | 522 ----------- docs.md | 549 ----------- jdacEmbeds.json | 118 --- pom.xml | 88 +- .../kaktushose/nplaybot/Bootstrapper.java | 14 + .../de/kaktushose/levelbot/Bootstrapper.java | 78 -- .../de/kaktushose/levelbot/bot/Levelbot.java | 432 --------- .../levelbot/bot/ShutdownHttpServer.java | 50 - .../levelbot/bot/TaskScheduler.java | 22 - .../levelbot/commands/PermissionsService.java | 50 - .../commands/member/BotInfoCommand.java | 30 - .../member/ChangeDiamondsCommand.java | 67 -- .../levelbot/commands/member/GiftCommand.java | 54 -- .../commands/member/LeaderboardCommand.java | 141 --- .../levelbot/commands/member/PingCommand.java | 28 - .../commands/member/RankInfoCommand.java | 27 - .../commands/member/SwitchDailyCommand.java | 44 - .../commands/moderation/BlacklistCommand.java | 80 -- .../moderation/BulkDeleteCommand.java | 46 - .../moderation/ChangeCurrencyCommand.java | 81 -- .../moderation/IgnoreChannelCommand.java | 63 -- .../moderation/SetCurrencyCommand.java | 77 -- .../commands/moderation/SetPermsCommand.java | 55 -- .../moderation/WelcomeEmbedsCommand.java | 37 - .../events/BalanceEventCommand.java | 70 -- .../events/CollectEventCommand.java | 81 -- .../events/ContestEventCommand.java | 68 -- .../levelbot/commands/owner/EvalCommand.java | 66 -- .../levelbot/commands/owner/StopCommand.java | 56 -- .../levelbot/database/model/BotUser.java | 221 ----- .../levelbot/database/model/CollectEvent.java | 107 --- .../levelbot/database/model/ContestEntry.java | 55 -- .../database/model/CurrencyChance.java | 68 -- .../database/model/GuildSettings.java | 145 --- .../levelbot/database/model/NitroBooster.java | 49 - .../levelbot/database/model/Rank.java | 86 -- .../levelbot/database/model/Reward.java | 80 -- .../repositories/ChancesRepository.java | 26 - .../repositories/CollectEventRepository.java | 7 - .../repositories/ContestRepository.java | 16 - .../repositories/NitroBoosterRepository.java | 14 - .../database/repositories/RankRepository.java | 13 - .../repositories/RewardRepository.java | 20 - .../repositories/SettingsRepository.java | 39 - .../database/repositories/UserRepository.java | 33 - .../database/services/BoosterService.java | 155 ---- .../database/services/EventService.java | 181 ---- .../database/services/LevelService.java | 215 ----- .../database/services/SettingsService.java | 133 --- .../database/services/UserService.java | 198 ---- .../listener/ContestEventListener.java | 108 --- .../listener/DailyRewardListener.java | 65 -- .../levelbot/listener/JoinLeaveListener.java | 31 - .../levelbot/listener/LevelListener.java | 113 --- .../levelbot/shop/ShopListener.java | 206 ----- .../shop/commands/AddItemCommand.java | 245 ----- .../shop/commands/InitShopCommand.java | 66 -- .../shop/commands/RemoveItemCommand.java | 85 -- .../shop/commands/SetPriceCommand.java | 34 - .../shop/commands/ShopDeprecatedCommand.java | 25 - .../levelbot/shop/data/ShopService.java | 163 ---- .../levelbot/shop/data/items/FrozenItem.java | 68 -- .../shop/data/items/FrozenItemRepository.java | 6 - .../levelbot/shop/data/items/Item.java | 106 --- .../shop/data/items/ItemCategory.java | 28 - .../shop/data/items/ItemRepository.java | 16 - .../levelbot/shop/data/items/ItemVariant.java | 18 - .../shop/data/transactions/Transaction.java | 61 -- .../transactions/TransactionRepository.java | 29 - .../spring/ApplicationContextHolder.java | 22 - .../levelbot/util/NumberEmojis.java | 20 - .../de/kaktushose/levelbot/util/Pageable.java | 9 - .../kaktushose/levelbot/util/Pagination.java | 85 -- .../kaktushose/levelbot/util/Statistics.java | 123 --- src/main/resources/META-INF/MANIFEST.MF | 2 +- src/test/java/BoosterServiceTest.java | 114 --- src/test/java/LevelbotMock.java | 32 - src/test/java/SettingsServiceMock.java | 16 - src/test/resources/application.properties | 2 - src/test/resources/truncate_all.sql | 2 - welcomeEmbeds.json | 131 --- 84 files changed, 260 insertions(+), 7704 deletions(-) delete mode 100644 commandEmbeds.json delete mode 100644 docs.md delete mode 100644 jdacEmbeds.json create mode 100644 src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java delete mode 100644 src/main/java/de/kaktushose/levelbot/Bootstrapper.java delete mode 100644 src/main/java/de/kaktushose/levelbot/bot/Levelbot.java delete mode 100644 src/main/java/de/kaktushose/levelbot/bot/ShutdownHttpServer.java delete mode 100644 src/main/java/de/kaktushose/levelbot/bot/TaskScheduler.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/PermissionsService.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/member/BotInfoCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/member/ChangeDiamondsCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/member/GiftCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/member/LeaderboardCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/member/PingCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/member/RankInfoCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/member/SwitchDailyCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/BlacklistCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/BulkDeleteCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/ChangeCurrencyCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/IgnoreChannelCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/SetCurrencyCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/SetPermsCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/WelcomeEmbedsCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/events/BalanceEventCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/events/CollectEventCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/moderation/events/ContestEventCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/owner/EvalCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/commands/owner/StopCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/model/BotUser.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/model/CollectEvent.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/model/ContestEntry.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/model/CurrencyChance.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/model/GuildSettings.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/model/NitroBooster.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/model/Rank.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/model/Reward.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/repositories/ChancesRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/repositories/CollectEventRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/repositories/ContestRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/repositories/NitroBoosterRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/repositories/RankRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/repositories/RewardRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/repositories/SettingsRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/repositories/UserRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/services/BoosterService.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/services/EventService.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/services/LevelService.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/services/SettingsService.java delete mode 100644 src/main/java/de/kaktushose/levelbot/database/services/UserService.java delete mode 100644 src/main/java/de/kaktushose/levelbot/listener/ContestEventListener.java delete mode 100644 src/main/java/de/kaktushose/levelbot/listener/DailyRewardListener.java delete mode 100644 src/main/java/de/kaktushose/levelbot/listener/JoinLeaveListener.java delete mode 100644 src/main/java/de/kaktushose/levelbot/listener/LevelListener.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/ShopListener.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/commands/AddItemCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/commands/InitShopCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/commands/RemoveItemCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/commands/SetPriceCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/commands/ShopDeprecatedCommand.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/ShopService.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/items/FrozenItem.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/items/FrozenItemRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/items/Item.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/items/ItemCategory.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/items/ItemRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/items/ItemVariant.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/transactions/Transaction.java delete mode 100644 src/main/java/de/kaktushose/levelbot/shop/data/transactions/TransactionRepository.java delete mode 100644 src/main/java/de/kaktushose/levelbot/spring/ApplicationContextHolder.java delete mode 100644 src/main/java/de/kaktushose/levelbot/util/NumberEmojis.java delete mode 100644 src/main/java/de/kaktushose/levelbot/util/Pageable.java delete mode 100644 src/main/java/de/kaktushose/levelbot/util/Pagination.java delete mode 100644 src/main/java/de/kaktushose/levelbot/util/Statistics.java delete mode 100644 src/test/java/BoosterServiceTest.java delete mode 100644 src/test/java/LevelbotMock.java delete mode 100644 src/test/java/SettingsServiceMock.java delete mode 100644 src/test/resources/application.properties delete mode 100644 src/test/resources/truncate_all.sql delete mode 100644 welcomeEmbeds.json diff --git a/.gitignore b/.gitignore index 109df20..8e84f37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,34 @@ +### IntelliJ ### +out/ .idea/ +*.iml +modules.xml +*.ipr + +### Java ### +# Compiled class file +*.class +# Log file +*.log +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +### Maven ### target/ -logs/ -src/main/resources/application.properties +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties dependency-reduced-pom.xml -*.iml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +### Project ### diff --git a/LICENSE b/LICENSE index e72bfdd..261eeb9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,201 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. \ No newline at end of file + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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. diff --git a/README.md b/README.md index 4a466cc..a09f7d3 100644 --- a/README.md +++ b/README.md @@ -2,82 +2,17 @@ [![Deploy](https://github.com/Kaktushose/Levelbot/actions/workflows/deploy.yml/badge.svg)](https://github.com/Kaktushose/Levelbot/actions/workflows/deploy.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/6eaa0127de99428795b4f5f759da188a)](https://www.codacy.com/gh/Kaktushose/Levelbot/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Kaktushose/Levelbot&utm_campaign=Badge_Grade) ![Generic badge](https://img.shields.io/badge/Version-2.3.0-86c240".svg) -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![license-shield](https://img.shields.io/badge/License-Apache%202.0-lightgrey.svg)]() discord -# Levelbot (NEW Level-System) +# NPLAY-Bot This bot was created specifically for the Discord [server](https://discord.gg/qcpeZQhJf5) of the german YouTuber and Twitch Streamer [nordrheintvplay](https://www.youtube.com/user/nordrheintvplay). The core feature of this bot is a leveling system, similar to Mee6, but with a high level of customization. Besides 3 currencies that can be collected, there are 13 items that all bring different functions and advantages. In addition, there are various temporary events, daily rewards and many other useful features. ## Test Server The bot is in constant development. Join the test [server](https://discord.gg/JYWezvQ) to receive regular updates, make suggestions and test preview versions. This is also the place to get support if you want to host the bot by yourself. - -## Installation - -Due to the high level of customization, I do not provide a public instance that anyone can invite. However, you can still host your own version of the bot. Therefore, you should have a basic understanding of Maven, MySQL, Discord bots in general and intermediate knowledge of Java and Spring Boot. - -### 0. Prerequisites - -Make sure to have the following things up and running: - -- Java 11 -- MySQL Server -- Maven -- git - -### 1. Cloning the repo - -``` -git clone https://github.com/Kaktushose/Levelbot.git -``` - -### 2. Configuration - -Go to the resources folder and add the following `application.properties` file: - -``` -spring.jpa.hibernate.ddl-auto=update -spring.datasource.url=jdbcurl -spring.datasource.username=username -spring.datasource.password=password -``` - -Make sure to set `ddl-auto` to `none` once you are in production environment. - -There are also some hardcoded values (e.g. channel ids) inside the codebase, make sure to change them. - -Go to [this](https://github.com/Kaktushose/Levelbot/blob/master/src/main/java/de/kaktushose/levelbot/bot/Levelbot.java#L91) line and provide your own bot token. - -### 3. Setting up the database - -As soon as you start the bot for the first time, Spring Boot will create all the database tables automatically. Afterwards you can start to fill in values, the column names should be self-explanatory. Feel free to hit me up, if you need help with this step. _Kaktushose#4036 is my discord tag_. - -### 4. Building the jar - -Once you are done with all configuration steps, you can build the jar and run it: - -``` -mvn clean package -``` - -``` -java -jar Levelbot.jar -``` - -## Contributing - -If you believe that something is missing, and you want to add it yourself, feel free to open a pull request. I recommend opening an issue first to prevent misunderstandings or waste of time because I'm already making your feature. Please try to keep your code quality at least as good as mine and stick to the design concepts of this project. - -## Used Technologies - -- [Java 11](https://openjdk.java.net/projects/jdk/11/) -- [Maven](https://maven.apache.org/) - Project Management Software -- [JDA](https://github.com/DV8FromTheWorld/JDA) - Discord API Wrapper -- [jda-commands](https://github.com/Kaktushose/jda-commands) - Command Framework -- [MariaDB](https://mariadb.com/) - Database -- [Spring Boot Data JPA](https://spring.io/projects/spring-data-jpa) - Database Access diff --git a/commandEmbeds.json b/commandEmbeds.json deleted file mode 100644 index 3460c37..0000000 --- a/commandEmbeds.json +++ /dev/null @@ -1,522 +0,0 @@ -{ - "pingEmbed": { - "title": ":satellite_orbital: Ping zur Discord-API", - "color": "#666666", - "fields": [ - { - "name": "Gateway", - "value": "{gatewayPing}" - }, - { - "name": "Rest", - "value": "{restPing}" - } - ] - }, - "currencyChange": { - "title": "Erfolg!", - "description": "Die {currency} von {user} wurden um {value} {operation}.", - "color": "#86c240" - }, - "currencySet": { - "title": "Erfolg!", - "description": "Die {currency} von {user} wurden auf {value} gesetzt.", - "color": "#86c240" - }, - "memberNotFound": { - "title": "Fehler!", - "description": "Der angegebene Nutzer ist nicht in der Datenbank vorhanden.\n\nBitte kontaktiere <@393843637437464588> .", - "color": "#aa0c14" - }, - "confirmAction": { - "title": "Achtung!", - "description": "Bist du sicher, dass {action}", - "color": "#f4cd53" - }, - "memberBlacklistAdd": { - "title": "Erfolg!", - "description": "{user} wurde zur Blacklist hinzugefügt und kann nicht mehr mit dem Bot interagieren.", - "color": "#86c240" - }, - "memberBlacklistShow": { - "title": "Folgende Nutzer stehen auf der Blacklist:", - "description": "{blacklist}", - "color": "#86c240" - }, - "memberBlacklistRemove": { - "title": "Erfolg!", - "description": "{user} wurde von der Blacklist entfernt und kann wieder mit dem Bot interagieren.\nHinweis: Sein Berechtigungslevel wurde auf `1` zurückgesetzt!", - "color": "#86c240" - }, - "memberBlacklistInvalidTarget": { - "title": "Unzureichende Berechtigungen!", - "description": "Dein Berechtigungslevel reicht nicht aus, um {user} zur Blacklist hinzuzufügen.", - "color": "#aa0c14" - }, - "permissionSet": { - "title": "Erfolg!", - "description": "Das Berechtigungslevel von {user} wurde auf {value} gesetzt.", - "color": "#86c240" - }, - "permissionSetInvalidTarget": { - "title": "Unzureichende Berechtigungen!", - "description": "Dein Berechtigungslevel reicht nicht aus, um das Berechtigungslevel von {user} zu bearbeiten.", - "color": "#aa0c14" - }, - "invalidValue": { - "title": "Fehler!", - "description": "Es sind nur Werte von {min} bis einschließlich {max} erlaubt.", - "color": "#aa0c14" - }, - "bulkDeleteSuccess": { - "title": "Erfolg!", - "description": "{amount} Nachrichten wurden gelöscht.", - "color": "#86c240" - }, - "removeItem": { - "title": "Item entfernen", - "description": "{user} besitzt folgende Items:", - "color": "#f4cd53", - "footer": { - "text": "Reagiere mit dem entsprechenden Emote, um ein Item zu entfernen" - } - }, - "removeItemSuccess": { - "title": "Erfolg!", - "description": "{user} wurde das Item entfernt.", - "color": "#86c240" - }, - "evalCommand": { - "title": "Eval Result", - "description": "```{result}```", - "color": "{color}" - }, - "botInfo": { - "title": "Informationen zum Bot", - "color": "#86c240", - "fields": [ - { - "name": "Prefix", - "value": "{prefix}" - }, - { - "name": "Version", - "value": "{version}" - }, - { - "name": "Lizenz", - "value": "[GNU General Public License 3](https://www.gnu.org/licenses/gpl-3.0)" - }, - { - "name": "Website", - "value": "[GitHub](https://github.com/Kaktushose/Levelbot)" - }, - { - "name": "Credits", - "value": "Konzept und Idee: <@307973135746072578> \nProgrammierung: <@393843637437464588> \nLogo und Testing: <@487320784759685130>" - } - ] - }, - "missingCurrency": { - "title": "Fehler!", - "description": "Du hast nicht genügend {currency}.", - "color": "#aa0c14" - }, - "diamondChangeSuccess": { - "title": "Erfolg!", - "description": "Du hast {diamonds} Diamanten gegen {coins} Münzen getauscht.", - "color": "#86c240" - }, - "switchDailySuccess": { - "title": "Erfolg!", - "description": "Die tägliche Kontoinformation wurde {action}.", - "color": "#86c240" - }, - "switchDailyError": { - "title": "Fehler!", - "description": "Der Bot kann dir keine Direktnachrichten senden!\nBitte überprüfe deine Einstellungen und versuche es dann erneut!", - "color": "#aa0c14" - }, - "rankInfo": { - "title": ":information_source: Kontoinformation für", - "description": "{user}", - "color": "{color}", - "thumbnail": { - "url": "{avatarUrl}" - }, - "fields": [ - { - "name": "Stufe", - "value": ":level_slider: {currentRank}" - }, - { - "name": "Nächste Stufe", - "value": ":dart: {nextRank}" - }, - { - "name": "", - "value": "__Währungen__:" - }, - { - "name": "XP:", - "value": ":star2: {xp}", - "inline": true - }, - { - "name": "Münzen:", - "value": ":moneybag: {coins}", - "inline": true - }, - { - "name": "Diamanten:", - "value": ":gem: {diamonds}", - "inline": true - }, - { - "name": "", - "value": "__Statistiken__:" - }, - { - "name": "Ressourcen-Zuwachs:", - "value": ":star2: {xpGain} | :moneybag: {coinsGain} | :gem: {diamondsGain}" - }, - { - "name": "Gezählte Nachrichten:", - "value": ":chart_with_upwards_trend: {messageCount}" - }, - { - "name": "", - "value": "__Items__:" - } - ], - "footer": { - "text": "Hole dir Items direkt im Kanal #levelsystem" - } - }, - "eventRankInfo": { - "title": ":information_source: Kontoinformation für", - "description": "{user}", - "color": "{color}", - "thumbnail": { - "url": "{avatarUrl}" - }, - "fields": [ - { - "name": "Stufe", - "value": ":level_slider: {currentRank}" - }, - { - "name": "Nächste Stufe", - "value": ":dart: {nextRank}" - }, - { - "name": "", - "value": "__Währungen__:" - }, - { - "name": "XP:", - "value": ":star2: {xp}", - "inline": true - }, - { - "name": "Münzen:", - "value": ":moneybag: {coins}", - "inline": true - }, - { - "name": "Diamanten:", - "value": ":gem: {diamonds}", - "inline": true - }, - { - "name": "", - "value": "__Statistiken__:" - }, - { - "name": "Ressourcen-Zuwachs:", - "value": ":star2: {xpGain} | :moneybag: {coinsGain} | :gem: {diamondsGain}" - }, - { - "name": "Gezählte Nachrichten:", - "value": ":chart_with_upwards_trend: {messageCount}" - }, - { - "name": "", - "value": "__{eventName}__:" - }, - { - "name": "{currencyName}:", - "value": "{currencyEmote} {currencyPoints}" - }, - { - "name": "Belohnungen:", - "value": "{eventRewards}" - }, - { - "name": "", - "value": "__Items__:" - } - ], - "footer": { - "text": "Hole dir Items direkt im Kanal #levelsystem" - } - }, - "leaderboard": { - "title": "Rangliste {guild}: {currency}", - "color": "#86c240" - }, - "shopOverview": { - "title": ":shopping_cart: Shop", - "description": "Item hinzufügen für {user}", - "color": "#86c240", - "fields": [ - { - "name": ":star: PREMIUM", - "value": "Erhalte mit dieser besonderen Rolle satte 12 exklusive Vorteile auf unserem Server!" - }, - { - "name": ":musical_note: Rythm DJ Perk", - "value": "Mit der DJ Rolle für den \"Rythm\" bist Du der Star!" - }, - { - "name": ":man_tone3: Nickname Perk", - "value": "Mit dem Nickname Perk erhältst du das Recht, Deinen Nicknamen auf dem Server selbstständig jederzeit zu ändern!" - }, - { - "name": ":moneybag: Münzenbooster", - "value": "Mit dem Münzenbooster erhältst Du +2 Münzen je gezählter Nachricht zusätzlich - optimal für viele Münzen!" - }, - { - "name": ":star2: XP-Booster", - "value": "Mit dem XP-Booster erhältst Du +2 XP je gezählter Nachricht zusätzlich - steige so leichter in neue Stufen auf!" - } - ], - "footer": { - "text": "Um ein Item zu kaufen, reagiere mit dem jeweiligen Emote!" - } - }, - "specificShop": { - "title": ":shopping_cart: Shop", - "description": "Für dieses Item stehen folgende Variationen zur Verfügung:", - "color": "#86c240", - "footer": { - "text": "Um ein Item zu kaufen, reagiere mit dem jeweiligen Emote!" - } - }, - "shopSuccess": { - "title": ":shopping_bags: Erfolgreicher Kauf!", - "description": "Du besitzt jetzt das Item \"{item}\"", - "color": "#86c240", - "fields": [ - { - "name": "Dauer:", - "value": ":timer_clock: {days} Tage" - } - ] - }, - "addItemSuccess": { - "title": "Erfolg!", - "description": "{user} besitzt jetzt das Item \"{item}\"", - "color": "#86c240", - "fields": [ - { - "name": "Dauer:", - "value": ":timer_clock: {days} Tage" - } - ] - }, - "shopConfirm": { - "title": "Bestätige Deinen Kauf! :gift:", - "description": "Bist du sicher, dass Du das Item \"{item}\" für {price} Münzen :moneybag: erwerben möchtest? Klicke auf den :white_check_mark:, um den Kauf zu bestätigen.", - "color": "#f4cd53" - }, - "shopError": { - "title": "Fehler!", - "description": "{message}", - "color": "#aa0c14" - }, - "levelUp": { - "title": ":arrow_up: Stufenaufstieg!", - "description": "{user}", - "color": "{color}", - "fields": [ - { - "name": "Neue Stufe:", - "value": ":level_slider: {currentRank}" - }, - { - "name": "Einmalige Belohnung:", - "value": "{reward}" - }, - { - "name": "Nächste Stufe:", - "value": ":dart: {nextRank} (ab {xp} XP)" - } - ] - }, - "itemExpired": { - "title": "Item abgelaufen!", - "description": "Dein {item} ist abgelaufen!", - "color": "#f4cd53", - "fields": [ - { - "name": "Gekauft am:", - "value": "{date}" - } - ] - }, - "botStop": { - "title": "Bot wurde heruntergefahren!", - "description": ":wrench: Wir sind in Kürze wieder zurück!", - "color": "#aa0c14" - }, - "botStart": { - "title": "Bot wurde gestartet!", - "description": ":gear: Version: {version}", - "color": "#86c240" - }, - "itemNotFound": { - "title": "Fehler!", - "description": "Es wurde kein Item mit der ID {id} gefunden!", - "color": "#aa0c14" - }, - "itemPriceChanged": { - "title": "Erfolg!", - "description": "Der Preis wurde erfolgreich geändert!", - "color": "#86c240" - }, - "nitroBoostStart": { - "title": "Vielen Dank für Deine Unterstützung {user}!", - "description": "Danke, dass Du den nordrheintvplay Discord mit Deinem Nitro-Boost unterstützt.\nAls kleine Aufmerksamkeit hast Du einige coole einmalige Belohnung erhalten:\n> **EINMALIG:**\n\n> <:icon_confirmation2:693837343580356618> 250 XP :star2: geschenkt\n> <:icon_confirmation2:693837343580356618> 250 Münzen :moneybag: geschenkt\n> <:icon_confirmation2:693837343580356618> 25 Diamanten :gem: geschenkt\n> + PREMIUM unlimited, gilt dauerhaft bis zur Beendigung des Boosts!\n\nAußerdem schenken wir Dir alle 30 Tage zusätzlich:\n\n**FORTLAUFEND:**\n> <:icon_confirmation2:693837343580356618> 50 XP :star2: jeden Monat zusätzlich!\n> <:icon_confirmation2:693837343580356618> 50 Münzen :moneybag:\n> <:icon_confirmation2:693837343580356618> 5 Diamanten :gem:\n> <:icon_confirmation2:693837343580356618> Nitro Booster Rolle :icon_boost: als cooles Abzeichen!\n\nWir wünschen Dir weiterhin viel Spaß auf dem Server.", - "color": "#f47fff" - }, - "nitroBoostResume": { - "title": "Vielen Dank für Deine Unterstützung {user}!", - "description": "Danke, dass Du den nordrheintvplay Discord erneut mit Deinem Nitro-Boost unterstützt. \nAls kleine Aufmerksamkeit hast Du PREMIUM unlimited :star: erhalten. Die Rolle gilt dauerhaft bis zur Beendigung des Boosts!\n\nAußerdem schenken wir Dir alle 30 Tage zusätzlich:\n> <:icon_confirmation2:693837343580356618> 50 XP :star2: jeden Monat zusätzlich!\n> <:icon_confirmation2:693837343580356618> 50 Münzen :moneybag:\n> <:icon_confirmation2:693837343580356618> 5 Diamanten :gem:\n> <:icon_confirmation2:693837343580356618> Nitro Booster Rolle :icon_boost: als cooles Abzeichen!\n\nWir wünschen Dir weiterhin viel Spaß auf dem Server.", - "color": "#f47fff" - }, - "nitroBoostStop": { - "title": "Information zu Deinem Server-Boost bei nordrheintvplay", - "description": "Lieber {user},\nschade, dass du unseren Server nicht mehr mit einem Server-Boost unterstützt.\nWir möchten dich darüber informieren, dass mit der Beendigung des Boosts auch die dazugehörigen fortlaufenden Belohnungen entfallen. \nWir bedanken uns für Deine Treue und wünschen Dir weiterhin viel Spaß auf dem Server.\nDein Serverteam\n", - "color": "#f4cd53" - }, - "shopInitSuccess": { - "title": "Erfolg!", - "description": "Der Reaction Shop wurde erfolgreich eingerichtet!", - "color": "#86c240" - }, - "statistics": { - "thumbnail": { - "url": "https://cdn.discordapp.com/attachments/850466263216947252/970283398063079464/nplay_logo_standard.jpg" - }, - "title": "Aktuelle Statistiken", - "description": "__**NPLAY Discord:**__\n\n**Mitglieder auf dem Server:**\n{totalMemberCount}\n\n**Mitglieder online:**\n{onlineMemberCount}\n\n**Mitglieder in Spielen:**\n:tractor: LS 22: {lsMemberCount}\n:truck: ETS 2: {etsMemberCount}\n:fire: Notruf 112: {notrufMemberCount}\n:bus: OMSI 2: {omsiMemberCount}\n:mountain_cableway: Winter Resort Simulator: {wrsMemberCount}\n:gun: GTA V: {gtaMemberCount}\n\n**Nitro-Booster:**\nDerzeit insgesamt {boosterMemberCount} <@&596780997727617045>, die uns mit {boosterCount} Boosts unterstützen.\n\n{boosterMemberList}\n\n**PREMIUM:**\nDerzeit insgesamt {premiumMemberCount} <@&386302591883018242>- Mitglieder.\n\n{premiumMemberList}\n\n__**NPLAY auf Youtube:**__\n\n**Abonnenten auf YouTube:** \n{ytFollowerCount}\n\n**Videos auf YouTube:**\n{ytVideoCount}\n\n**Views auf YouTube:**\n{ytViewCount}", - "color": "#aeff00" - }, - "dailyReward": { - "title": "Herzlichen Glückwunsch, {user}!", - "description": "Du hast Deine tägliche Belohnung in Höhe von {reward} erfolgreich abgeholt.\nSchau gerne morgen wieder vorbei.", - "color": "#86c240" - }, - "balanceEventList": { - "title": "Folgende Balance Events sind verfügbar", - "description": "0: XP-Booster x2\n1: Münzen-Rush x2\n2: Diamanten-Regen x2\n3: Cooldown -67%", - "color": "#86c240" - }, - "unknownEventId": { - "title": "Fehler!", - "description": "Unbekannte Event ID. Siehe `event list` für Details!", - "color": "#aa0c14" - }, - "balanceEventStart": { - "title": "Erfolg!", - "description": "Das Event \"{name}\" wurde erfolgreich aktiviert.", - "color": "#86c240" - }, - "balanceEventStop": { - "title": "Erfolg!", - "description": "Das Event \"{name}\" wurde erfolgreich deaktiviert.", - "color": "#86c240" - }, - "contestEventStart": { - "title": "Erfolg!", - "description": "Das Bilder Contest Event wurde erfolgreich aktiviert.", - "color": "#86c240" - }, - "contestEventStop": { - "title": "Erfolg!", - "description": "Das Bilder Contest Event wurde erfolgreich deaktiviert.", - "color": "#86c240" - }, - "collectEventStart": { - "title": "Erfolg!", - "description": "Das Sammel Event \"{name}\" wurde erfolgreich aktiviert.", - "color": "#86c240" - }, - "collectEventStop": { - "title": "Erfolg!", - "description": "Das Sammel Event wurde erfolgreich deaktiviert.", - "color": "#86c240" - }, - "collectEventList": { - "title": "Folgende Collect Events sind verfügbar", - "description": "{list}", - "color": "#86c240" - }, - "noActiveCollectEvent": { - "title": "Fehler!", - "description": "Es ist kein Sammel Event aktiviert.", - "color": "#aa0c14" - }, - - "collectEventRoleReward": { - "title": "Frohe Weihnachtszeit :santa: , {user}!", - "description": "Du hast Dein **erstes Rentier** :deer: auf dem Server gefunden!\nAls Belohnung verleihen wir Dir unsere exklusive **Eventrolle XMAS 2022** :red_circle:, mit der Du über die Weihnachtage ganz oben auf dem Server gelistet bist! Genieße die freie Zeit und finde insgesamt **12 Rentiere** :deer: für eine weitere Belohnung.\n\n:point_right: Nimm an unserem Screenshot-Contest teil! Alle Infos in <#1053712477650686063>.\n\n:point_right: Und vom 24.12 - 26.12 ist unser **CHRISTMAS-BOOSTER** :christmas_tree: aktiv, der alle bisherigen Booster kombiniert.\n\n:point_right: In diesem Zeitraum kannst Du dir außerdem mit dem Befehl !geschenk im Kanal bot-befehle ein **besonderes Geschenk** :gift: abholen.", - "color": "#EC5C0F" - }, - "collectEventItemReward": { - "title": "Ein Geschenk für Dich :man_surfing:, {user}!", - "description": "Du hast nun großartige **30 Wassermelonen** :watermelon: geerntet.\nWie versprochen darfst Du jetzt **15 Tage** :clock:️ **GRATIS PREMIUM** :star: auf dem Server genießen!\nWir wünschen Dir weiterhin viel Spaß mit der NPLAY-Sommerzeit :man_surfing:.\n\nÜbrigens: Wenn du insgesamt **30 Melonen** :watermelon: gesammelt hast, darfst Du dich über **15 Tage** :clock:️ **GRATIS PREMIUM** :star: auf dem Server freuen!\nUnd während der gesamten NPLAY-Sommerzeit ist unser **XXL-SOMMERBOOSTER** :timer::star2: mit **2-fachen XP** und **2-fachten Münzen** aktiviert.", - "color": "#EC5C0F" - }, - "collectEventXpReward": { - "title": "Frohe Weihnachtszeit :santa: , {user}!", - "description": "Du hast nun satte **12 Rentiere** :deer: auf dem Server gefunden!\nAls Dankeschön haben wir Dir zusätzlich **50 XP** :star2: auf Deinem Konto gutgeschrieben. Und die Suche geht weiter, denn ab insgesamt **24 Rentieren** :deer: erhältst Du eine weitere Belohnung!\n\n:point_right: Nimm an unserem Screenshot-Contest teil! Alle Infos in <#1053712477650686063>.\n\n:point_right: Und vom 24.12 - 26.12 ist unser **CHRISTMAS-BOOSTER** :christmas_tree: aktiv, der alle bisherigen Booster kombiniert.\n\n:point_right: In diesem Zeitraum kannst Du dir außerdem mit dem Befehl !geschenk im Kanal bot-befehle ein **besonderes Geschenk** :gift: abholen.", - "color": "#EC5C0F" - }, - "collectEventCoinsReward": { - "title": "Frohe Weihnachtszeit :santa: , {user}!", - "description": "Du hast nun fabelhafte **24 Rentiere** :deer: auf dem Server gefunden!\nDafür schenken wir Dir weitere **100 Münzen** :moneybag:. Viel Spaß damit und weiterhin eine gute Zeit!\n\n:point_right: Nimm an unserem Screenshot-Contest teil! Alle Infos in <#1053712477650686063>.\n\n:point_right: Und vom 24.12 - 26.12 ist unser **CHRISTMAS-BOOSTER** :christmas_tree: aktiv, der alle bisherigen Booster kombiniert.\n\n:point_right: In diesem Zeitraum kannst Du dir außerdem mit dem Befehl !geschenk im Kanal bot-befehle ein **besonderes Geschenk** :gift: abholen.", - "color": "#EC5C0F" - }, - "rewardAlreadyClaimed": { - "title": "Fehler!", - "description": "Du hast deine tägliche Belohnung bereits abgeholt.\nSchau gerne in {hours} Stunden wieder vorbei!", - "color": "#f4cd53" - }, - "shopCommandDeprecated": { - "title": ":information_source: Hinweis", - "description": "Der Shop-Command wurde entfernt.\nItems kannst du ab sofort direkt in <#851388807239827466> kaufen.", - "color": "#f4cd53" - }, - "mutedChannelsAdd": { - "title": "Erfolg!", - "description": "{channel} wird ab sofort vom Levelsystem ignoriert.", - "color": "#86c240" - }, - "mutedChannelsRemove": { - "title": "Erfolg!", - "description": "{channel} wird nicht mehr vom Levelsystem ignoriert.", - "color": "#86c240" - }, - "mutedChannelsList": { - "title": "Folgende Kanäle werden vom Levelsystem ignoriert:", - "description": "{list}", - "color": "#86c240" - }, - "buyLog": { - "title": "Transaktion", - "description": "{user} hat das Item \"{item}\" gekauft.", - "color": "#86c240" - } -} diff --git a/docs.md b/docs.md deleted file mode 100644 index 35bb178..0000000 --- a/docs.md +++ /dev/null @@ -1,549 +0,0 @@ -> Auto generated command manual | 2021/06/08 11:59:24 - -Owner -===== -### Bot herunterfahren - -**Description:** - -Fährt den Bot herunter. - -**Usage:** - -`!stop` - -**Aliases:** - -- shutdown - -**Permissions:** - -- owner - -### Code ausführen - -**Description:** - -Führt Code in der aktuellen Runtime des Bots aus - -**Usage:** - -`!eval ` - -**Permissions:** - -- owner - -Moderation -========== -### Item entfernen - -**Description:** - -Entfernt ein Item aus dem Besitz eines Benutzers - -**Usage:** - -`!remove ` - -**Aliases:** - -- rm - -**Permissions:** - -- moderator - -### Preis ändern - -**Description:** - -Setzt den Preis eines Items auf den angegebenen Wert - -**Usage:** - -`!setprice ` - -**Permissions:** - -- moderator - -### Willkommen Embeds senden - -**Description:** - -Sendet die Embeds in <#551483788337872927> - -**Usage:** - -`!embeds resend ` - -**Permissions:** - -- moderator - -### Willkommen Embeds senden - -**Description:** - -Sendet die Embeds in <#551483788337872927> - -**Usage:** - -`!embeds send` - -**Permissions:** - -- moderator - -### Contest Event deaktivieren - -**Description:** - -Stoppt ein Bilder Contest Event - -**Usage:** - -`!event contest stop` - -**Permissions:** - -- moderator - -### Collect Event Arten - -**Description:** - -Zeigt eine Liste aller verfügbaren Collect Events an - -**Usage:** - -`!event collect list` - -**Permissions:** - -- moderator - -### XP setzen - -**Description:** - -Setzt die Anzahl der XP eines Benutzers auf den angegbenen Wert. - -**Usage:** - -`!set xp ` - -**Permissions:** - -- moderator - -### Balance Event Arten - -**Description:** - -Zeigt eine Liste aller verfügbaren Balance Events an - -**Usage:** - -`!event balance list` - -**Permissions:** - -- moderator - -### Balance Event deaktivieren - -**Description:** - -Stoppt das Balance Event mit der angegeben ID - -**Usage:** - -`!event balance stop ` - -**Permissions:** - -- moderator - -### Münzen ändern - -**Description:** - -Ändert die Anzahl der Münzen eines Benutzers um den angegbenen Wert. - -**Usage:** - -`!add coins ` - -**Permissions:** - -- moderator - -### Balance Event aktivieren - -**Description:** - -Startet das Balance Event mit der angegeben ID - -**Usage:** - -`!event balance start ` - -**Permissions:** - -- moderator - -### Benutzer sperren - -**Description:** - -Fügt einen Benutzer zur Blacklist hinzu - -**Usage:** - -`!blacklist add ` - -**Aliases:** - -- banlist add -- bl add - -**Permissions:** - -- moderator - -### Sammel Event aktivieren - -**Description:** - -Startet das Sammel Event mit der angegeben ID - -**Usage:** - -`!event collect start ` - -**Permissions:** - -- moderator - -### Berechtigung ändern - -**Description:** - -Setzt das Berechtigungslevel eines Benutzers auf den angegebenen Wert - -**Usage:** - -`!setperms ` - -**Permissions:** - -- moderator - -### Diamanten ändern - -**Description:** - -Ändert die Anzahl der Diamanten eines Benutzers um den angegbenen Wert. - -**Usage:** - -`!add diamonds ` - -**Permissions:** - -- moderator - -### Reaction Shop einrichten - -**Description:** - -Fügt die benötigten Reactions für den Shop hinzu - -**Usage:** - -`!initshop ` - -**Permissions:** - -- moderator - -### XP ändern - -**Description:** - -Ändert die Anzahl der XP eines Benutzers um den angegbenen Wert. - -**Usage:** - -`!add xp ` - -**Permissions:** - -- moderator - -### Münzen setzen - -**Description:** - -Setzt die Anzahl der Münzen eines Benutzers auf den angegbenen Wert. - -**Usage:** - -`!set coins ` - -**Permissions:** - -- moderator - -### Benutzer entsperren - -**Description:** - -Entfernt einen Benutzer von der Blacklist - -**Usage:** - -`!blacklist remove ` - -**Aliases:** - -- blacklist rm -- banlist remove -- banlist rm -- bl remove -- bl rm - -**Permissions:** - -- moderator - -### Nachrichten löschen - -**Description:** - -Löscht die angegebene Zahl von Nachrichten aus einem Channel - -**Usage:** - -`!delete ` - -**Aliases:** - -- purge -- clear - -**Permissions:** - -- moderator - -### Gesperrte Benutzer - -**Description:** - -Zeigt alle Nutzer, die auf der Blacklist stehen - -**Usage:** - -`!blacklist show` - -**Aliases:** - -- blacklist list -- blacklist view -- banlist show -- banlist list -- banlist view -- bl show -- bl list -- bl view - -**Permissions:** - -- moderator - -### Contest Event aktivieren - -**Description:** - -Startet ein Bilder Contest Event - -**Usage:** - -`!event contest start ` - -**Permissions:** - -- moderator - -### Diamanten setzen - -**Description:** - -Setzt die Anzahl der Diamanten eines Benutzers auf den angegbenen Wert. - -**Usage:** - -`!set diamonds ` - -**Permissions:** - -- moderator - -### Collect Event deaktivieren - -**Description:** - -Stoppt das aktuelle Collect Event - -**Usage:** - -`!event collect stop` - -**Permissions:** - -- moderator - -Levelsystem -=========== -### Täglich Command - -**Description:** - -Aktiviert bzw. deaktiviert die täglichen Kontoinformationen - -**Usage:** - -`!täglich` - -**Permissions:** - - - -### Diamanten tauschen - -**Description:** - -Tauscht Diamanten gegen Münzen ein. Ein Diamant ist 20 Münzen wert - -**Usage:** - -`!tauschen ` - -**Aliases:** - -- wechseln - -**Permissions:** - - - -### Level-Shop - -**Description:** - -Mit diesem Command kannst du Items aus dem Levelshop kaufen - -**Usage:** - -`!kaufen` - -**Aliases:** - -- shop - -**Permissions:** - - - -### Level-Shop - -**Description:** - -Fügt ein Item einem anderen Nutzer hinzu - -**Usage:** - -`!kaufen ` - -**Aliases:** - -- shop for - -**Permissions:** - -- moderator - -### Kontoinformation abrufen - -**Description:** - -Zeigt die Kontoinformationen zu einem User an - -**Usage:** - -`!info ` - -**Aliases:** - -- rank -- konto - -**Permissions:** - - - -### Rangliste - -**Description:** - -Zeigt eine Rangliste der Benutzer mit den meisten XP, Münzen oder Diamanten - -**Usage:** - -`!rangliste` - -**Aliases:** - -- leaderboard -- lb - -**Permissions:** - - - -Sonstiges -========= -### Ping Command - -**Description:** - -Zeigt den Ping zur Discord-API an - -**Usage:** - -`!ping` - -**Permissions:** - - - -### Bot Information - -**Description:** - -Zeigt allgemeine Inforamtionen über den Bot an - -**Usage:** - -`!botinfo` - -**Aliases:** - -- credits - -**Permissions:** - - - diff --git a/jdacEmbeds.json b/jdacEmbeds.json deleted file mode 100644 index dce38ec..0000000 --- a/jdacEmbeds.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "defaultHelp": { - "title": "Generelle Hilfe", - "description": "Die folgenden Commands sind verfügbar. Siehe `{prefix}{helpLabel} ` um spezifische Hilfe zu erhalten.", - "color": "#86c240" - }, - "specificHelp": { - "title": "Ausführliche Hilfe", - "description": "Command Details für `{prefix}{label}`", - "color": "#86c240", - "fields": [ - { - "name": "Name", - "value": "`{name}`" - }, - { - "name": "Benutzung", - "value": "`{usage}`" - }, - { - "name": "Aliase", - "value": "`{aliases}`" - }, - { - "name": "Beschreibung", - "value": "`{description}`" - }, - { - "name": "Berechtigungen", - "value": "`{permissions}`" - }, - { - "name": "Kategorie", - "value": "`{category}`" - } - ] - }, - "genericHelp": { - "title": "Generelle Hilfe", - "description": "Die folgenden Commands sind verfügbar. Siehe `{prefix}{helpLabel} ` für eine ausführliche Hilfe", - "color": "#86c240" - }, - "commandNotFound": { - "title": "Command nicht gefunden!", - "description": "```Siehe {prefix}{helpLabel} um eine Liste aller verfügbaren Commands zu erhalten```", - "fields": [ - { - "name": "Ähnliche Commands", - "value": "{commands}" - } - ], - "color": "#f4cd53" - }, - "insufficientPermissions": { - "title": "Unzureichende Berechtigungen", - "description": "`{prefix}{label}` benötigt bestimmte Berechtigungen um ausgeführt zu werden", - "color": "#ff0000", - "fields": [ - { - "name": "Berechtigungen:", - "value": "`{permissions}`" - } - ] - }, - "guildMuted": { - "title": "Unzureichende Berechtigungen", - "description": "Dieser Server wurde gemuted!", - "color": "#ff0000" - }, - "channelMuted": { - "title": "Unzureichende Berechtigungen", - "description": "Dieser Kanal wurde gemuted!", - "color": "#ff0000" - }, - "userMuted": { - "title": "Unzureichende Berechtigungen", - "description": "Du wurdest gemuted!", - "color": "#ff0000" - }, - "syntaxError": { - "title": "Syntax Fehler", - "description": "`{usage}`", - "color": "#ffc800", - "fields": [ - { - "name": "Erwartete Argumente", - "value": "`{expected}`" - }, - { - "name": "Übergebene Parameter", - "value": "`{actual}`" - } - ] - }, - "cooldown": { - "title": "Cooldown", - "description": "Du kannst diesen Command wieder nutzen in: {cooldown}", - "color": "#ff0000" - }, - "wrongChannel": { - "title": "Falscher Kanal", - "description": "Dieser Command kann in Direktnachrichten nicht ausgeführt werden!", - "color": "#ff0000" - }, - "constraintFailed": { - "title": "Parameter Fehler", - "description": "```{message}```", - "color": "#ffc800" - }, - "executionFailed": { - "title": "Command Ausführung fehlgeschlagen", - "description": "```exception```", - "color": "#ff0000", - "footer": { - "text": "Bitte kontaktiere Kaktushose" - } - } -} diff --git a/pom.xml b/pom.xml index 892fd7e..5912a45 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - de.kaktushose.levelbot - Levelbot - 2.3.0 + com.github.kaktushose + NPLAY-Bot + 3.0.0 UTF-8 - 11 - 11 + 21 + 21 @@ -40,28 +40,11 @@ - de.kaktushose.levelbot.Bootstrapper + com.github.kaktushose.nplaybot.Bootstrapper - - org.springframework.boot - spring-boot-maven-plugin - 2.6.4 - - - - repackage - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M5 - @@ -81,76 +64,27 @@ net.dv8tion JDA - 4.4.0_352 + 5.0.0-beta.18 com.github.Kaktushose jda-commands - v.2.2.0 - - - com.github.Kaktushose - reactionwaiter - 71c2dd9ba2 - - - com.google.apis - google-api-services-youtube - v3-rev125-1.19.1 - - - org.springframework.boot - spring-boot-starter-data-jpa - 2.6.4 - - - mysql - mysql-connector-java - 8.0.28 - - - javax.xml.bind - jaxb-api - 2.3.1 + 4.0.0-beta.1 org.slf4j slf4j-api - 1.7.36 + 2.0.9 ch.qos.logback logback-classic - 1.2.11 + 1.4.11 ch.qos.logback logback-core - 1.2.11 - - - org.junit.jupiter - junit-jupiter-api - 5.8.2 - test - - - org.junit.jupiter - junit-jupiter-engine - 5.8.2 - test - - - org.springframework.boot - spring-boot-starter-test - 2.6.4 - test - - - com.h2database - h2 - 2.1.210 - test + 1.4.11 diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java new file mode 100644 index 0000000..7879bd1 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java @@ -0,0 +1,14 @@ +package com.github.kaktushose.nplaybot; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class Bootstrapper { + + private final static Logger log = LoggerFactory.getLogger(Bootstrapper.class); + + public static void main(String[] args) { + log.info("Hello World!"); + } +} diff --git a/src/main/java/de/kaktushose/levelbot/Bootstrapper.java b/src/main/java/de/kaktushose/levelbot/Bootstrapper.java deleted file mode 100644 index d3530c7..0000000 --- a/src/main/java/de/kaktushose/levelbot/Bootstrapper.java +++ /dev/null @@ -1,78 +0,0 @@ -package de.kaktushose.levelbot; - -import com.github.kaktushose.jda.commands.annotations.Produces; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.services.*; -import de.kaktushose.levelbot.shop.data.ShopService; -import net.dv8tion.jda.api.JDA; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import javax.security.auth.login.LoginException; -import java.util.concurrent.TimeUnit; - -@SpringBootApplication -public class Bootstrapper { - - private final static Logger log = LoggerFactory.getLogger(Bootstrapper.class); - private static Levelbot levelbot; - - public static void main(String[] args) throws LoginException, InterruptedException { - long startTime = System.currentTimeMillis(); - log.info("Starting bot..."); - Thread.setDefaultUncaughtExceptionHandler((t, e) -> log.error("An unexpected error has occurred!", e)); - SpringApplication.run(Bootstrapper.class, args); - levelbot = new Levelbot(Levelbot.GuildType.PRODUCTION); - levelbot.start().indexMembers(); - startTime = System.currentTimeMillis() - startTime; - log.info("Successfully started bot! Took {} seconds", TimeUnit.MILLISECONDS.toSeconds(startTime)); - } - - @Produces - public UserService getUserService() { - return levelbot.getUserService(); - } - - @Produces - public ShopService getShopService() { - return levelbot.getShopService(); - } - - @Produces - public LevelService getLevelService() { - return levelbot.getLevelService(); - } - - @Produces - public SettingsService getSettingsService() { - return levelbot.getSettingsService(); - } - - @Produces - public BoosterService getBoosterService() { - return levelbot.getBoosterService(); - } - - @Produces - public EventService getEventService() { - return levelbot.getEventService(); - } - - @Produces - public JDA getJda() { - return levelbot.getJda(); - } - - @Produces - public EmbedCache getEmbedCache() { - return levelbot.getEmbedCache(); - } - - @Produces - public Levelbot getLevelbot() { - return levelbot; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/bot/Levelbot.java b/src/main/java/de/kaktushose/levelbot/bot/Levelbot.java deleted file mode 100644 index a8fd57f..0000000 --- a/src/main/java/de/kaktushose/levelbot/bot/Levelbot.java +++ /dev/null @@ -1,432 +0,0 @@ -package de.kaktushose.levelbot.bot; - -import com.github.kaktushose.jda.commands.JDACommands; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import com.github.kaktushose.jda.commands.embeds.EmbedDTO; -import com.github.kaktushose.jda.commands.embeds.error.JsonErrorMessageFactory; -import com.github.kaktushose.jda.commands.embeds.help.JsonHelpMessageFactory; -import de.kaktushose.discord.reactionwaiter.ReactionListener; -import de.kaktushose.levelbot.Bootstrapper; -import de.kaktushose.levelbot.commands.moderation.WelcomeEmbedsCommand; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.model.CollectEvent; -import de.kaktushose.levelbot.database.model.Rank; -import de.kaktushose.levelbot.database.services.*; -import de.kaktushose.levelbot.listener.*; -import de.kaktushose.levelbot.shop.ShopListener; -import de.kaktushose.levelbot.shop.data.ShopService; -import de.kaktushose.levelbot.shop.data.items.Item; -import de.kaktushose.levelbot.util.Statistics; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.JDABuilder; -import net.dv8tion.jda.api.OnlineStatus; -import net.dv8tion.jda.api.entities.*; -import net.dv8tion.jda.api.exceptions.ErrorHandler; -import net.dv8tion.jda.api.requests.ErrorResponse; -import net.dv8tion.jda.api.requests.GatewayIntent; -import net.dv8tion.jda.api.utils.ChunkingFilter; -import net.dv8tion.jda.api.utils.MemberCachePolicy; -import net.dv8tion.jda.api.utils.cache.CacheFlag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.security.auth.login.LoginException; -import java.io.File; -import java.text.SimpleDateFormat; -import java.time.Instant; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -@SuppressWarnings("ConstantConditions") -public class Levelbot { - - private static final Logger log = LoggerFactory.getLogger(Levelbot.class); - private final UserService userService; - private final ShopService shopService; - private final SettingsService settingsService; - private final LevelService levelService; - private final EventService eventService; - private final BoosterService boosterService; - private final EmbedCache embedCache; - private final TaskScheduler taskScheduler; - private final Statistics statistics; - private final ShutdownHttpServer httpServer; - private final long guildId; - private JDACommands jdaCommands; - private JDA jda; - private Guild guild; - private TextChannel botChannel; - private TextChannel logChannel; - - public Levelbot() { - userService = null; - shopService = null; - settingsService = null; - levelService = null; - eventService = null; - boosterService = null; - embedCache = null; - taskScheduler = null; - statistics = null; - httpServer = null; - guildId = 0; - } - - public Levelbot(GuildType guildType) { - userService = new UserService(); - shopService = new ShopService(this); - settingsService = new SettingsService(); - eventService = new EventService(settingsService, userService); - boosterService = new BoosterService(this); - levelService = new LevelService(this); - guildId = guildType.id; - embedCache = new EmbedCache(new File("commandEmbeds.json")); - taskScheduler = new TaskScheduler(); - statistics = new Statistics(this, guildId); - httpServer = new ShutdownHttpServer(this, 8080); - } - - public Levelbot start() throws LoginException, InterruptedException { - log.info("Bot is running at version {}", settingsService.getVersion(guildId)); - - embedCache.loadEmbedsToCache(); - - log.debug("Starting jda..."); - String token = settingsService.getBotToken(guildId); - jda = JDABuilder.create( - token, - GatewayIntent.GUILD_MEMBERS, - GatewayIntent.GUILD_VOICE_STATES, - GatewayIntent.GUILD_MESSAGES, - GatewayIntent.GUILD_MESSAGE_REACTIONS, - GatewayIntent.GUILD_PRESENCES - ).disableCache( - CacheFlag.EMOTE, - CacheFlag.CLIENT_STATUS - ).enableCache( - CacheFlag.ACTIVITY - ).setChunkingFilter( - ChunkingFilter.ALL - ).setMemberCachePolicy( - MemberCachePolicy.NONE - ).setActivity( - Activity.playing("starting...") - ).setStatus( - OnlineStatus.DO_NOT_DISTURB - ).build().awaitReady(); - - log.debug("Registering event listeners..."); - jda.addEventListener( - new JoinLeaveListener(this), - new LevelListener(this), - new ShopListener(this), - new DailyRewardListener(this), - new ContestEventListener(settingsService, eventService) - ); - - log.debug("Starting ReactionListener..."); - ReactionListener.setAutoRemove(true); - ReactionListener.setAutoRemoveDelay(60, TimeUnit.SECONDS); - ReactionListener.startListening(jda); - - log.debug("Starting jda-commands..."); - jdaCommands = JDACommands.start(jda, Bootstrapper.class, "de.kaktushose.levelbot"); - EmbedCache embeds = new EmbedCache("jdacEmbeds.json"); - embeds.loadEmbedsToCache(); - jdaCommands.getImplementationRegistry().setHelpMessageFactory( - new JsonHelpMessageFactory(embeds) - ); - jdaCommands.getImplementationRegistry().setErrorMessageFactory( - new JsonErrorMessageFactory(embeds) - ); - guild = jda.getGuildById(guildId); - botChannel = guild.getTextChannelById(settingsService.getBotChannelId(guildId)); - logChannel = guild.getTextChannelById(settingsService.getLogChannelId(guildId)); - - // first start of bot, check for expired items immediately - shopService.checkForExpiredItems(); - - // get offset time until it's 0 am, also ensures that this task only runs once every 24 hours - long current = TimeUnit.HOURS.toMinutes(Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) + Calendar.getInstance().get(Calendar.MINUTE); - long delay = TimeUnit.HOURS.toMinutes(24) - current; - taskScheduler.addRepetitiveTask(() -> { - log.info("Starting daily tasks!"); - try { - log.info("Sending dm rank infos..."); - dmRankInfo(); - log.info("Checking for expired items..."); - shopService.checkForExpiredItems(); - log.info("Checking for new nitro boosters..."); - boosterService.updateBoosterStatus(guild, botChannel, embedCache); - log.info("Checking for booster rewards..."); - checkForNitroBoostersRewards(); - log.info("Update user statistics..."); - updateUserStatistics(); - log.info("Done!"); - } catch (Throwable t) { - log.error("An exception has occurred while executing daily tasks!", t); - } - }, delay, TimeUnit.HOURS.toMinutes(24), TimeUnit.MINUTES); - - taskScheduler.addRepetitiveTask(() -> { - try { - updateStatistics(); - } catch (Throwable t) { - log.error("An exception has occurred while updating statistics!", t); - } - }, 0, 4, TimeUnit.HOURS); - - String version = settingsService.getVersion(guildId); - jda.getPresence().setStatus(OnlineStatus.ONLINE); - jda.getPresence().setActivity(Activity.playing(version)); - - getBotChannel().sendMessageEmbeds(embedCache.getEmbed("botStart") - .injectValue("version", version) - .toMessageEmbed() - ).queue(); - - httpServer.start(); - - return this; - } - - public Levelbot stop() { - getBotChannel().sendMessageEmbeds(embedCache.getEmbed("botStop").toMessageEmbed()).complete(); - jdaCommands.shutdown(); - jda.shutdown(); - return this; - } - - public void terminate(int status) { - System.exit(status); - } - - public Levelbot indexMembers() { - log.info("Indexing members..."); - guild.loadMembers().onSuccess(guildMembers -> { - guildMembers.forEach(member -> userService.createUserIfAbsent(member.getIdLong())); - - List guildMemberIds = guildMembers.stream().map(ISnowflake::getIdLong).collect(Collectors.toList()); - userService.getAllUsers().forEach(botUser -> { - if (!guildMemberIds.contains(botUser.getUserId())) { - log.info("Removing " + botUser.getUserId()); - userService.deleteUser(botUser.getUserId()); - } - }); - }); - return this; - } - - public void addRankRole(long userId, int rankId) { - Rank rank = levelService.getRank(rankId); - guild.addRoleToMember(userId, guild.getRoleById(rank.getRoleId())).queue(); - } - - public void removeRankRole(long userId, int rankId) { - Rank rank = levelService.getRank(rankId); - guild.removeRoleFromMember(userId, guild.getRoleById(rank.getRoleId())).queue(); - } - - public void addItemRole(long userId, int itemId) { - Item item = levelService.getItem(itemId); - if (item.getRoleId() == -1) { - return; - } - guild.addRoleToMember(userId, guild.getRoleById(item.getRoleId())).queue(); - } - - public void removeItemRole(long userId, int itemId) { - Item item = levelService.getItem(itemId); - if (item.getRoleId() == -1) { - return; - } - guild.removeRoleFromMember(userId, guild.getRoleById(item.getRoleId())).queue(); - } - - public void addCollectEventRole(long userId) { - CollectEvent event = eventService.getActiveCollectEvent(guildId); - guild.addRoleToMember(userId, guild.getRoleById(event.getRoleId())).queue(); - } - - public void updateUserStatistics() { - userService.getAllUserIds().forEach(userService::updateUserStatistics); - } - - public void dmRankInfo() { - userService.getUsersByDailyEnabled().forEach(botUser -> { - User user = jda.getUserById(botUser.getUserId()); - user.openPrivateChannel().flatMap(privateChannel -> - privateChannel.sendMessageEmbeds(generateRankInfo(user, true).build()) - ).queue(null, new ErrorHandler().ignore(ErrorResponse.CANNOT_SEND_TO_USER)); - }); - } - - public void checkForNitroBoostersRewards() { - boosterService.getActiveNitroBoosters().forEach(nitroBooster -> { - if (TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - nitroBooster.getBoostStart()) % 30 == 0) { - boosterService.addMonthlyReward(nitroBooster.getUserId()); - } - }); - } - - public void sendItemExpiredInformation(long userId, int itemId, long buyTime) { - User user = jda.getUserById(userId); - Item item = levelService.getItem(itemId); - EmbedBuilder embed = embedCache.getEmbed("itemExpired") - .injectValue("item", item.getName()) - .injectValue("date", new SimpleDateFormat("dd.MM.yyyy HH:mm").format(new Date(buyTime))) - .toEmbedBuilder(); - user.openPrivateChannel().flatMap(privateChannel -> privateChannel.sendMessageEmbeds(embed.build())) - .queue(null, new ErrorHandler().handle(ErrorResponse.CANNOT_SEND_TO_USER, exception -> { - TextChannel channel = getBotChannel(); - channel.sendMessage(user.getAsMention()).and(channel.sendMessageEmbeds(embed.build())).queue(); - })); - } - - public EmbedBuilder generateRankInfo(User target, boolean dm) { - BotUser botUser = userService.getUserById(target.getIdLong()); - - Rank currentRank = levelService.getCurrentRank(botUser.getUserId()); - Rank nextRank = levelService.getNextRank(botUser.getUserId()); - long nextRankXp = nextRank.getBound() - botUser.getXp(); - long xpGain = botUser.getXp() - botUser.getStartXp(); - long coinsGain = botUser.getCoins() - botUser.getStartCoins(); - long diamondsGain = botUser.getDiamonds() - botUser.getStartDiamonds(); - - // if event is active, load another template - String embed = eventService.isCollectEventActive(guildId) ? "eventRankInfo" : "rankInfo"; - - String currentRankInfo = dm ? currentRank.getName() : String.format("<@&%d>", currentRank.getRoleId()); - String nextRankInfo = dm ? String.format("%s (noch %d XP)", nextRank.getName(), nextRankXp) : String.format("<@&%d> (noch %d XP)", nextRank.getRoleId(), nextRankXp); - nextRankInfo = currentRank.equals(nextRank) ? "N/A" : nextRankInfo; - String url = target.getAvatarUrl() == null ? "https://cdn.discordapp.com/embed/avatars/0.png" : target.getAvatarUrl(); - - EmbedDTO embedDTO = embedCache.getEmbed(embed) - .injectValue("user", target.getAsMention()) - .injectValue("color", currentRank.getColor()) - .injectValue("currentRank", currentRankInfo) - .injectValue("nextRank", nextRankInfo) - .injectValue("avatarUrl", url) - .injectValue("xpGain", xpGain) - .injectValue("coinsGain", coinsGain) - .injectValue("diamondsGain", diamondsGain) - .injectFields(botUser); - - - if (eventService.isCollectEventActive(guildId)) { - CollectEvent collectEvent = eventService.getActiveCollectEvent(guildId); - Role collectEventRole = guild.getRoleById(collectEvent.getRoleId()); - long eventPoints = botUser.getEventPoints(); - embedDTO.injectValue("eventName", collectEvent.getName()) - .injectValue("currencyName", collectEvent.getCurrencyName()) - .injectValue("currencyEmote", collectEvent.getCurrencyEmote()) - .injectValue("currencyPoints", eventPoints); - if (eventPoints >= 24) { - embedDTO.injectValue("eventRewards", ":red_circle: **Eventrolle XMAS 2022**\n:star2: **50 XP**\n:moneybag: **100 Münzen**"); - } else if (eventPoints >= 12) { - embedDTO.injectValue("eventRewards", ":red_circle: **Eventrolle XMAS 2022**\n:star2: **50 XP**"); - } else if (eventPoints >= 1) { - embedDTO.injectValue("eventRewards", ":red_circle: **Eventrolle XMAS 2022**"); - } else { - embedDTO.injectValue("eventRewards", ":no_entry_sign: noch keine"); - } - } - EmbedBuilder embedBuilder = embedDTO.toEmbedBuilder(); - - if (botUser.getTransactions().isEmpty()) { - embedBuilder.addField("Keine Items in Besitz", "", false); - } - - botUser.getTransactions().forEach(transaction -> { - Item item = transaction.getItem(); - embedBuilder.addField(item.getName(), "noch " + item.getRemainingTimeAsDate(transaction.getBuyTime()), false); - }); - - return embedBuilder; - } - - public void updateStatistics() { - statistics.queryStatistics().onSuccess(stats -> { - TextChannel channel = guild.getTextChannelById(WelcomeEmbedsCommand.WELCOME_CHANNEL_ID); - channel.retrieveMessageById(settingsService.getStatisticsMessageId(guildId)).flatMap(message -> - message.editMessageEmbeds(embedCache.getEmbed("statistics") - .injectFields(statistics) - .toEmbedBuilder() - .setTimestamp(Instant.now()) - .build()) - ).queue(); - }); - } - - public UserService getUserService() { - return userService; - } - - public ShopService getShopService() { - return shopService; - } - - public LevelService getLevelService() { - return levelService; - } - - public SettingsService getSettingsService() { - return settingsService; - } - - public BoosterService getBoosterService() { - return boosterService; - } - - public EventService getEventService() { - return eventService; - } - - public JDA getJda() { - return jda; - } - - public EmbedCache getEmbedCache() { - return embedCache; - } - - public Levelbot getLevelbot() { - return this; - } - - public Guild getGuild() { - return guild; - } - - public JDACommands getJdaCommands() { - return jdaCommands; - } - - public TextChannel getBotChannel() { - return botChannel; - } - - public TextChannel getLogChannel() { - return logChannel; - } - - public TaskScheduler getTaskScheduler() { - return taskScheduler; - } - - public enum GuildType { - - TESTING(496614159254028289L), - PRODUCTION(367353132772098048L); - - public final long id; - - GuildType(long id) { - this.id = id; - } - } -} diff --git a/src/main/java/de/kaktushose/levelbot/bot/ShutdownHttpServer.java b/src/main/java/de/kaktushose/levelbot/bot/ShutdownHttpServer.java deleted file mode 100644 index d1eea7b..0000000 --- a/src/main/java/de/kaktushose/levelbot/bot/ShutdownHttpServer.java +++ /dev/null @@ -1,50 +0,0 @@ -package de.kaktushose.levelbot.bot; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; - -public class ShutdownHttpServer { - - private static final Logger log = LoggerFactory.getLogger(ShutdownHttpServer.class); - private final Levelbot levelbot; - private HttpServer server; - - public ShutdownHttpServer(Levelbot levelbot, int port) { - this.levelbot = levelbot; - try { - server = HttpServer.create(new InetSocketAddress(port), 0); - server.createContext("/api/v1/shutdown", new ShutdownHandler()); - } catch (IOException e) { - log.error("Unable to create http server!", e); - } - } - - public void start() { - server.start(); - } - - private class ShutdownHandler implements HttpHandler { - @Override - public void handle(HttpExchange exchange) throws IOException { - log.info("Received shutdown request! Shutting down bot..."); - String response = "OK"; - exchange.sendResponseHeaders(200, response.getBytes(StandardCharsets.UTF_8).length); - OutputStream outputStream = exchange.getResponseBody(); - outputStream.write(response.getBytes()); - outputStream.close(); - new Thread(() -> { - levelbot.stop(); - levelbot.terminate(0); - }).start(); - server.stop(0); - } - } -} diff --git a/src/main/java/de/kaktushose/levelbot/bot/TaskScheduler.java b/src/main/java/de/kaktushose/levelbot/bot/TaskScheduler.java deleted file mode 100644 index 46c23f9..0000000 --- a/src/main/java/de/kaktushose/levelbot/bot/TaskScheduler.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.kaktushose.levelbot.bot; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -public class TaskScheduler { - - private final ScheduledExecutorService executorService; - - public TaskScheduler() { - executorService = Executors.newScheduledThreadPool(10); - } - - public void addSingleTask(Runnable runnable, long delay, TimeUnit timeUnit) { - executorService.schedule(runnable, delay, timeUnit); - } - - public void addRepetitiveTask(Runnable runnable, long initialDelay, long period, TimeUnit timeUnit) { - executorService.scheduleAtFixedRate(runnable, initialDelay, period, timeUnit); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/PermissionsService.java b/src/main/java/de/kaktushose/levelbot/commands/PermissionsService.java deleted file mode 100644 index 46830b4..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/PermissionsService.java +++ /dev/null @@ -1,50 +0,0 @@ -package de.kaktushose.levelbot.commands; - -import com.github.kaktushose.jda.commands.annotations.Component; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.dispatching.CommandContext; -import com.github.kaktushose.jda.commands.permissions.PermissionsProvider; -import de.kaktushose.levelbot.database.services.UserService; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.User; -import org.jetbrains.annotations.NotNull; - -@Component -public class PermissionsService implements PermissionsProvider { - - @Inject - private UserService userService; - - @Override - public boolean isMuted(@NotNull User user, @NotNull CommandContext context) { - return userService.getMutedUsers().contains(user.getIdLong()); - } - - @Override - public boolean hasPermission(@NotNull User user, @NotNull CommandContext context) { - for (String permission : context.getCommand().getPermissions()) { - if (!userService.getUsersByPermission(getLevelByName(permission)).contains(user.getIdLong())) { - return false; - } - } - return true; - } - - @Override - public boolean hasPermission(@NotNull Member member, @NotNull CommandContext context) { - return hasPermission(member.getUser(), context); - } - - private int getLevelByName(String name) { - switch (name) { - case "moderator": - return 2; - case "admin": - return 3; - default: - //for security reasons, commands with an unknown permission string can only be executed by level owner - return 4; - } - } - -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/member/BotInfoCommand.java b/src/main/java/de/kaktushose/levelbot/commands/member/BotInfoCommand.java deleted file mode 100644 index f85ee29..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/member/BotInfoCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.kaktushose.levelbot.commands.member; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.SettingsService; - -@CommandController(value = {"botinfo", "credits"}, category = "Sonstiges") -public class BotInfoCommand { - - @Inject - private SettingsService settingsService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Bot Information", - usage = "{prefix}botinfo", - desc = "Zeigt allgemeine Informationen über den Bot an" - ) - public void onBotInfo(CommandEvent event) { - long guildId = event.getGuild().getIdLong(); - event.reply(embedCache.getEmbed("botInfo") - .injectValue("prefix", settingsService.getBotPrefix(guildId)) - .injectValue("version", settingsService.getVersion(guildId)) - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/member/ChangeDiamondsCommand.java b/src/main/java/de/kaktushose/levelbot/commands/member/ChangeDiamondsCommand.java deleted file mode 100644 index ff9f67f..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/member/ChangeDiamondsCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -package de.kaktushose.levelbot.commands.member; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Optional; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.discord.reactionwaiter.EmoteType; -import de.kaktushose.discord.reactionwaiter.ReactionWaiter; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.services.UserService; - -@CommandController(value = {"tauschen", "wechseln"}, category = "Levelsystem") -public class ChangeDiamondsCommand { - - @Inject - private UserService userService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Diamanten tauschen", - usage = "{prefix}tauschen ", - desc = "Tauscht Diamanten gegen Münzen ein. Ein Diamant ist 20 Münzen wert" - ) - public void onChangeDiamonds(CommandEvent event, @Optional("1") long amount) { - BotUser botUser = userService.getUserById(event.getAuthor().getIdLong()); - long diamonds = botUser.getDiamonds(); - long coins = amount * 20; - if (diamonds == 0 || amount > diamonds) { - event.reply(embedCache.getEmbed("missingCurrency").injectValue("currency", "Diamanten")); - return; - } - - event.reply( - embedCache.getEmbed("confirmAction").injectValue( - "action", - String.format("du %d Diamanten gegen %d Münzen tauschen möchtest?", amount, coins) - ), - confirmMessage -> { - confirmMessage.addReaction(EmoteType.THUMBSUP.unicode) - .and(confirmMessage.addReaction(EmoteType.THUMBSDOWN.unicode)) - .queue(); - - ReactionWaiter reactionWaiter = new ReactionWaiter( - confirmMessage, - event.getMember(), - EmoteType.THUMBSUP.unicode, - EmoteType.THUMBSDOWN.unicode - ); - - reactionWaiter.onEvent(reactionEvent -> { - if (reactionEvent.getEmote().equals(EmoteType.THUMBSUP.unicode)) { - userService.exchangeDiamonds(botUser.getUserId(), amount); - confirmMessage.editMessageEmbeds(embedCache.getEmbed("diamondChangeSuccess") - .injectValue("diamonds", amount) - .injectValue("coins", coins) - .toMessageEmbed() - ).queue(); - } - reactionWaiter.stopWaiting(true); - }); - } - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/member/GiftCommand.java b/src/main/java/de/kaktushose/levelbot/commands/member/GiftCommand.java deleted file mode 100644 index 36ad447..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/member/GiftCommand.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.kaktushose.levelbot.commands.member; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.services.SettingsService; -import de.kaktushose.levelbot.shop.data.ShopService; -import de.kaktushose.levelbot.shop.data.items.ItemCategory; -import de.kaktushose.levelbot.shop.data.items.ItemVariant; -import net.dv8tion.jda.api.EmbedBuilder; - -import java.awt.*; - -@CommandController(value = "geschenk", category = "Levelsystem", isActive = false) -public class GiftCommand { - - @Inject - private SettingsService settingsService; - @Inject - private ShopService shopService; - @Inject - private Levelbot levelbot; - - @Command( - name = "Geschenke", - usage = "{prefix}geschenk", - desc = "Fröhliche Weihnachten!" - ) - public void onGift(CommandEvent event) { - if (settingsService.getRewardedUsers().contains(event.getAuthor().getIdLong())) { - event.reply("Du hast dein Geschenk bereits erhalten!"); - return; - } - shopService.addItem(event.getAuthor().getIdLong(), ItemCategory.PREMIUM, ItemVariant.LIGHT); - settingsService.addRewardedUser(event.getAuthor().getIdLong()); - event.reply(new EmbedBuilder() - .setTitle("Ein Geschenk für Dich, " + event.getMember().getEffectiveName() + ":christmas_tree::santa::snowflake:") - .setDescription("Das ganze Serverteam wünscht Dir **frohe Festtage** und einen **guten Rutsch.**\n\n" + - "Wir bedanken uns für Deine Treue und schenken Dir das Item:\n**:gift: PREMIUM light :star:!**\n\n" + - "Freue dich über **15 Tage kostenfreies PREMIUM** auf dem Server mit **vielen Vorteilen!**\n\n" + - "Dein Serverteam") - .setColor(Color.ORANGE) - ); - } - - // needed to hide the command - @Command("dummy") - public void onDummyCommand(CommandEvent event) { - onGift(event); - } - -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/member/LeaderboardCommand.java b/src/main/java/de/kaktushose/levelbot/commands/member/LeaderboardCommand.java deleted file mode 100644 index cde1041..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/member/LeaderboardCommand.java +++ /dev/null @@ -1,141 +0,0 @@ -package de.kaktushose.levelbot.commands.member; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.discord.reactionwaiter.ReactionWaiter; -import de.kaktushose.levelbot.database.services.LevelService; -import de.kaktushose.levelbot.util.Pagination; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Message; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import static de.kaktushose.levelbot.util.Pagination.CurrencyType; - -@CommandController(value = {"rangliste", "leaderboard", "lb"}, category = "Levelsystem") -public class LeaderboardCommand { - - private static final String BACK = "◀️"; - private static final String FORTH = "▶️"; - private static final String XP = "\uD83C\uDF1F"; - private static final String COINS = "\uD83D\uDCB0"; - private static final String DIAMONDS = "\uD83D\uDC8E"; - - @Inject - private LevelService levelService; - @Inject - private EmbedCache embedCache; - private Guild guild; - private CurrencyType currencyType; - private Map paginationMap; - - @Command( - name = "Rangliste", - usage = "{prefix}rangliste", - desc = "Zeigt eine Rangliste der Benutzer mit den meisten XP, Münzen oder Diamanten" - ) - public void onLeaderboard(CommandEvent event) { - this.guild = event.getGuild(); - currencyType = CurrencyType.XP; - paginationMap = new HashMap<>(); - paginationMap.put(CurrencyType.XP, levelService.getXpLeaderboard(10, event.getJDA())); - paginationMap.put(CurrencyType.COINS, levelService.getCoinsLeaderboard(10, event.getJDA())); - paginationMap.put(CurrencyType.DIAMONDS, levelService.getDiamondsLeaderboard(10, event.getJDA())); - showLeaderboard(event, null); - } - - public void showLeaderboard(CommandEvent event, Message sentMessage) { - Pagination pagination = paginationMap.get(currencyType); - Consumer success = message -> { - addReactions(message, pagination.index(), pagination.pages()); - ReactionWaiter waiter = new ReactionWaiter(message, event.getMember(), BACK, FORTH, XP, COINS, DIAMONDS); - waiter.onEvent(reactionEvent -> { - switch (reactionEvent.getEmote()) { - case BACK: - if (pagination.index() == 0) { - return; - } - pagination.previousPage(); - break; - case FORTH: - if (pagination.index() + 1 == pagination.pages()) { - return; - } - pagination.nextPage(); - break; - case XP: - if (currencyType == CurrencyType.XP) { - return; - } - currencyType = CurrencyType.XP; - break; - case COINS: - if (currencyType == CurrencyType.COINS) { - return; - } - currencyType = CurrencyType.COINS; - break; - case DIAMONDS: - if (currencyType == CurrencyType.DIAMONDS) { - return; - } - currencyType = CurrencyType.DIAMONDS; - break; - default: - break; - } - showLeaderboard(event, message); - waiter.stopWaiting(true); - }); - }; - - if (sentMessage == null) { - event.reply(buildEmbed(pagination.getPage(), pagination.index(), pagination.pages()), success); - } else { - sentMessage.editMessage(buildEmbed(pagination.getPage(), pagination.index(), pagination.pages()).build()).queue(success); - } - } - - private EmbedBuilder buildEmbed(List users, int index, int page) { - EmbedBuilder embedBuilder = embedCache.getEmbed("leaderboard") - .injectValue("guild", guild.getName()) - .injectValue("currency", currencyType.toString()) - .toEmbedBuilder(); - - StringBuilder stringBuilder = new StringBuilder(); - for (int i = 0; i < users.size(); i++) { - stringBuilder.append(String.format("`%d)` ", (i + 1) + index * 10)).append(users.get(i)).append("\n"); - } - - return embedBuilder.setDescription(stringBuilder.toString()) - .setFooter(String.format("Seite %d/%d", index + 1, page)); - } - - private void addReactions(Message message, int index, int pages) { - if (index == 0) { - message.addReaction(FORTH).queue(); - } else if (index + 1 == pages) { - message.addReaction(BACK).queue(); - } else { - message.addReaction(BACK).and(message.addReaction(FORTH)).queue(); - } - switch (currencyType) { - case XP: - message.addReaction(COINS).and(message.addReaction(DIAMONDS)).queue(); - break; - case COINS: - message.addReaction(XP).and(message.addReaction(DIAMONDS)).queue(); - break; - case DIAMONDS: - message.addReaction(XP).and(message.addReaction(COINS)).queue(); - break; - } - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/member/PingCommand.java b/src/main/java/de/kaktushose/levelbot/commands/member/PingCommand.java deleted file mode 100644 index ae896a7..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/member/PingCommand.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.kaktushose.levelbot.commands.member; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; - -@CommandController(value = "ping", category = "Sonstiges") -public class PingCommand { - - @Inject - private EmbedCache embedCache; - - @Command( - name = "Ping Command", - usage = "{prefix}ping", - desc = "Zeigt den Ping zur Discord-API an") - public void onPing(CommandEvent event) { - long gatewayPing = event.getJDA().getGatewayPing(); - long restPing = event.getJDA().getRestPing().complete(); - event.reply(embedCache - .getEmbed("pingEmbed") - .injectValue("gatewayPing", gatewayPing) - .injectValue("restPing", restPing) - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/member/RankInfoCommand.java b/src/main/java/de/kaktushose/levelbot/commands/member/RankInfoCommand.java deleted file mode 100644 index d4befd5..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/member/RankInfoCommand.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.kaktushose.levelbot.commands.member; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Optional; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import de.kaktushose.levelbot.bot.Levelbot; -import net.dv8tion.jda.api.entities.Member; - -@CommandController(value = {"info", "rank", "konto"}, category = "Levelsystem") -public class RankInfoCommand { - - @Inject - Levelbot levelbot; - - @Command( - name = "Kontoinformation abrufen", - usage = "{prefix}info ", - desc = "Zeigt die Kontoinformationen zu einem User an" - ) - public void onRankInfo(CommandEvent event, @Optional Member member) { - Member target = member == null ? event.getMember() : member; - event.reply(levelbot.generateRankInfo(target.getUser(), false)); - } -} - diff --git a/src/main/java/de/kaktushose/levelbot/commands/member/SwitchDailyCommand.java b/src/main/java/de/kaktushose/levelbot/commands/member/SwitchDailyCommand.java deleted file mode 100644 index d9488b3..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/member/SwitchDailyCommand.java +++ /dev/null @@ -1,44 +0,0 @@ -package de.kaktushose.levelbot.commands.member; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.UserService; -import net.dv8tion.jda.api.exceptions.ErrorHandler; -import net.dv8tion.jda.api.requests.ErrorResponse; - -@CommandController(value = {"täglich", "daily"}, category = "Levelsystem") -public class SwitchDailyCommand { - - @Inject - private EmbedCache embedCache; - @Inject - private UserService userService; - - @Command( - name = "Täglich Command", - usage = "{prefix}täglich", - desc = "Aktiviert bzw. deaktiviert die täglichen Kontoinformationen" - ) - public void onSwitchDaily(CommandEvent event) { - if (!userService.switchDaily(event.getAuthor().getIdLong())) { - event.reply(embedCache.getEmbed("switchDailySuccess").injectValue("action", "deaktiviert")); - } else { - event.getAuthor().openPrivateChannel() - .flatMap(privateChannel -> - privateChannel.sendMessageEmbeds(embedCache.getEmbed("switchDailySuccess") - .injectValue("action", "aktiviert") - .toMessageEmbed() - ) - ) - .queue(success -> event.getMessage().delete().queue(), - new ErrorHandler().handle(ErrorResponse.CANNOT_SEND_TO_USER, e -> { - userService.switchDaily(event.getAuthor().getIdLong()); - event.reply(embedCache.getEmbed("switchDailyError")); - }) - ); - } - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/BlacklistCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/BlacklistCommand.java deleted file mode 100644 index 826b845..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/BlacklistCommand.java +++ /dev/null @@ -1,80 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.services.UserService; -import net.dv8tion.jda.api.entities.Member; - -import java.util.List; - -@CommandController(value = {"blacklist", "banlist", "bl"}, category = "Moderation") -@Permission("moderator") -public class BlacklistCommand { - - @Inject - private UserService userService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Blacklist", - usage = "{prefix}blacklist | {prefix}blacklist list", - desc = "Benutzer, die auf der Blacklist stehen, können keine Commands ausführen", - isSuper = true - ) - public void onBlacklist(CommandEvent event) { - event.sendSpecificHelpMessage(); - } - - @Command( - value = "add", - name = "Benutzer sperren", - usage = "{prefix}blacklist add ", - desc = "Fügt einen Benutzer zur Blacklist hinzu" - ) - public void onBlacklistAdd(CommandEvent event, Member member) { - BotUser executor = userService.getUserById(event.getAuthor().getIdLong()); - BotUser target = userService.getUserById(member.getIdLong()); - // can only blacklist users with lower permissions - if (executor.getPermissionLevel() < target.getPermissionLevel()) { - event.reply(embedCache.getEmbed("memberBlacklistInvalidTarget").injectValue("user", member.getAsMention())); - return; - } - userService.setPermission(member.getIdLong(), 0); - event.reply(embedCache.getEmbed("memberBlacklistAdd").injectValue("user", member.getAsMention())); - } - - @Command( - value = {"remove", "rm"}, - name = "Benutzer entsperren", - usage = "{prefix}blacklist remove ", - desc = "Entfernt einen Benutzer von der Blacklist" - ) - public void onBlacklistRemove(CommandEvent event, Member member) { - BotUser target = userService.getUserById(member.getIdLong()); - userService.setPermission(member.getIdLong(), 1); - event.reply(embedCache.getEmbed("memberBlacklistRemove") - .injectValue("user", member.getAsMention()) - ); - } - - @Command( - value = {"show", "list", "view"}, - name = "Gesperrte Benutzer", - usage = "{prefix}blacklist show", - desc = "Zeigt alle Nutzer, die auf der Blacklist stehen" - ) - public void onBlacklistShow(CommandEvent event) { - List blacklist = userService.getUsersByPermission(0); - StringBuilder members = new StringBuilder(); - blacklist.forEach(id -> members.append(event.getGuild().retrieveMemberById(id).complete().getEffectiveName()).append(", ")); - event.reply(embedCache.getEmbed("memberBlacklistShow") - .injectValue("blacklist", members.substring(0, members.length() - 2)) - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/BulkDeleteCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/BulkDeleteCommand.java deleted file mode 100644 index 5fb1387..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/BulkDeleteCommand.java +++ /dev/null @@ -1,46 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.annotations.constraints.Max; -import com.github.kaktushose.jda.commands.annotations.constraints.Min; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.exceptions.ErrorHandler; -import net.dv8tion.jda.api.requests.ErrorResponse; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -@CommandController(value = {"delete", "purge", "clear"}, category = "Moderation") -@Permission("moderator") -public class BulkDeleteCommand { - - @Inject - private EmbedCache embedCache; - - @Command( - name = "Nachrichten löschen", - usage = "{prefix}delete ", - desc = "Löscht die angegebene Zahl von Nachrichten aus einem Channel" - ) - public void onBulkDeleteMessages( - CommandEvent event, - @Min(value = 1, message = "Muss mindestens eine Nachricht löschen") - @Max(value = 100, message = "Kann maximal 100 Nachrichten gleichzeitig löschen") int amount - ) { - // amount + 1 so we delete the command message as well - // complete aka blocking to make sure that the success message is sent correctly - List messageHistory = event.getChannel().getHistory().retrievePast(amount + 1).complete(); - messageHistory.forEach(message -> message.delete().complete()); - - event.reply(embedCache.getEmbed("bulkDeleteSuccess") - .injectValue("amount", amount), - message -> message.delete().queueAfter(10, TimeUnit.SECONDS, - null, new ErrorHandler().ignore(ErrorResponse.UNKNOWN_MESSAGE) - )); // delete success message after 10 secs - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/ChangeCurrencyCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/ChangeCurrencyCommand.java deleted file mode 100644 index b36ca1c..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/ChangeCurrencyCommand.java +++ /dev/null @@ -1,81 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.UserService; -import net.dv8tion.jda.api.entities.Member; - -@CommandController(value = "add", category = "Moderation") -@Permission("moderator") -public class ChangeCurrencyCommand { - - @Inject - private UserService userService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Add", - usage = "{prefix}add ", - desc = "Ändert die Währungen eines Nutzers um den angegebenen Wert", - isSuper = true - ) - public void onChangeCurrency(CommandEvent event) { - event.sendSpecificHelpMessage(); - } - - @Command( - value = "coins", - name = "Münzen ändern", - usage = "{prefix}add coins ", - desc = "Ändert die Anzahl der Münzen eines Benutzers um den angegebenen Wert.", - category = "Moderation" - ) - public void onAddCoins(CommandEvent event, Member member, Integer amount) { - userService.addCoins(member.getIdLong(), amount); - event.reply(embedCache.getEmbed("currencyChange") - .injectValue("currency", "Münzen") - .injectValue("user", member.getAsMention()) - .injectValue("value", amount) - .injectValue("operation", amount > 0 ? "erhöht" : "verringert") - ); - } - - @Command( - value = "xp", - name = "XP ändern", - usage = "{prefix}add xp ", - desc = "Ändert die Anzahl der XP eines Benutzers um den angegebenen Wert.", - category = "Moderation" - ) - public void onAddXp(CommandEvent event, Member member, Integer amount) { - userService.addXp(member.getIdLong(), amount); - event.reply(embedCache.getEmbed("currencyChange") - .injectValue("currency", "XP") - .injectValue("user", member.getAsMention()) - .injectValue("value", amount) - .injectValue("operation", amount > 0 ? "erhöht" : "verringert") - ); - } - - @Command( - value = "diamonds", - name = "Diamanten ändern", - usage = "{prefix}add diamonds ", - desc = "Ändert die Anzahl der Diamanten eines Benutzers um den angegebenen Wert.", - category = "Moderation" - ) - public void onAddDiamonds(CommandEvent event, Member member, Integer amount) { - userService.addDiamonds(member.getIdLong(), amount); - event.reply(embedCache.getEmbed("currencyChange") - .injectValue("currency", "Diamanten") - .injectValue("user", member.getAsMention()) - .injectValue("value", amount) - .injectValue("operation", amount > 0 ? "erhöht" : "verringert") - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/IgnoreChannelCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/IgnoreChannelCommand.java deleted file mode 100644 index 4309e8f..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/IgnoreChannelCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.SettingsService; -import net.dv8tion.jda.api.entities.TextChannel; - -import java.util.List; - -@CommandController(value = "ignore", category = "Moderation") -@Permission("moderator") -public class IgnoreChannelCommand { - - @Inject - private SettingsService settingsService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Textkanal muten", - usage = "{prefix}ignore ", - desc = "Mutet einen Textkanal", - category = "Moderation", - isSuper = true - ) - public void onMutedChannelsAdd(CommandEvent event, TextChannel channel) { - if (!settingsService.isIgnoredChannel(channel.getIdLong())) { - settingsService.addIgnoredChannel(channel.getIdLong()); - } else { - event.reply(embedCache.getEmbed("mutedChannelsAdd").injectValue("channel", channel.getAsMention())); - } - } - - @Command( - value = {"remove", "rm"}, - name = "Textkanal unmuten", - usage = "{prefix}ignore remove ", - desc = "Unmutet einen Textkanal", - category = "Moderation" - ) - public void onMutedChannelsRemove(CommandEvent event, TextChannel channel) { - settingsService.removeIgnoredChannel(channel.getIdLong()); - event.reply(embedCache.getEmbed("mutedChannelsRemove").injectValue("channel", channel.getAsMention())); - } - - @Command( - value = "list", - name = "Gemutete Textkanäle", - usage = "{prefix}ignore list", - desc = "Zeigt alle Channel, die vom Levelsystem ignoriert werden", - category = "Moderation" - ) - public void onMutedChannelsList(CommandEvent event) { - List mutedChannels = settingsService.getIgnoredChannels(); - StringBuilder list = new StringBuilder(); - mutedChannels.forEach(channelId -> list.append(String.format("<#%d>", channelId)).append("\n")); - event.reply(embedCache.getEmbed("mutedChannelsList").injectValue("list", list)); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/SetCurrencyCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/SetCurrencyCommand.java deleted file mode 100644 index 979287e..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/SetCurrencyCommand.java +++ /dev/null @@ -1,77 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.UserService; -import net.dv8tion.jda.api.entities.Member; - -@CommandController(value = "set", category = "Moderation") -@Permission("moderator") -public class SetCurrencyCommand { - - @Inject - private UserService userService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Set", - usage = "{prefix}set ", - desc = "Setzt die Währungen eines Nutzers auf den angegebenen Wert", - isSuper = true) - public void onSetCurrency(CommandEvent event) { - event.sendSpecificHelpMessage(); - } - - @Command( - value = "coins", - name = "Münzen setzen", - usage = "{prefix}set coins ", - desc = "Setzt die Anzahl der Münzen eines Benutzers auf den angegebenen Wert.", - category = "Moderation" - ) - public void onSetCoins(CommandEvent event, Member member, Integer amount) { - userService.setCoins(member.getIdLong(), amount); - event.reply(embedCache.getEmbed("currencySet") - .injectValue("currency", "Münzen") - .injectValue("user", member.getAsMention()) - .injectValue("value", amount) - ); - } - - @Command( - value = "xp", - name = "XP setzen", - usage = "{prefix}set xp ", - desc = "Setzt die Anzahl der XP eines Benutzers auf den angegebenen Wert.", - category = "Moderation" - ) - public void onSetXp(CommandEvent event, Member member, Integer amount) { - userService.setXp(member.getIdLong(), amount); - event.reply(embedCache.getEmbed("currencySet") - .injectValue("currency", "XP") - .injectValue("user", member.getAsMention()) - .injectValue("value", amount) - ); - } - - @Command( - value = "diamonds", - name = "Diamanten setzen", - usage = "{prefix}set diamonds ", - desc = "Setzt die Anzahl der Diamanten eines Benutzers auf den angegebenen Wert.", - category = "Moderation" - ) - public void onSetDiamonds(CommandEvent event, Member member, Integer amount) { - userService.setDiamonds(member.getIdLong(), amount); - event.reply(embedCache.getEmbed("currencySet") - .injectValue("currency", "Diamanten") - .injectValue("user", member.getAsMention()) - .injectValue("value", amount) - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/SetPermsCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/SetPermsCommand.java deleted file mode 100644 index f12eb10..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/SetPermsCommand.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.annotations.constraints.NotPerm; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.services.UserService; -import net.dv8tion.jda.api.entities.Member; - -@CommandController(value = "setperms", category = "Moderation") -@Permission("moderator") -public class SetPermsCommand { - - @Inject - private UserService userService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Berechtigung ändern", - usage = "{prefix}setperms ", - desc = "Setzt das Berechtigungslevel eines Benutzers auf den angegebenen Wert" - ) - public void onSetPerms(CommandEvent event, Member member, int level) { - if (level < 1 || level > 4) { - event.reply(embedCache.getEmbed("invalidValue") - .injectValue("min", 1) - .injectValue("max", 4) - ); - return; - } - - BotUser executor = userService.getUserById(event.getAuthor().getIdLong()); - BotUser target = userService.getUserById(member.getIdLong()); - - // can only update users with lower perms - if (executor.getPermissionLevel() < level + 1 || executor.getPermissionLevel() < target.getPermissionLevel()) { - event.reply(embedCache.getEmbed("permissionSetInvalidTarget") - .injectValue("user", member.getAsMention()) - ); - return; - } - - userService.setPermission(target.getUserId(), level); - - event.reply(embedCache.getEmbed("permissionSet") - .injectValue("user", member.getAsMention()) - .injectValue("value", level) - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/WelcomeEmbedsCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/WelcomeEmbedsCommand.java deleted file mode 100644 index ffc1f9d..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/WelcomeEmbedsCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; - -import java.io.File; - -@CommandController(value = "sendembeds", category = "Moderation") -@Permission("moderator") -public class WelcomeEmbedsCommand { - - public static final long WELCOME_CHANNEL_ID = 851434963316375602L; - private final EmbedCache welcomeEmbedCache; - @Inject - private EmbedCache embedCache; - - public WelcomeEmbedsCommand() { - welcomeEmbedCache = new EmbedCache(new File("welcomeEmbeds.json")); - } - - @Command( - name = "Willkommen Embeds senden", - usage = "{prefix}sendembeds", - desc = "Sendet die Embeds in <#551483788337872927>" - ) - public void sendEmbeds(CommandEvent event) { - welcomeEmbedCache.loadEmbedsToCache(); - welcomeEmbedCache.values().forEach(embedDTO -> { - event.getGuild().getTextChannelById(WELCOME_CHANNEL_ID).sendMessageEmbeds(embedDTO.toMessageEmbed()).queue(); - }); - } -} - diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/events/BalanceEventCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/events/BalanceEventCommand.java deleted file mode 100644 index 5ab4d63..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/events/BalanceEventCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation.events; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.EventService; - -@CommandController(value = "balance", category = "Moderation") -@Permission("moderator") -public class BalanceEventCommand { - - @Inject - private EventService eventService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Balance Event", - usage = "{prefix}balance | {prefix}balance list", - desc = "Balance Events verändern die Ausschüttung der Währungen", - isSuper = true - ) - public void onBalance(CommandEvent event) { - event.sendSpecificHelpMessage(); - } - - @Command( - value = "start", - name = "Balance Event aktivieren", - usage = "{prefix}balance start ", - desc = "Startet das Balance Event mit der angegeben ID" - ) - public void onBalanceEventStart(CommandEvent event, int id) { - if (id < 0 || id > 3) { - event.reply(embedCache.getEmbed("unknownEventId")); - return; - } - String name = eventService.startBalanceEvent(id, event.getGuild().getIdLong()); - event.reply(embedCache.getEmbed("balanceEventStart").injectValue("name", name)); - } - - @Command( - value = "stop", - name = "Balance Event deaktivieren", - usage = "{prefix}balance stop ", - desc = "Stoppt das Balance Event mit der angegeben ID" - ) - public void onBalanceEventStop(CommandEvent event, int id) { - if (id < 0 || id > 3) { - event.reply(embedCache.getEmbed("unknownEventId")); - return; - } - String name = eventService.stopBalanceEvent(id, event.getGuild().getIdLong()); - event.reply(embedCache.getEmbed("balanceEventStop").injectValue("name", name)); - } - - @Command( - value = "list", - name = "Balance Event Arten", - usage = "{prefix}balance list", - desc = "Zeigt eine Liste aller verfügbaren Balance Events an" - ) - public void onBalanceEventList(CommandEvent event) { - event.reply(embedCache.getEmbed("balanceEventList")); - } - -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/events/CollectEventCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/events/CollectEventCommand.java deleted file mode 100644 index 0359486..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/events/CollectEventCommand.java +++ /dev/null @@ -1,81 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation.events; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.model.CollectEvent; -import de.kaktushose.levelbot.database.services.EventService; - -import java.util.List; - -@CommandController(value = "collect", category = "Moderation") -@Permission("moderator") -public class CollectEventCommand { - - @Inject - private EventService eventService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Collect Event", - usage = "{prefix}collect | {prefix}collect list", - desc = "Collect Events mit zusätzlichen Währungen", - isSuper = true - ) - public void onCollect(CommandEvent event) { - event.sendSpecificHelpMessage(); - } - - @Command( - value = "start", - name = "Sammel Event aktivieren", - usage = "{prefix}collect start ", - desc = "Startet das Sammel Event mit der angegeben ID", - category = "Moderation" - ) - public void onCollectEventStart(CommandEvent event, int id) { - if (!eventService.collectEventExistsById(id)) { - event.reply(embedCache.getEmbed("unknownEventId")); - return; - } - String name = eventService.startCollectEvent(id, event.getGuild()); - event.reply(embedCache.getEmbed("collectEventStart").injectValue("name", name)); - } - - @Command( - value = "stop", - name = "Collect Event deaktivieren", - usage = "{prefix}collect stop", - desc = "Stoppt das aktuelle Collect Event", - category = "Moderation" - ) - public void onCollectEventStop(CommandEvent event) { - if (eventService.stopCollectEvent(event.getGuild().getIdLong())) { - event.reply(embedCache.getEmbed("collectEventStop")); - } else { - event.reply(embedCache.getEmbed("noActiveCollectEvent")); - } - } - - @Command( - value = "list", - name = "Collect Event Arten", - usage = "{prefix}collect list", - desc = "Zeigt eine Liste aller verfügbaren Collect Events an", - category = "Moderation" - ) - public void onCollectEventList(CommandEvent event) { - List events = eventService.getAllCollectEvents(); - StringBuilder builder = new StringBuilder(); - events.forEach(collectEvent -> { - builder.append(String.format("%d: %s\n", collectEvent.getEventId(), collectEvent.getName())); - }); - String list = builder.length() == 0 ? "N/A" : builder.substring(0, builder.length() - 1); - event.reply(embedCache.getEmbed("collectEventList").injectValue("list", list)); - } - -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/moderation/events/ContestEventCommand.java b/src/main/java/de/kaktushose/levelbot/commands/moderation/events/ContestEventCommand.java deleted file mode 100644 index 965aef9..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/moderation/events/ContestEventCommand.java +++ /dev/null @@ -1,68 +0,0 @@ -package de.kaktushose.levelbot.commands.moderation.events; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.EventService; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.TextChannel; - -import java.util.List; - -@CommandController(value = "contest", category = "Moderation") -@Permission("moderator") -public class ContestEventCommand { - - @Inject - private EventService eventService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Contest Event", - usage = "{prefix}contest start | {prefix}contest stop", - desc = "Bilder Contest Event", - isSuper = true - ) - public void onContest(CommandEvent event) { - event.sendSpecificHelpMessage(); - } - - @Command( - value = "start", - name = "Contest Event aktivieren", - usage = "{prefix}contest start ", - desc = "Startet ein Bilder Contest Event", - category = "Moderation" - ) - public void onContestEventStart(CommandEvent event, TextChannel textChannel, String emote) { - eventService.startContestEvent(event.getGuild().getIdLong(), textChannel.getIdLong(), emote); - event.reply(embedCache.getEmbed("contestEventStart")); - } - - @Command( - value = "stop", - name = "Contest Event deaktivieren", - usage = "{prefix}contest stop", - desc = "Stoppt ein Bilder Contest Event", - category = "Moderation" - ) - public void onContestEventStop(CommandEvent event) { - eventService.stopContestEvent(event.getGuild().getIdLong()); - event.reply(embedCache.getEmbed("contestEventStop")); - List users = eventService.getVoteResult(10, event.getJDA()).getPage(); - EmbedBuilder embedBuilder = embedCache.getEmbed("leaderboard") - .injectValue("guild", "Contest Event") - .injectValue("currency", "") - .toEmbedBuilder(); - StringBuilder result = new StringBuilder(); - for (int i = 0; i < users.size(); i++) { - result.append(String.format("`%d)` ", i + 1)).append(users.get(i)).append("\n"); - } - event.reply(embedBuilder.setDescription(result.toString())); - } - -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/owner/EvalCommand.java b/src/main/java/de/kaktushose/levelbot/commands/owner/EvalCommand.java deleted file mode 100644 index 21bf773..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/owner/EvalCommand.java +++ /dev/null @@ -1,66 +0,0 @@ -package de.kaktushose.levelbot.commands.owner; - -import com.github.kaktushose.jda.commands.annotations.*; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.bot.Levelbot; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; - -@CommandController("eval") -@Permission("owner") -public class EvalCommand { - - private static final Logger log = LoggerFactory.getLogger(EvalCommand.class); - private final ScriptEngine engine; - @Inject - private Levelbot levelbot; - @Inject - private EmbedCache embedCache; - - public EvalCommand() { - engine = new ScriptEngineManager().getEngineByName("nashorn"); - try { - engine.eval("var imports = new JavaImporter(" + - "java.io," + - "java.lang," + - "java.util)" - ); - } catch (ScriptException e) { - log.error("Unable to initialize script engine!", e); - } - } - - @Command( - name = "Code ausführen", - usage = "{prefix}eval ", - desc = "Führt Code in der aktuellen Runtime des Bots aus", - category = "Owner" - ) - public void onEval(CommandEvent event, @Concat String code) { - log.warn("Executing eval command with code: {}", code); - engine.put("levelbot", levelbot); - engine.put("event", event); - code = code.replaceAll("`", ""); - - String script = String.format("(function() { with (imports) {%s} } )();", code); - Object result; - String color; - try { - result = engine.eval(script); - color = "#86c240"; - } catch (ScriptException e) { - result = e; - color = "#aa0c14"; - } - log.info("Eval Command returned result: {}", result); - event.reply(embedCache.getEmbed("evalCommand") - .injectValue("result", result) - .injectValue("color", color) - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/commands/owner/StopCommand.java b/src/main/java/de/kaktushose/levelbot/commands/owner/StopCommand.java deleted file mode 100644 index f0daa99..0000000 --- a/src/main/java/de/kaktushose/levelbot/commands/owner/StopCommand.java +++ /dev/null @@ -1,56 +0,0 @@ -package de.kaktushose.levelbot.commands.owner; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.discord.reactionwaiter.EmoteType; -import de.kaktushose.discord.reactionwaiter.ReactionWaiter; -import de.kaktushose.levelbot.bot.Levelbot; - -@CommandController(value = {"stop", "shutdown"}) -@Permission("owner") -public class StopCommand { - - @Inject - private Levelbot levelbot; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Bot herunterfahren", - usage = "{prefix}stop", - desc = "Fährt den Bot herunter", - category = "Owner" - ) - public void onStop(CommandEvent event) { - event.reply(embedCache.getEmbed("confirmAction").injectValue( - "action", - "du den Bot herunterfahren möchtest?\nNur <@393843637437464588> kann den Bot wieder starten!" - ), - confirmMessage -> { - confirmMessage.addReaction(EmoteType.THUMBSUP.unicode) - .and(confirmMessage.addReaction(EmoteType.THUMBSDOWN.unicode)) - .queue(); - - ReactionWaiter reactionWaiter = new ReactionWaiter( - confirmMessage, - event.getMember(), - EmoteType.THUMBSUP.unicode, - EmoteType.THUMBSDOWN.unicode - ); - - reactionWaiter.onEvent(reactionEvent -> { - if (reactionEvent.getEmote().equals(EmoteType.THUMBSUP.unicode)) { - event.reply("https://tenor.com/view/tekashi-69-fade-out-peace-gif-15141419"); - confirmMessage.delete().queue(); - levelbot.stop().terminate(0); - } - reactionWaiter.stopWaiting(true); - }); - } - ); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/model/BotUser.java b/src/main/java/de/kaktushose/levelbot/database/model/BotUser.java deleted file mode 100644 index 94253d6..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/model/BotUser.java +++ /dev/null @@ -1,221 +0,0 @@ -package de.kaktushose.levelbot.database.model; - -import de.kaktushose.levelbot.shop.data.transactions.Transaction; -import de.kaktushose.levelbot.util.Pageable; -import de.kaktushose.levelbot.util.Pagination; - -import javax.persistence.*; -import java.util.List; - -@Entity -@Table(name = "users") -public class BotUser implements Pageable { - - @Id - private Long userId; - private int level; - private long xp; - private long coins; - private long diamonds; - private long lastValidMessage; - private long messageCount; - private long startXp; - private long startCoins; - private long startDiamonds; - @Column(name = "daily") - private boolean dailyUpdate; - private int permissionLevel; - private int rewardLevel; - private long lastReward; - private long eventPoints; - @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER) - @JoinColumn(name = "userId", referencedColumnName = "userId") - private List transactions; - - public BotUser() { - level = 1; - } - - public BotUser(long userId) { - this.userId = userId; - level = 1; - permissionLevel = 1; - } - - public BotUser(long userId, - int level, - long xp, - long coins, - long diamonds, - long lastValidMessage, - long messageCount, - long startXp, - long startCoins, - long startDiamonds, - boolean dailyUpdate, - int permissionLevel, - int rewardLevel, - long lastReward, - long eventPoints, - List transactions) { - this.userId = userId; - this.level = level; - this.xp = xp; - this.coins = coins; - this.diamonds = diamonds; - this.lastValidMessage = lastValidMessage; - this.messageCount = messageCount; - this.startXp = startXp; - this.startCoins = startCoins; - this.startDiamonds = startDiamonds; - this.dailyUpdate = dailyUpdate; - this.permissionLevel = permissionLevel; - this.rewardLevel = rewardLevel; - this.lastReward = lastReward; - this.eventPoints = eventPoints; - this.transactions = transactions; - } - - @Override - public Long getUserId() { - return userId; - } - - @Override - public long getCount(Pagination.CurrencyType currencyType) { - switch (currencyType) { - case XP: - return getXp(); - case DIAMONDS: - return getDiamonds(); - case COINS: - return getCoins(); - default: - return 0; - } - } - - public void setUserId(Long userId) { - this.userId = userId; - } - - public int getLevel() { - return level; - } - - public void setLevel(int level) { - this.level = level; - } - - public long getXp() { - return xp; - } - - public void setXp(long xp) { - this.xp = xp; - } - - public long getCoins() { - return coins; - } - - public void setCoins(long coins) { - this.coins = coins; - } - - public long getDiamonds() { - return diamonds; - } - - public void setDiamonds(long diamonds) { - this.diamonds = diamonds; - } - - public long getLastValidMessage() { - return lastValidMessage; - } - - public void setLastValidMessage(long lastValidMessage) { - this.lastValidMessage = lastValidMessage; - } - - public long getMessageCount() { - return messageCount; - } - - public void setMessageCount(long messageCount) { - this.messageCount = messageCount; - } - - public long getStartXp() { - return startXp; - } - - public void setStartXp(long startXp) { - this.startXp = startXp; - } - - public long getStartCoins() { - return startCoins; - } - - public void setStartCoins(long startCoins) { - this.startCoins = startCoins; - } - - public long getStartDiamonds() { - return startDiamonds; - } - - public void setStartDiamonds(long startDiamonds) { - this.startDiamonds = startDiamonds; - } - - public boolean isDailyUpdate() { - return dailyUpdate; - } - - public void setDailyUpdate(boolean dailyUpdate) { - this.dailyUpdate = dailyUpdate; - } - - public int getPermissionLevel() { - return permissionLevel; - } - - public void setPermissionLevel(int permissionLevel) { - this.permissionLevel = permissionLevel; - } - - public int getRewardLevel() { - return rewardLevel; - } - - public void setRewardLevel(int rewardLevel) { - this.rewardLevel = rewardLevel; - } - - public long getLastReward() { - return lastReward; - } - - public void setLastReward(long lastReward) { - this.lastReward = lastReward; - } - - public long getEventPoints() { - return eventPoints; - } - - public void setEventPoints(long eventPoints) { - this.eventPoints = eventPoints; - } - - public List getTransactions() { - return transactions; - } - - public void setTransactions(List transactions) { - this.transactions = transactions; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/model/CollectEvent.java b/src/main/java/de/kaktushose/levelbot/database/model/CollectEvent.java deleted file mode 100644 index ccf095e..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/model/CollectEvent.java +++ /dev/null @@ -1,107 +0,0 @@ -package de.kaktushose.levelbot.database.model; - -import de.kaktushose.levelbot.shop.data.items.Item; - -import javax.persistence.*; - -@Table(name = "collect_events") -@Entity -public class CollectEvent { - - @Id - private Integer eventId; - private String name; - private String currencyName; - private String currencyEmote; - @OneToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "itemId", referencedColumnName = "itemId") - private Item item; - private long roleId; - private int itemBound; - private int roleBound; - - public CollectEvent() { - } - - public CollectEvent(Integer eventId, - String name, - String currencyName, - String currencyEmote, - Item item, - long roleId, - int itemBound, - int roleBound) { - this.eventId = eventId; - this.name = name; - this.currencyName = currencyName; - this.currencyEmote = currencyEmote; - this.item = item; - this.roleId = roleId; - this.itemBound = itemBound; - this.roleBound = roleBound; - } - - public Integer getEventId() { - return eventId; - } - - public void setEventId(Integer eventId) { - this.eventId = eventId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getCurrencyName() { - return currencyName; - } - - public void setCurrencyName(String currencyName) { - this.currencyName = currencyName; - } - - public String getCurrencyEmote() { - return currencyEmote; - } - - public void setCurrencyEmote(String currencyEmote) { - this.currencyEmote = currencyEmote; - } - - public Item getItem() { - return item; - } - - public void setItem(Item item) { - this.item = item; - } - - public long getRoleId() { - return roleId; - } - - public void setRoleId(long roleId) { - this.roleId = roleId; - } - - public int getItemBound() { - return itemBound; - } - - public void setItemBound(int itemBound) { - this.itemBound = itemBound; - } - - public int getRoleBound() { - return roleBound; - } - - public void setRoleBound(int roleBound) { - this.roleBound = roleBound; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/model/ContestEntry.java b/src/main/java/de/kaktushose/levelbot/database/model/ContestEntry.java deleted file mode 100644 index 8dac324..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/model/ContestEntry.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.kaktushose.levelbot.database.model; - -import de.kaktushose.levelbot.util.Pageable; -import de.kaktushose.levelbot.util.Pagination; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -@Entity -@Table(name = "contest_entries") -public class ContestEntry implements Pageable { - - @Id - private long messageId; - private long count; - private long userId; - - public ContestEntry() { - } - - public ContestEntry(long messageId, long userId, long count) { - this.messageId = messageId; - this.count = count; - this.userId = userId; - } - - public Long getUserId() { - return userId; - } - - public void setUserId(long userId) { - this.userId = userId; - } - - public long getCount(Pagination.CurrencyType currencyType) { - return count; - } - - public void setCount(long count) { - this.count = count; - } - - public long getMessageId() { - return messageId; - } - - public void setMessageId(long messageId) { - this.messageId = messageId; - } - - public long getCount() { - return count; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/model/CurrencyChance.java b/src/main/java/de/kaktushose/levelbot/database/model/CurrencyChance.java deleted file mode 100644 index 7471696..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/model/CurrencyChance.java +++ /dev/null @@ -1,68 +0,0 @@ -package de.kaktushose.levelbot.database.model; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -@Entity -@Table(name = "currency_chances") -public class CurrencyChance { - - @Id - private Integer id; - private int amount; - private int chance; - private int type; - - public CurrencyChance() { - } - - public CurrencyChance(int id, int amount, int chance, int type) { - this.id = id; - this.amount = amount; - this.chance = chance; - this.type = type; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public int getAmount() { - return amount; - } - - public void setAmount(int amount) { - this.amount = amount; - } - - public int getChance() { - return chance; - } - - public void setChance(int chance) { - this.chance = chance; - } - - public int getType() { - return type; - } - - public void setType(int type) { - this.type = type; - } - - @Override - public String toString() { - return "CurrencyChance{" + - "id=" + id + - ", amount=" + amount + - ", chance=" + chance + - ", type=" + type + - '}'; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/model/GuildSettings.java b/src/main/java/de/kaktushose/levelbot/database/model/GuildSettings.java deleted file mode 100644 index 57d3999..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/model/GuildSettings.java +++ /dev/null @@ -1,145 +0,0 @@ -package de.kaktushose.levelbot.database.model; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -@Entity -@Table(name = "guild_settings") -public class GuildSettings { - - @Id - private Long guildId; - private String version; - private String botToken; - private String botPrefix; - private long botChannelId; - private long messageCooldown; - private String youtubeApiKey; - private long eventChannelId; - private String eventEmote; - private int collectEventId; - private long statisticsMessageId; - private long logChannelId; - - public GuildSettings() { - } - - public GuildSettings(Long guildId, - String version, - String botToken, - String botPrefix, - long botChannelId, - long messageCooldown, - String youtubeApiKey, - long eventChannelId, - String eventEmote, - int collectEventId, - long statisticsMessageId, - long logChannelId) { - this.guildId = guildId; - this.version = version; - this.botToken = botToken; - this.botPrefix = botPrefix; - this.botChannelId = botChannelId; - this.messageCooldown = messageCooldown; - this.youtubeApiKey = youtubeApiKey; - this.eventChannelId = eventChannelId; - this.eventEmote = eventEmote; - this.collectEventId = collectEventId; - this.statisticsMessageId = statisticsMessageId; - this.logChannelId = logChannelId; - } - - public Long getGuildId() { - return guildId; - } - - public void setGuildId(Long guildId) { - this.guildId = guildId; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getBotToken() { - return botToken; - } - - public void setBotToken(String botToken) { - this.botToken = botToken; - } - - public String getBotPrefix() { - return botPrefix; - } - - public void setBotPrefix(String botPrefix) { - this.botPrefix = botPrefix; - } - - public long getBotChannelId() { - return botChannelId; - } - - public long getLogChannelId() { - return logChannelId; - } - - public void setBotChannelId(long botChannelId) { - this.botChannelId = botChannelId; - } - - public long getMessageCooldown() { - return messageCooldown; - } - - public void setMessageCooldown(long messageCooldown) { - this.messageCooldown = messageCooldown; - } - - public String getYoutubeApiKey() { - return youtubeApiKey; - } - - public void setYoutubeApiKey(String youtubeApiKey) { - this.youtubeApiKey = youtubeApiKey; - } - - public long getEventChannelId() { - return eventChannelId; - } - - public void setEventChannelId(long eventChannelId) { - this.eventChannelId = eventChannelId; - } - - public String getEventEmote() { - return eventEmote; - } - - public void setEventEmote(String eventEmote) { - this.eventEmote = eventEmote; - } - - public int getCollectEventId() { - return collectEventId; - } - - public void setCollectEventId(int collectEventId) { - this.collectEventId = collectEventId; - } - - public long getStatisticsMessageId() { - return statisticsMessageId; - } - - public void setStatisticsMessageId(long statisticsMessageId) { - this.statisticsMessageId = statisticsMessageId; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/model/NitroBooster.java b/src/main/java/de/kaktushose/levelbot/database/model/NitroBooster.java deleted file mode 100644 index 6dc545b..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/model/NitroBooster.java +++ /dev/null @@ -1,49 +0,0 @@ -package de.kaktushose.levelbot.database.model; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -@Entity -@Table(name = "nitro_boosters") -public class NitroBooster { - - @Id - private Long userId; - private long boostStart; - private boolean active; - - public NitroBooster() { - - } - - public NitroBooster(Long userId, long boostStart, boolean active) { - this.userId = userId; - this.boostStart = boostStart; - this.active = active; - } - - public Long getUserId() { - return userId; - } - - public void setUserId(Long userId) { - this.userId = userId; - } - - public long getBoostStart() { - return boostStart; - } - - public void setBoostStart(long boostStart) { - this.boostStart = boostStart; - } - - public boolean isActive() { - return active; - } - - public void setActive(boolean active) { - this.active = active; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/model/Rank.java b/src/main/java/de/kaktushose/levelbot/database/model/Rank.java deleted file mode 100644 index c03d09c..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/model/Rank.java +++ /dev/null @@ -1,86 +0,0 @@ -package de.kaktushose.levelbot.database.model; - -import javax.persistence.*; -import java.util.List; - -@Entity -@Table(name = "ranks") -public class Rank { - - @Id - private Integer rankId; - private long roleId; - private int bound; - private String color; - private String name; - @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) - @JoinColumn(name = "rankId", referencedColumnName = "rankId") - private List rewards; - - public Rank() { - } - - public Rank(Integer rankId, long roleId, int bound, String color, String name) { - this.rankId = rankId; - this.roleId = roleId; - this.bound = bound; - this.color = color; - this.name = name; - } - - public Integer getRankId() { - return rankId; - } - - public void setRankId(Integer rankId) { - this.rankId = rankId; - } - - public long getRoleId() { - return roleId; - } - - public void setRoleId(long roleId) { - this.roleId = roleId; - } - - public int getBound() { - return bound; - } - - public void setBound(int bound) { - this.bound = bound; - } - - public String getColor() { - return color; - } - - public void setColor(String color) { - this.color = color; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getRankRewards() { - return rewards; - } - - public void setRankRewards(List rewards) { - this.rewards = rewards; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Rank rank = (Rank) o; - return rankId.equals(rank.getRankId()); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/model/Reward.java b/src/main/java/de/kaktushose/levelbot/database/model/Reward.java deleted file mode 100644 index c9bec3b..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/model/Reward.java +++ /dev/null @@ -1,80 +0,0 @@ -package de.kaktushose.levelbot.database.model; - -import de.kaktushose.levelbot.shop.data.items.Item; - -import javax.persistence.*; - -@Entity -@Table(name = "rewards") -public class Reward { - - @Id - private Integer rewardId; - private int coins; - private int xp; - private int diamonds; - @ManyToOne(cascade = CascadeType.ALL) - @JoinColumn(name = "itemId", referencedColumnName = "itemId") - private Item item; - private String message; - - public Reward() { - } - - public Reward(Integer rewardId, int coins, int xp, int diamonds, Item item, String message) { - this.rewardId = rewardId; - this.coins = coins; - this.xp = xp; - this.diamonds = diamonds; - this.item = item; - this.message = message; - } - - public Integer getRewardId() { - return rewardId; - } - - public void setRewardId(Integer rewardId) { - this.rewardId = rewardId; - } - - public int getCoins() { - return coins; - } - - public void setCoins(int coins) { - this.coins = coins; - } - - public int getXp() { - return xp; - } - - public void setXp(int xp) { - this.xp = xp; - } - - public int getDiamonds() { - return diamonds; - } - - public void setDiamonds(int diamonds) { - this.diamonds = diamonds; - } - - public Item getItem() { - return item; - } - - public void setItem(Item item) { - this.item = item; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/repositories/ChancesRepository.java b/src/main/java/de/kaktushose/levelbot/database/repositories/ChancesRepository.java deleted file mode 100644 index ec3beb2..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/repositories/ChancesRepository.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.kaktushose.levelbot.database.repositories; - -import de.kaktushose.levelbot.database.model.CurrencyChance; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; - -import java.util.List; - -/* -defined in table currency_chances -type 0 = xp -type 1 = coins -type 2 = diamonds - */ -public interface ChancesRepository extends CrudRepository { - - @Query(value = "SELECT * FROM currency_chances WHERE type = 0", nativeQuery = true) - List getXpChances(); - - @Query(value = "SELECT * FROM currency_chances WHERE type = 1", nativeQuery = true) - List getCoinChances(); - - @Query(value = "SELECT * FROM currency_chances WHERE type = 2", nativeQuery = true) - List getDiamondChances(); - -} diff --git a/src/main/java/de/kaktushose/levelbot/database/repositories/CollectEventRepository.java b/src/main/java/de/kaktushose/levelbot/database/repositories/CollectEventRepository.java deleted file mode 100644 index b75a4ac..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/repositories/CollectEventRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.kaktushose.levelbot.database.repositories; - -import de.kaktushose.levelbot.database.model.CollectEvent; -import org.springframework.data.repository.CrudRepository; - -public interface CollectEventRepository extends CrudRepository { -} diff --git a/src/main/java/de/kaktushose/levelbot/database/repositories/ContestRepository.java b/src/main/java/de/kaktushose/levelbot/database/repositories/ContestRepository.java deleted file mode 100644 index 7218a6b..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/repositories/ContestRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package de.kaktushose.levelbot.database.repositories; - -import de.kaktushose.levelbot.database.model.ContestEntry; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; - -import java.util.List; - -public interface ContestRepository extends CrudRepository { - - @Query(value = "SELECT * FROM contest_entries order by count desc", nativeQuery = true) - List getContestResult(); - - boolean existsByUserId(long userId); - -} diff --git a/src/main/java/de/kaktushose/levelbot/database/repositories/NitroBoosterRepository.java b/src/main/java/de/kaktushose/levelbot/database/repositories/NitroBoosterRepository.java deleted file mode 100644 index 3e22585..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/repositories/NitroBoosterRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.kaktushose.levelbot.database.repositories; - -import de.kaktushose.levelbot.database.model.NitroBooster; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; - -import java.util.List; - -public interface NitroBoosterRepository extends CrudRepository { - - @Query(value = "SELECT * FROM nitro_boosters where active = true", nativeQuery = true) - List getActiveNitroBoosters(); - -} diff --git a/src/main/java/de/kaktushose/levelbot/database/repositories/RankRepository.java b/src/main/java/de/kaktushose/levelbot/database/repositories/RankRepository.java deleted file mode 100644 index f8d7280..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/repositories/RankRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.kaktushose.levelbot.database.repositories; - -import de.kaktushose.levelbot.database.model.Rank; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; - -public interface RankRepository extends CrudRepository { - - @Query(value = "SELECT * FROM ranks where bound <= :xp order by bound desc limit 1", nativeQuery = true) - Rank getRankByXp(@Param("xp") long xp); - -} diff --git a/src/main/java/de/kaktushose/levelbot/database/repositories/RewardRepository.java b/src/main/java/de/kaktushose/levelbot/database/repositories/RewardRepository.java deleted file mode 100644 index fa1891f..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/repositories/RewardRepository.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.kaktushose.levelbot.database.repositories; - -import de.kaktushose.levelbot.database.model.Reward; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; - -import java.util.List; - -public interface RewardRepository extends CrudRepository { - - @Query(value = "SELECT * FROM rewards WHERE reward_id = 12", nativeQuery = true) - Reward getMonthlyNitroBoosterReward(); - - @Query(value = "SELECT * FROM rewards WHERE reward_id = 11", nativeQuery = true) - Reward getOneTimeNitroBoosterReward(); - - @Query(value = "SELECT * FROM rewards WHERE reward_id > 15", nativeQuery = true) - List getDailyRewards(); - -} diff --git a/src/main/java/de/kaktushose/levelbot/database/repositories/SettingsRepository.java b/src/main/java/de/kaktushose/levelbot/database/repositories/SettingsRepository.java deleted file mode 100644 index 4ace01b..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/repositories/SettingsRepository.java +++ /dev/null @@ -1,39 +0,0 @@ -package de.kaktushose.levelbot.database.repositories; - -import de.kaktushose.levelbot.database.model.GuildSettings; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; - -import javax.transaction.Transactional; -import java.util.List; -import java.util.Optional; - -public interface SettingsRepository extends CrudRepository { - - @Query(value = "SELECT * FROM guild_settings WHERE guild_id = :guildId", nativeQuery = true) - Optional getGuildSettings(@Param("guildId") long guildId); - - @Query(value = "SELECT * FROM ignored_channels", nativeQuery = true) - List getIgnoredChannels(); - - @Query(value = "SELECT * FROM rewarded_users", nativeQuery = true) - List getRewardedUsers(); - - @Modifying - @Transactional - @Query(value = "INSERT INTO ignored_channels VALUES (:channelId)", nativeQuery = true) - void addIgnoredChannel(@Param("channelId") long channelId); - - @Modifying - @Transactional - @Query(value = "INSERT INTO rewarded_users VALUES (:userId)", nativeQuery = true) - void addRewardedUser(@Param("userId") long userId); - - @Modifying - @Transactional - @Query(value = "DELETE FROM ignored_channels where channel_id = :channelId", nativeQuery = true) - void removeIgnoredChannel(@Param("channelId") long channelId); - -} diff --git a/src/main/java/de/kaktushose/levelbot/database/repositories/UserRepository.java b/src/main/java/de/kaktushose/levelbot/database/repositories/UserRepository.java deleted file mode 100644 index 897b236..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/repositories/UserRepository.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.kaktushose.levelbot.database.repositories; - -import de.kaktushose.levelbot.database.model.BotUser; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; - -import java.util.List; - -public interface UserRepository extends CrudRepository { - - @Query(value = "SELECT user_id FROM users", nativeQuery = true) - List findAllIds(); - - @Query(value = "SELECT user_id FROM users where permission_level <= 0", nativeQuery = true) - List findMutedUsers(); - - @Query(value = "SELECT user_id FROM users where permission_level >= :level", nativeQuery = true) - List findByPermissionLevel(@Param("level") int permissionLevel); - - @Query(value = "SELECT * FROM users where daily = true", nativeQuery = true) - List getAllWithDaily(); - - @Query(value = "SELECT * FROM users order by xp desc", nativeQuery = true) - List getXpLeaderboard(); - - @Query(value = "SELECT * FROM users order by coins desc", nativeQuery = true) - List getCoinsLeaderboard(); - - @Query(value = "SELECT * FROM users order by diamonds desc", nativeQuery = true) - List getDiamondsLeaderboard(); - -} diff --git a/src/main/java/de/kaktushose/levelbot/database/services/BoosterService.java b/src/main/java/de/kaktushose/levelbot/database/services/BoosterService.java deleted file mode 100644 index e34cdbc..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/services/BoosterService.java +++ /dev/null @@ -1,155 +0,0 @@ -package de.kaktushose.levelbot.database.services; - -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.model.NitroBooster; -import de.kaktushose.levelbot.database.model.Reward; -import de.kaktushose.levelbot.database.repositories.NitroBoosterRepository; -import de.kaktushose.levelbot.shop.data.items.ItemCategory; -import de.kaktushose.levelbot.shop.data.items.ItemVariant; -import de.kaktushose.levelbot.shop.data.ShopService; -import de.kaktushose.levelbot.spring.ApplicationContextHolder; -import net.dv8tion.jda.api.entities.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class BoosterService { - - private static final Logger log = LoggerFactory.getLogger(BoosterService.class); - private final NitroBoosterRepository nitroBoosterRepository; - private final Levelbot levelbot; - private final ShopService shopService; - private final UserService userService; - private final SettingsService settingsService; - - public BoosterService(Levelbot levelbot) { - ApplicationContext context = ApplicationContextHolder.getContext(); - nitroBoosterRepository = context.getBean(NitroBoosterRepository.class); - this.shopService = levelbot.getShopService(); - this.settingsService = levelbot.getSettingsService(); - userService = levelbot.getUserService(); - this.levelbot = levelbot; - } - - public void updateBoosterStatus(Guild guild, TextChannel botChannel, EmbedCache embedCache) { - // iterate through all actual nitro boosters - log.debug("updateBoosterStatus started:"); - guild.findMembers(member -> member.getTimeBoosted() != null).onSuccess(boosterList -> { - log.debug("Queried members: {}", Arrays.toString(boosterList.toArray())); - boosterList.forEach(member -> { - long userId = member.getIdLong(); - log.debug("Checking on {}...", member); - // user is already registered as an active booster in db, skip this one - if (isActiveNitroBooster(userId)) { - log.debug("Member is active booster!"); - return; - } - - // user is in db, must be a resumed booster - if (isNitroBooster(userId)) { - changeNitroBoosterStatus(userId, true); - addMonthlyReward(userId); - shopService.addItem(userId, ItemCategory.PREMIUM, ItemVariant.UNLIMITED); - botChannel.sendMessage(member.getAsMention()) - .and(botChannel.sendMessageEmbeds(embedCache.getEmbed("nitroBoostResume") - .injectValue("user", member.getEffectiveName()) - .toMessageEmbed() - )).queue(); - log.debug("Member is inactive booster!"); - return; - } - // else, user is not in db, must be a first time booster - createNewNitroBooster(userId); - addOneTimeReward(userId); - shopService.addItem(userId, ItemCategory.PREMIUM, ItemVariant.UNLIMITED); - botChannel.sendMessage(member.getAsMention()) - .and(botChannel.sendMessageEmbeds(embedCache.getEmbed("nitroBoostStart") - .injectValue("user", member.getEffectiveName()) - .toMessageEmbed() - )).queue(); - log.debug("Member is new booster!"); - }); - - log.debug("Comparing with active boosters..."); - // iterate through all active boosters and compare with actual boosters - getActiveNitroBoosters().forEach(nitroBooster -> { - Long userId = nitroBooster.getUserId(); - Member member = guild.retrieveMemberById(userId).complete(); - log.debug("Checking on {}...", member); - if (boosterList.stream().map(ISnowflake::getIdLong).noneMatch(userId::equals)) { - log.debug("Member stopped boosting!"); - changeNitroBoosterStatus(userId, false); - shopService.removeItem(userId, ItemCategory.PREMIUM, ItemVariant.UNLIMITED); - botChannel.sendMessage(member.getAsMention()) - .and(botChannel.sendMessageEmbeds(embedCache.getEmbed("nitroBoostStop") - .injectValue("user", member.getAsMention()) - .toMessageEmbed() - )).queue(); - } - log.debug("Member is still boosting!"); - }); - }).onError(throwable -> { - log.debug("Querying members failed!", throwable); - throw new IllegalStateException("Unable to query boosters!", throwable); - }); - log.debug("updateBoosterStatus finished!"); - } - - public List getAllNitroBoosters() { - List result = new ArrayList<>(); - nitroBoosterRepository.findAll().forEach(result::add); - return result; - } - - public List getActiveNitroBoosters() { - return nitroBoosterRepository.getActiveNitroBoosters(); - } - - public boolean isNitroBooster(long userId) { - return nitroBoosterRepository.findById(userId).isPresent(); - } - - public boolean isActiveNitroBooster(long userId) { - return getActiveNitroBoosters().stream().map(NitroBooster::getUserId).anyMatch(((Long) userId)::equals); - } - - public void createNewNitroBooster(long userId) { - nitroBoosterRepository.save(new NitroBooster(userId, System.currentTimeMillis(), true)); - } - - public void changeNitroBoosterStatus(long userId, boolean active) { - if (!isNitroBooster(userId)) { - return; - } - NitroBooster nitroBooster = nitroBoosterRepository.findById(userId).orElseThrow(); - nitroBooster.setActive(active); - nitroBoosterRepository.save(nitroBooster); - } - - public String addMonthlyReward(long userId) { - Reward reward = settingsService.getMonthlyNitroBoosterReward(); - userService.addCoins(userId, reward.getCoins()); - userService.addXp(userId, reward.getXp()); - userService.addDiamonds(userId, reward.getDiamonds()); - if (reward.getItem() != null) { - shopService.addItem(userId, reward.getItem().getItemId()); - } - return reward.getMessage(); - } - - public String addOneTimeReward(long userId) { - Reward reward = settingsService.getOneTimeNitroBoosterReward(); - userService.addCoins(userId, reward.getCoins()); - userService.addXp(userId, reward.getXp()); - userService.addDiamonds(userId, reward.getDiamonds()); - if (reward.getItem() != null) { - shopService.addItem(userId, reward.getItem().getItemId()); - } - return reward.getMessage(); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/services/EventService.java b/src/main/java/de/kaktushose/levelbot/database/services/EventService.java deleted file mode 100644 index 539c125..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/services/EventService.java +++ /dev/null @@ -1,181 +0,0 @@ -package de.kaktushose.levelbot.database.services; - -import de.kaktushose.levelbot.database.model.CollectEvent; -import de.kaktushose.levelbot.database.model.ContestEntry; -import de.kaktushose.levelbot.database.model.CurrencyChance; -import de.kaktushose.levelbot.database.repositories.ChancesRepository; -import de.kaktushose.levelbot.database.repositories.CollectEventRepository; -import de.kaktushose.levelbot.database.repositories.ContestRepository; -import de.kaktushose.levelbot.spring.ApplicationContextHolder; -import de.kaktushose.levelbot.util.Pagination; -import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.Guild; -import org.springframework.context.ApplicationContext; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class EventService { - - private final ChancesRepository chancesRepository; - private final ContestRepository contestRepository; - private final CollectEventRepository collectEventRepository; - private final SettingsService settingsService; - private final UserService userService; - - public EventService(SettingsService settingsService, UserService userService) { - ApplicationContext context = ApplicationContextHolder.getContext(); - this.chancesRepository = context.getBean(ChancesRepository.class); - this.contestRepository = context.getBean(ContestRepository.class); - this.collectEventRepository = context.getBean(CollectEventRepository.class); - this.settingsService = settingsService; - this.userService = userService; - } - - public String startBalanceEvent(int eventId, long guildId) { - switch (eventId) { - case 0: - for (CurrencyChance chance : chancesRepository.getXpChances()) { - chance.setAmount(chance.getAmount() * 2); - chancesRepository.save(chance); - } - return "XP-Booster x2"; - case 1: - for (CurrencyChance chance : chancesRepository.getCoinChances()) { - chance.setAmount(chance.getAmount() * 2); - chancesRepository.save(chance); - } - return "Münzen-Rush x2"; - case 2: - for (CurrencyChance chance : chancesRepository.getDiamondChances()) { - if (chance.getAmount() == 0) { - chance.setChance(80); - chancesRepository.save(chance); - } - } - return "Diamanten-Regen x2"; - case 3: - settingsService.setMessageCooldown(guildId, TimeUnit.MINUTES.toMillis(5)); - return "Cooldown -67%"; - default: - return "N/A"; - } - } - - public String stopBalanceEvent(int eventId, long guildId) { - switch (eventId) { - case 0: - for (CurrencyChance chance : chancesRepository.getXpChances()) { - chance.setAmount(chance.getAmount() / 2); - chancesRepository.save(chance); - } - return "XP-Booster x2"; - case 1: - for (CurrencyChance chance : chancesRepository.getCoinChances()) { - chance.setAmount(chance.getAmount() / 2); - chancesRepository.save(chance); - } - return "Münzen-Rush x2"; - case 2: - for (CurrencyChance chance : chancesRepository.getDiamondChances()) { - if (chance.getAmount() == 0) { - chance.setChance(90); - chancesRepository.save(chance); - } - } - return "Diamanten-Regen x2"; - case 3: - settingsService.setMessageCooldown(guildId, TimeUnit.MINUTES.toMillis(15)); - return "Cooldown -67%"; - default: - return "N/A"; - } - } - - public void startContestEvent(long guildId, long channelId, String emote) { - settingsService.setEventChannelId(guildId, channelId); - settingsService.setEventEmote(guildId, emote.replaceAll(":", "")); - contestRepository.deleteAll(); - } - - public void stopContestEvent(long guildId) { - settingsService.setEventChannelId(guildId, 0); - settingsService.setEventEmote(guildId, ""); - } - - public boolean voteCountExists(long userId) { - return contestRepository.existsByUserId(userId); - } - - public boolean isSelfUser(long messageId, long userId) { - return userId == contestRepository.findById(messageId).orElseThrow().getUserId(); - } - - public void increaseVoteCount(long messageId) { - ContestEntry entry = contestRepository.findById(messageId).orElseThrow(); - entry.setCount(entry.getCount(Pagination.CurrencyType.CONTEST) + 1); - contestRepository.save(entry); - } - - public void decreaseVoteCount(long messageId) { - ContestEntry entry = contestRepository.findById(messageId).orElseThrow(); - entry.setCount(entry.getCount(Pagination.CurrencyType.CONTEST) - 1); - contestRepository.save(entry); - } - - public void createVoteCount(long messageId, long userId) { - contestRepository.save(new ContestEntry(messageId, userId, 0)); - } - - public void deleteVoteCount(long messageId) { - if (!contestRepository.existsById(messageId)) { - return; - } - contestRepository.deleteById(messageId); - } - - public Pagination getVoteResult(int pageSize, JDA jda) { - return new Pagination(pageSize, contestRepository.getContestResult(), jda, Pagination.CurrencyType.CONTEST); - } - - public boolean contestEventExistsById(int id) { - return collectEventRepository.existsById(id); - } - - public boolean collectEventExistsById(int id) { - return collectEventRepository.existsById(id); - } - - public CollectEvent getCollectEvent(int id) { - return collectEventRepository.findById(id).orElseThrow(); - } - - public List getAllCollectEvents() { - List result = new ArrayList<>(); - collectEventRepository.findAll().forEach(result::add); - return result; - } - - public String startCollectEvent(int id, Guild guild) { - settingsService.setActiveCollectEvent(guild.getIdLong(), id); - userService.getAllUsers().forEach(botUser -> userService.resetEventPoints(botUser.getUserId())); - return getCollectEvent(id).getName(); - } - - public boolean stopCollectEvent(long guildId) { - if (!isCollectEventActive(guildId)) { - return false; - } - settingsService.setActiveCollectEvent(guildId, -1); - return true; - } - - public CollectEvent getActiveCollectEvent(long guildId) { - return getCollectEvent(settingsService.getActiveCollectEventId(guildId)); - } - - public boolean isCollectEventActive(long guildId) { - return settingsService.getActiveCollectEventId(guildId) > -1; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/services/LevelService.java b/src/main/java/de/kaktushose/levelbot/database/services/LevelService.java deleted file mode 100644 index 074e83d..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/services/LevelService.java +++ /dev/null @@ -1,215 +0,0 @@ -package de.kaktushose.levelbot.database.services; - -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.model.CurrencyChance; -import de.kaktushose.levelbot.database.model.Rank; -import de.kaktushose.levelbot.database.model.Reward; -import de.kaktushose.levelbot.database.repositories.ChancesRepository; -import de.kaktushose.levelbot.database.repositories.RankRepository; -import de.kaktushose.levelbot.database.repositories.UserRepository; -import de.kaktushose.levelbot.shop.data.items.ItemCategory; -import de.kaktushose.levelbot.shop.data.ShopService; -import de.kaktushose.levelbot.shop.data.items.Item; -import de.kaktushose.levelbot.shop.data.items.ItemRepository; -import de.kaktushose.levelbot.spring.ApplicationContextHolder; -import de.kaktushose.levelbot.util.Pagination; -import net.dv8tion.jda.api.JDA; -import org.springframework.context.ApplicationContext; - -import java.util.List; -import java.util.Optional; -import java.util.concurrent.ThreadLocalRandom; - -public class LevelService { - - private final UserRepository userRepository; - private final RankRepository rankRepository; - private final ItemRepository itemRepository; - private final ChancesRepository chancesRepository; - private final UserService userService; - private final SettingsService settingsService; - private final ShopService shopService; - private final Levelbot levelbot; - - public LevelService(Levelbot levelbot) { - ApplicationContext context = ApplicationContextHolder.getContext(); - userRepository = context.getBean(UserRepository.class); - rankRepository = context.getBean(RankRepository.class); - itemRepository = context.getBean(ItemRepository.class); - chancesRepository = context.getBean(ChancesRepository.class); - this.userService = levelbot.getUserService(); - this.settingsService = levelbot.getSettingsService(); - shopService = levelbot.getShopService(); - this.levelbot = levelbot; - } - - public Rank getRank(int rankId) { - return rankRepository.findById(rankId).orElseThrow(); - } - - public Rank getPreviousRank(long userId) { - BotUser botUser = userService.getUserById(userId); - if (botUser.getLevel() == 1) { - return getRank(1); - } - return getRank(botUser.getLevel() - 1); - } - - public Rank getCurrentRank(long userId) { - BotUser botUser = userService.getUserById(userId); - return getRank(botUser.getLevel()); - } - - public Rank getNextRank(long userId) { - BotUser botUser = userService.getUserById(userId); - if (botUser.getLevel() == 13) { - return getRank(13); - } - return getRank(botUser.getLevel() + 1); - } - - public List getItemsByCategoryId(int categoryId) { - return itemRepository.findByCategoryId(categoryId); - } - - public Item getItem(int itemId) { - return itemRepository.findById(itemId).orElseThrow(); - } - - public boolean setItemPrice(int itemId, int price) { - if (!itemRepository.existsById(itemId)) { - return false; - } - Item item = getItem(itemId); - item.setPrice(price); - itemRepository.save(item); - return true; - } - - public Pagination getXpLeaderboard(int pageSize, JDA jda) { - return new Pagination(pageSize, userRepository.getXpLeaderboard(), jda, Pagination.CurrencyType.XP); - } - - public Pagination getCoinsLeaderboard(int pageSize, JDA jda) { - return new Pagination(pageSize, userRepository.getCoinsLeaderboard(), jda, Pagination.CurrencyType.COINS); - } - - public Pagination getDiamondsLeaderboard(int pageSize, JDA jda) { - return new Pagination(pageSize, userRepository.getDiamondsLeaderboard(), jda, Pagination.CurrencyType.DIAMONDS); - } - - public boolean isValidMessage(long userId, long guildId, long channelId) { - BotUser botUser = userService.getUserById(userId); - if (settingsService.isIgnoredChannel(channelId)) { - return false; - } - if (botUser.getPermissionLevel() < 1) { - return false; - } - return System.currentTimeMillis() - botUser.getLastValidMessage() >= settingsService.getMessageCooldown(guildId); - } - - public long randomXp() { - List chances = chancesRepository.getXpChances(); - int random = ThreadLocalRandom.current().nextInt(1, 101); - for (CurrencyChance chance : chances) { - if (random <= chance.getChance()) { - return chance.getAmount(); - } - } - return 0; - } - - public long randomCoins() { - List chances = chancesRepository.getCoinChances(); - int random = ThreadLocalRandom.current().nextInt(1, 101); - for (CurrencyChance chance : chances) { - if (random <= chance.getChance()) { - return chance.getAmount(); - } - } - return 0; - } - - public long randomDiamonds() { - List chances = chancesRepository.getDiamondChances(); - int random = ThreadLocalRandom.current().nextInt(1, 101); - for (CurrencyChance chance : chances) { - if (random <= chance.getChance()) { - return chance.getAmount(); - } - } - return 0; - } - - public Optional onValidMessage(long userId) { - userService.updateLastValidMessage(userId); - userService.updateMessageCount(userId); - - long diamonds = randomDiamonds(); - long coins = randomCoins(); - if (shopService.hasItemOfCategory(userId, ItemCategory.COIN_BOOSTER)) { - coins += 2; - } - long xp = randomXp(); - if (shopService.hasItemOfCategory(userId, ItemCategory.XP_BOOSTER)) { - xp += 2; - } - - userService.addDiamonds(userId, diamonds); - userService.addCoins(userId, coins); - long newXp = userService.addXp(userId, xp); - - if (getCurrentRank(userId).getRankId() == 13) { - return Optional.empty(); - } - - if (newXp < getNextRank(userId).getBound()) { - return Optional.empty(); - } - - Rank rank = rankRepository.getRankByXp(newXp); - - return Optional.of(getRank(userService.setRank(userId, rank.getRankId()))); - } - - public Optional getDailyReward(long userId) { - BotUser botUser = userService.getUserById(userId); - - int rewardLevel; - if (System.currentTimeMillis() - botUser.getLastReward() >= 172800000L) { - rewardLevel = userService.resetRewardLevel(userId); - } else if (System.currentTimeMillis() - botUser.getLastReward() >= 86400000L) { - rewardLevel = userService.increaseRewardLevel(userId); - } else { - return Optional.empty(); - } - - Reward reward = settingsService.getReward(rewardLevel); - userService.addCoins(userId, reward.getCoins()); - userService.addXp(userId, reward.getXp()); - userService.addDiamonds(userId, reward.getDiamonds()); - userService.updateLastReward(userId); - if (reward.getItem() != null) { - shopService.addItem(userId, reward.getItem().getItemId()); - } - - return Optional.of(reward.getMessage()); - } - - public String applyRewards(long userId, int rankId) { - Rank rank = getRank(rankId); - StringBuilder rewardText = new StringBuilder(); - rank.getRankRewards().forEach(rankReward -> { - userService.addCoins(userId, rankReward.getCoins()); - userService.addDiamonds(userId, rankReward.getDiamonds()); - userService.addXp(userId, rankReward.getXp()); - if (rankReward.getItem() != null) { - shopService.addItem(userId, rankReward.getItem().getItemId()); - } - rewardText.append(rankReward.getMessage()).append("\n"); - }); - return rewardText.substring(0, rewardText.length() - 1); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/database/services/SettingsService.java b/src/main/java/de/kaktushose/levelbot/database/services/SettingsService.java deleted file mode 100644 index 66b298f..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/services/SettingsService.java +++ /dev/null @@ -1,133 +0,0 @@ -package de.kaktushose.levelbot.database.services; - -import de.kaktushose.levelbot.database.model.GuildSettings; -import de.kaktushose.levelbot.database.model.Reward; -import de.kaktushose.levelbot.database.repositories.RewardRepository; -import de.kaktushose.levelbot.database.repositories.SettingsRepository; -import de.kaktushose.levelbot.spring.ApplicationContextHolder; -import org.springframework.context.ApplicationContext; - -import java.util.List; - -public class SettingsService { - - private final SettingsRepository settingsRepository; - private final RewardRepository rewardRepository; - - public SettingsService() { - ApplicationContext context = ApplicationContextHolder.getContext(); - this.settingsRepository = context.getBean(SettingsRepository.class); - this.rewardRepository = context.getBean(RewardRepository.class); - } - - private GuildSettings getGuildSettings(long guildId) { - return settingsRepository.getGuildSettings(guildId).orElseThrow(); - } - - public long getBotChannelId(long guildId) { - return getGuildSettings(guildId).getBotChannelId(); - } - - public long getLogChannelId(long guildId) { - return getGuildSettings(guildId).getLogChannelId(); - } - - public String getBotPrefix(long guildId) { - return getGuildSettings(guildId).getBotPrefix(); - } - - public String getBotToken(long guildId) { - return getGuildSettings(guildId).getBotToken(); - } - - public String getVersion(long guildId) { - return getGuildSettings(guildId).getVersion(); - } - - public long getMessageCooldown(long guildId) { - return getGuildSettings(guildId).getMessageCooldown(); - } - - public void setMessageCooldown(long guildId, long cooldown) { - GuildSettings settings = getGuildSettings(guildId); - settings.setMessageCooldown(cooldown); - settingsRepository.save(settings); - } - - public String getYoutubeApiKey(long guildId) { - return getGuildSettings(guildId).getYoutubeApiKey(); - } - - public boolean isIgnoredChannel(long channelId) { - return settingsRepository.getIgnoredChannels().contains(channelId); - } - - public List getIgnoredChannels() { - return settingsRepository.getIgnoredChannels(); - } - - public void addIgnoredChannel(long channelId) { - settingsRepository.addIgnoredChannel(channelId); - } - - public void removeIgnoredChannel(long channelId) { - settingsRepository.removeIgnoredChannel(channelId); - } - - public Reward getReward(int rewardLevel) { - return rewardRepository.findById(15 + rewardLevel).orElseThrow(); - } - - public Reward getMonthlyNitroBoosterReward() { - return rewardRepository.findById(12).orElseThrow(); - } - - public Reward getOneTimeNitroBoosterReward() { - return rewardRepository.findById(11).orElseThrow(); - } - - public List getRewardedUsers() { - return settingsRepository.getRewardedUsers(); - } - - public void addRewardedUser(long userId) { - settingsRepository.addRewardedUser(userId); - } - - public void setEventChannelId(long guildId, long channelId) { - GuildSettings settings = getGuildSettings(guildId); - settings.setEventChannelId(channelId); - settingsRepository.save(settings); - } - - public long getEventChannelId(long guildId) { - GuildSettings settings = getGuildSettings(guildId); - return settings.getEventChannelId(); - } - - public void setEventEmote(long guildId, String emote) { - GuildSettings settings = getGuildSettings(guildId); - settings.setEventEmote(emote); - settingsRepository.save(settings); - } - - public String getEventEmote(long guildId) { - GuildSettings settings = getGuildSettings(guildId); - return settings.getEventEmote(); - } - - public void setActiveCollectEvent(long guildId, int id) { - GuildSettings settings = getGuildSettings(guildId); - settings.setCollectEventId(id); - settingsRepository.save(settings); - } - - public int getActiveCollectEventId(long guildId) { - return getGuildSettings(guildId).getCollectEventId(); - } - - public long getStatisticsMessageId(long guildId) { - return getGuildSettings(guildId).getStatisticsMessageId(); - } - -} diff --git a/src/main/java/de/kaktushose/levelbot/database/services/UserService.java b/src/main/java/de/kaktushose/levelbot/database/services/UserService.java deleted file mode 100644 index 88bfe38..0000000 --- a/src/main/java/de/kaktushose/levelbot/database/services/UserService.java +++ /dev/null @@ -1,198 +0,0 @@ -package de.kaktushose.levelbot.database.services; - -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.repositories.UserRepository; -import de.kaktushose.levelbot.shop.data.items.FrozenItem; -import de.kaktushose.levelbot.shop.data.items.FrozenItemRepository; -import de.kaktushose.levelbot.shop.data.items.Item; -import de.kaktushose.levelbot.shop.data.items.ItemRepository; -import de.kaktushose.levelbot.shop.data.transactions.Transaction; -import de.kaktushose.levelbot.shop.data.transactions.TransactionRepository; -import de.kaktushose.levelbot.spring.ApplicationContextHolder; -import org.springframework.context.ApplicationContext; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -public class UserService { - - private final UserRepository userRepository; - private final TransactionRepository transactionRepository; - private final ItemRepository itemRepository; - private final FrozenItemRepository frozenItemRepository; - - public UserService() { - ApplicationContext context = ApplicationContextHolder.getContext(); - userRepository = context.getBean(UserRepository.class); - transactionRepository = context.getBean(TransactionRepository.class); - itemRepository = context.getBean(ItemRepository.class); - frozenItemRepository = context.getBean(FrozenItemRepository.class); - } - - public List getAllUsers() { - List result = new ArrayList<>(); - userRepository.findAll().forEach(result::add); - return result; - } - - public List getAllUserIds() { - return userRepository.findAllIds(); - } - - public BotUser getUserById(long userId) { - return userRepository.findById(userId).orElseThrow(); - } - - public List getMutedUsers() { - return userRepository.findMutedUsers(); - } - - public List getUsersByPermission(int permissionLevel) { - return userRepository.findByPermissionLevel(permissionLevel); - } - - public List getUsersByDailyEnabled() { - return userRepository.getAllWithDaily(); - } - - public BotUser createUser(long userId) { - return userRepository.save(new BotUser(userId)); - } - - public BotUser createUserIfAbsent(long userId) { - Optional optional = userRepository.findById(userId); - if (optional.isEmpty()) { - return createUser(userId); - } - return optional.get(); - } - - public void deleteUser(long id) { - userRepository.deleteById(id); - } - - public void exchangeDiamonds(long userId, long diamonds) { - BotUser botUser = getUserById(userId); - botUser.setDiamonds(botUser.getDiamonds() - diamonds); - botUser.setCoins(botUser.getCoins() + diamonds * 20); - userRepository.save(botUser); - } - - public boolean switchDaily(long userId) { - BotUser botUser = getUserById(userId); - botUser.setDailyUpdate(!botUser.isDailyUpdate()); - return userRepository.save(botUser).isDailyUpdate(); - } - - public void setPermission(long userId, int permissionLevel) { - BotUser botUser = getUserById(userId); - botUser.setPermissionLevel(permissionLevel); - userRepository.save(botUser); - } - - public long addCoins(long userId, long amount) { - BotUser botUser = getUserById(userId); - botUser.setCoins(botUser.getCoins() + amount); - userRepository.save(botUser); - return botUser.getCoins(); - } - - public long addXp(long userId, long amount) { - BotUser botUser = getUserById(userId); - botUser.setXp(botUser.getXp() + amount); - userRepository.save(botUser); - return botUser.getXp(); - } - - public long addDiamonds(long userId, long amount) { - BotUser botUser = getUserById(userId); - botUser.setDiamonds(botUser.getDiamonds() + amount); - userRepository.save(botUser); - return botUser.getDiamonds(); - } - - public void setCoins(long userId, int amount) { - BotUser botUser = getUserById(userId); - botUser.setCoins(amount); - userRepository.save(botUser); - } - - public void setXp(long userId, int amount) { - BotUser botUser = getUserById(userId); - botUser.setXp(amount); - userRepository.save(botUser); - } - - public void setDiamonds(long userId, int amount) { - BotUser botUser = getUserById(userId); - botUser.setDiamonds(amount); - userRepository.save(botUser); - } - - public void updateLastValidMessage(long userId) { - BotUser botUser = getUserById(userId); - botUser.setLastValidMessage(System.currentTimeMillis()); - userRepository.save(botUser); - } - - public void updateMessageCount(long userId) { - BotUser botUser = getUserById(userId); - botUser.setMessageCount(botUser.getMessageCount() + 1); - userRepository.save(botUser); - } - - public void updateUserStatistics(long userId) { - BotUser botUser = getUserById(userId); - botUser.setStartCoins(botUser.getCoins()); - botUser.setStartXp(botUser.getXp()); - botUser.setStartDiamonds(botUser.getDiamonds()); - userRepository.save(botUser); - } - - public int setRank(long userId, int rank) { - BotUser botUser = getUserById(userId); - if (botUser.getLevel() == 13) { - return 13; - } - botUser.setLevel(rank); - userRepository.save(botUser); - return botUser.getLevel(); - } - - public int increaseRewardLevel(long userId) { - BotUser botUser = getUserById(userId); - int newLevel = botUser.getRewardLevel() + 1; - newLevel = newLevel > 7 ? 1 : newLevel; - botUser.setRewardLevel(newLevel); - userRepository.save(botUser); - return newLevel; - } - - public int resetRewardLevel(long userId) { - BotUser botUser = getUserById(userId); - botUser.setRewardLevel(1); - userRepository.save(botUser); - return 1; - } - - public void updateLastReward(long userId) { - BotUser botUser = getUserById(userId); - botUser.setLastReward(System.currentTimeMillis()); - userRepository.save(botUser); - } - - public void resetEventPoints(long userId) { - BotUser botUser = getUserById(userId); - botUser.setEventPoints(0); - userRepository.save(botUser); - } - - public long increaseEventPoints(long userId) { - BotUser botUser = getUserById(userId); - botUser.setEventPoints(botUser.getEventPoints() + 1); - return userRepository.save(botUser).getEventPoints(); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/listener/ContestEventListener.java b/src/main/java/de/kaktushose/levelbot/listener/ContestEventListener.java deleted file mode 100644 index a85a0c4..0000000 --- a/src/main/java/de/kaktushose/levelbot/listener/ContestEventListener.java +++ /dev/null @@ -1,108 +0,0 @@ -package de.kaktushose.levelbot.listener; - -import de.kaktushose.levelbot.database.services.EventService; -import de.kaktushose.levelbot.database.services.SettingsService; -import net.dv8tion.jda.api.events.message.guild.GenericGuildMessageEvent; -import net.dv8tion.jda.api.events.message.guild.GuildMessageDeleteEvent; -import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; -import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent; -import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveAllEvent; -import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEmoteEvent; -import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -public class ContestEventListener extends ListenerAdapter { - - private final SettingsService settingsService; - private final EventService eventService; - - public ContestEventListener(SettingsService settingsService, EventService eventService) { - this.settingsService = settingsService; - this.eventService = eventService; - } - - @Override - public void onGuildMessageReceived(@NotNull GuildMessageReceivedEvent event) { - long guildId = event.getGuild().getIdLong(); - if (event.getAuthor().isBot()) { - return; - } - - if (event.getChannel().getIdLong() != settingsService.getEventChannelId(guildId)) { - return; - } - - if (eventService.voteCountExists(event.getAuthor().getIdLong())) { - event.getMessage().delete().queue(); - return; - } - - event.getMessage().addReaction(settingsService.getEventEmote(guildId)).queue(); - eventService.createVoteCount(event.getMessage().getIdLong(), event.getAuthor().getIdLong()); - } - - @Override - public void onGuildMessageReactionAdd(@NotNull GuildMessageReactionAddEvent event) { - long guildId = event.getGuild().getIdLong(); - if (event.getUser().isBot()) { - return; - } - if (event.getChannel().getIdLong() != settingsService.getEventChannelId(guildId)) { - return; - } - if (eventService.isSelfUser(event.getMessageIdLong(), event.getUserIdLong())) { - event.getReaction().removeReaction(event.getUser()).queue(); - return; - } - if (event.getReactionEmote().getName().equals(settingsService.getEventEmote(guildId))) { - eventService.increaseVoteCount(event.getMessageIdLong()); - } - } - - // single reaction removed - @Override - public void onGuildMessageReactionRemove(@NotNull GuildMessageReactionRemoveEvent event) { - long guildId = event.getGuild().getIdLong(); - if (event.getChannel().getIdLong() != settingsService.getEventChannelId(guildId)) { - return; - } - if (event.getReactionEmote().getName().equals(settingsService.getEventEmote(guildId))) { - eventService.decreaseVoteCount(event.getMessageIdLong()); - } - } - - // all reactions of a emote removed - @Override - public void onGuildMessageReactionRemoveEmote(@NotNull GuildMessageReactionRemoveEmoteEvent event) { - long guildId = event.getGuild().getIdLong(); - if (event.getChannel().getIdLong() != settingsService.getEventChannelId(guildId)) { - return; - } - if (event.getReactionEmote().getName().equals(settingsService.getEventEmote(guildId))) { - removeEntry(event); - } - } - - // all reactions removed - @Override - public void onGuildMessageReactionRemoveAll(@NotNull GuildMessageReactionRemoveAllEvent event) { - if (event.getChannel().getIdLong() != settingsService.getEventChannelId(event.getGuild().getIdLong())) { - return; - } - removeEntry(event); - } - - // message deleted - @Override - public void onGuildMessageDelete(@NotNull GuildMessageDeleteEvent event) { - if (event.getChannel().getIdLong() != settingsService.getEventChannelId(event.getGuild().getIdLong())) { - return; - } - removeEntry(event); - } - - private void removeEntry(GenericGuildMessageEvent event) { - eventService.deleteVoteCount(event.getMessageIdLong()); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/listener/DailyRewardListener.java b/src/main/java/de/kaktushose/levelbot/listener/DailyRewardListener.java deleted file mode 100644 index cd1968f..0000000 --- a/src/main/java/de/kaktushose/levelbot/listener/DailyRewardListener.java +++ /dev/null @@ -1,65 +0,0 @@ -package de.kaktushose.levelbot.listener; - -import de.kaktushose.levelbot.bot.Levelbot; -import net.dv8tion.jda.api.MessageBuilder; -import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -public class DailyRewardListener extends ListenerAdapter { - - // this should find its way into the database one day as well - public static final long DAILY_REWARD_MESSAGE_ID = 851454384893067274L; - private final Levelbot levelbot; - - public DailyRewardListener(Levelbot levelbot) { - this.levelbot = levelbot; - } - - @Override - public void onGuildMessageReactionAdd(@NotNull GuildMessageReactionAddEvent event) { - // bots should just be ignored - if (event.getUser().isBot()) { - return; - } - // must be right message - if (event.getMessageIdLong() != DAILY_REWARD_MESSAGE_ID) { - return; - } - - if (levelbot.getUserService().getMutedUsers().contains(event.getUser().getIdLong())) { - return; - } - - // must be :gift: emote - if (!event.getReactionEmote().getName().equals("\uD83C\uDF81")) { - return; - } - User user = event.getUser(); - Optional reward = levelbot.getLevelService().getDailyReward(user.getIdLong()); - MessageBuilder builder = new MessageBuilder().append(user.getAsMention()); - if (reward.isPresent()) { - builder.setEmbeds(levelbot.getEmbedCache() - .getEmbed("dailyReward") - .injectValue("user", user.getName()) - .injectValue("reward", reward.get()) - .toMessageEmbed() - ); - event.getChannel().sendMessage(builder.build()).queue(message -> message.delete().queueAfter(30, TimeUnit.SECONDS)); - } else { - long timePassed = System.currentTimeMillis() - levelbot.getUserService().getUserById(user.getIdLong()).getLastReward(); - long millis = TimeUnit.HOURS.toMillis(24) - timePassed; - long hours = TimeUnit.MILLISECONDS.toHours(millis) - TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(millis)); - builder.setEmbeds(levelbot.getEmbedCache() - .getEmbed("rewardAlreadyClaimed") - .injectValue("hours", hours) - .toMessageEmbed() - ); - event.getChannel().sendMessage(builder.build()).queue(message -> message.delete().queueAfter(30, TimeUnit.SECONDS)); - } - } -} diff --git a/src/main/java/de/kaktushose/levelbot/listener/JoinLeaveListener.java b/src/main/java/de/kaktushose/levelbot/listener/JoinLeaveListener.java deleted file mode 100644 index 6343849..0000000 --- a/src/main/java/de/kaktushose/levelbot/listener/JoinLeaveListener.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.kaktushose.levelbot.listener; - -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.model.BotUser; -import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent; -import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.TimeUnit; - -public class JoinLeaveListener extends ListenerAdapter { - - private final Levelbot levelbot; - - public JoinLeaveListener(Levelbot levelbot) { - this.levelbot = levelbot; - } - - @Override - public void onGuildMemberJoin(@NotNull GuildMemberJoinEvent event) { - BotUser botUser = levelbot.getUserService().createUserIfAbsent(event.getMember().getIdLong()); - levelbot.getUserService().addCoins(botUser.getUserId(), 100); - } - - @Override - public void onGuildMemberRemove(@NotNull GuildMemberRemoveEvent event) { - levelbot.getUserService().deleteUser(event.getUser().getIdLong()); - levelbot.getBoosterService().changeNitroBoosterStatus(event.getUser().getIdLong(), false); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/listener/LevelListener.java b/src/main/java/de/kaktushose/levelbot/listener/LevelListener.java deleted file mode 100644 index 81a4b58..0000000 --- a/src/main/java/de/kaktushose/levelbot/listener/LevelListener.java +++ /dev/null @@ -1,113 +0,0 @@ -package de.kaktushose.levelbot.listener; - -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.model.CollectEvent; -import de.kaktushose.levelbot.database.model.Rank; -import de.kaktushose.levelbot.database.services.EventService; -import de.kaktushose.levelbot.database.services.LevelService; -import de.kaktushose.levelbot.database.services.UserService; -import de.kaktushose.levelbot.shop.data.ShopService; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.TextChannel; -import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; - -public class LevelListener extends ListenerAdapter { - - private final LevelService levelService; - private final EventService eventService; - private final UserService userService; - private final ShopService shopService; - private final EmbedCache embedCache; - private final Levelbot levelbot; - - public LevelListener(Levelbot levelbot) { - this.levelService = levelbot.getLevelService(); - this.eventService = levelbot.getEventService(); - this.userService = levelbot.getUserService(); - this.shopService = levelbot.getShopService(); - this.embedCache = levelbot.getEmbedCache(); - this.levelbot = levelbot; - } - - @Override - public void onGuildMessageReceived(@NotNull GuildMessageReceivedEvent event) { - Guild guild = event.getGuild(); - long guildId = event.getGuild().getIdLong(); - User author = event.getAuthor(); - long userId = author.getIdLong(); - if (author.isBot()) { - return; - } - if (!levelService.isValidMessage(userId, guildId, event.getChannel().getIdLong())) { - return; - } - if (event.getMessage().getContentStripped().length() < 10) { - return; - } - - TextChannel channel = levelbot.getBotChannel(); - - if (eventService.isCollectEventActive(guildId)) { - CollectEvent collectEvent = eventService.getActiveCollectEvent(guildId); - long eventPoints = userService.increaseEventPoints(userId); - - if (eventPoints == 1) { - guild.addRoleToMember(userId, guild.getRoleById(collectEvent.getRoleId())).queue(); - - channel.sendMessage(author.getAsMention()) - .and(channel.sendMessageEmbeds(embedCache.getEmbed("collectEventRoleReward") - .injectValue("user", author.getName()) - .toMessageEmbed()) - ).queue(); - } else if (eventPoints == 12) { - userService.addXp(userId, 50); - - channel.sendMessage(author.getAsMention()) - .and(channel.sendMessageEmbeds(embedCache.getEmbed("collectEventXpReward") - .injectValue("user", author.getName()) - .toMessageEmbed()) - ).queue(); - } else if (eventPoints == 24) { - userService.addCoins(userId, 100); - - channel.sendMessage(author.getAsMention()) - .and(channel.sendMessageEmbeds(embedCache.getEmbed("collectEventCoinsReward") - .injectValue("user", author.getName()) - .toMessageEmbed()) - ).queue(); - } - } - - Optional optional = levelService.onValidMessage(userId); - if (optional.isEmpty()) { - return; - } - - Rank currentRank = optional.get(); - Rank nextRank = levelService.getNextRank(userId); - String rewards = levelService.applyRewards(userId, currentRank.getRankId()); - - levelbot.addRankRole(userId, currentRank.getRankId()); - levelbot.removeRankRole(userId, levelService.getPreviousRank(userId).getRankId()); - - String nextRankInfo = currentRank.equals(nextRank) ? "N/A" : String.format("<@&%d>", nextRank.getRoleId()); - String xp = currentRank.equals(nextRank) ? "0" : String.valueOf(nextRank.getBound()); - - channel.sendMessage(author.getAsMention()) - .and(channel.sendMessageEmbeds(embedCache.getEmbed("levelUp") - .injectValue("user", author.getAsMention()) - .injectValue("color", currentRank.getColor()) - .injectValue("currentRank", guild.getRoleById(currentRank.getRoleId()).getAsMention()) - .injectValue("nextRank", nextRankInfo) - .injectValue("reward", rewards) - .injectValue("xp", xp) - .toMessageEmbed()) - ).queue(); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/ShopListener.java b/src/main/java/de/kaktushose/levelbot/shop/ShopListener.java deleted file mode 100644 index b9dd163..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/ShopListener.java +++ /dev/null @@ -1,206 +0,0 @@ -package de.kaktushose.levelbot.shop; - -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.discord.reactionwaiter.ReactionWaiter; -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.services.LevelService; -import de.kaktushose.levelbot.database.services.UserService; -import de.kaktushose.levelbot.shop.data.ShopService; -import de.kaktushose.levelbot.shop.data.items.Item; -import de.kaktushose.levelbot.util.NumberEmojis; -import net.dv8tion.jda.api.MessageBuilder; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.TextChannel; -import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent; -import net.dv8tion.jda.api.exceptions.ErrorHandler; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import static net.dv8tion.jda.api.requests.ErrorResponse.UNKNOWN_MESSAGE; - -public class ShopListener extends ListenerAdapter { - - // this should find its way into the database one day - public static final String PREMIUM_MESSAGE_ID = "851454666591698965"; - public static final String DJ_MESSAGE_ID = "851454738904907827"; - public static final String NICKNAME_MESSAGE_ID = "851454813623681024"; - public static final String COINS_BOOSTER_MESSAGE_ID = "851454959224225813"; - public static final String XP_BOOSTER_MESSAGE_ID = "851454895788654625"; - private static final Long LEVEL_SYSTEM_CHANNEL_ID = 851388807239827466L; - private static final String CONFIRM = "✅"; - private static final String CANCEL = "❌"; - private final Set activeUsers; - private final UserService userService; - private final ShopService shopService; - private final LevelService levelService; - private final EmbedCache embedCache; - private final Levelbot levelbot; - - public ShopListener(Levelbot levelbot) { - this.userService = levelbot.getUserService(); - this.shopService = levelbot.getShopService(); - this.levelService = levelbot.getLevelService(); - this.embedCache = levelbot.getEmbedCache(); - this.levelbot = levelbot; - this.activeUsers = new HashSet<>(); - } - - @Override - public void onGuildMessageReactionAdd(@NotNull GuildMessageReactionAddEvent event) { - // bots should just be ignored - if (event.getUser().isBot()) { - return; - } - - // must be in channel #levelsystem - if (event.getChannel().getIdLong() != LEVEL_SYSTEM_CHANNEL_ID) { - return; - } - - if (levelbot.getUserService().getMutedUsers().contains(event.getUser().getIdLong())) { - event.getReaction().removeReaction(event.getUser()).queue( - null, new ErrorHandler().ignore(UNKNOWN_MESSAGE) - ); - return; - } - - // indicates active purchase - if (activeUsers.contains(event.getUser().getIdLong())) { - event.getReaction().removeReaction(event.getUser()).queue( - null, new ErrorHandler().ignore(UNKNOWN_MESSAGE) - ); - return; - } - - Member member = event.getMember(); - ItemCategory itemCategory; - switch (event.getMessageId()) { - case PREMIUM_MESSAGE_ID: - itemCategory = ItemCategory.PREMIUM; - break; - case DJ_MESSAGE_ID: - itemCategory = ItemCategory.DJ; - break; - case NICKNAME_MESSAGE_ID: - itemCategory = ItemCategory.NICKNAME; - break; - case COINS_BOOSTER_MESSAGE_ID: - itemCategory = ItemCategory.COINS_BOOSTER; - break; - case XP_BOOSTER_MESSAGE_ID: - itemCategory = ItemCategory.XP_BOOSTER; - break; - default: - event.getReaction().removeReaction(member.getUser()).queue( - null, new ErrorHandler().ignore(UNKNOWN_MESSAGE) - ); - return; - } - - int variant; - switch (event.getReactionEmote().getName()) { - case NumberEmojis.ONE: - variant = 0; - break; - case NumberEmojis.TWO: - variant = 1; - break; - case NumberEmojis.THREE: - variant = 2; - break; - default: - event.getReaction().removeReaction(member.getUser()).queue(); - return; - } - - if ((itemCategory == ItemCategory.COINS_BOOSTER || itemCategory == ItemCategory.XP_BOOSTER) && variant == 2) { - event.getReaction().removeReaction(member.getUser()).queue(); - return; - } - - Item item = levelService.getItemsByCategoryId(itemCategory.id).get(variant); - BotUser botUser = userService.getUserById(member.getIdLong()); - String fail = null; - if (shopService.hasItemOfCategory(member.getIdLong(), item.getCategoryId())) { - fail = "Du besitzt dieses Item bereits!"; - } - if (botUser.getCoins() < item.getPrice()) { - fail = "Du hast nicht genug Münzen!"; - } - - TextChannel channel = event.getChannel(); - - Consumer delete = success -> success.delete().queueAfter( - 30, TimeUnit.SECONDS, null, new ErrorHandler().ignore(UNKNOWN_MESSAGE) - ); - - MessageBuilder builder = new MessageBuilder().append(member.getAsMention()); - - if (fail != null) { - channel.sendMessage( // transaction failed - builder.setEmbeds(embedCache.getEmbed("shopError") - .injectValue("message", fail) - .toMessageEmbed() - ).build() - ).queue(delete); - } else { - activeUsers.add(member.getIdLong()); - channel.sendMessage( // confirm transaction - builder.setEmbeds(embedCache.getEmbed("shopConfirm") - .injectValue("item", item.getName()) - .injectValue("price", item.getPrice()) - .toMessageEmbed() - ).build() - ).queue(confirmMessage -> { // wait for reactions - confirmMessage.delete().queueAfter( // delete confirm message after 30 secs if nothing happens - 30, TimeUnit.SECONDS, - success -> activeUsers.remove(member.getIdLong()), - new ErrorHandler().ignore(UNKNOWN_MESSAGE) - ); - confirmMessage.addReaction(CONFIRM).and(confirmMessage.addReaction(CANCEL)).queue(); - ReactionWaiter waiter = new ReactionWaiter(confirmMessage, event.getMember(), CONFIRM, CANCEL); - waiter.onEvent(reactionEvent -> { // on reaction confirm or cancel emoji - confirmMessage.delete().queue(); - if (reactionEvent.getEmote().equals(CONFIRM)) { - shopService.buyItem(botUser.getUserId(), item.getItemId()); - channel.sendMessageEmbeds( // successful transaction - embedCache.getEmbed("shopSuccess") - .injectValue("item", item.getName()) - .injectValue("days", TimeUnit.MILLISECONDS.toDays(item.getDuration())) - .toMessageEmbed() - ).queue(delete); - levelbot.getLogChannel().sendMessageEmbeds(embedCache.getEmbed("buyLog") - .injectValue("user", member.getAsMention()) - .injectValue("item", item.getName()) - .toMessageEmbed() - ).queue(); - } - activeUsers.remove(member.getIdLong()); - waiter.stopWaiting(false); - }); - }); - } - event.getReaction().removeReaction(member.getUser()).queue(); - } - - private enum ItemCategory { - PREMIUM(0), - DJ(1), - NICKNAME(2), - COINS_BOOSTER(3), - XP_BOOSTER(4); - - public final int id; - - ItemCategory(int id) { - this.id = id; - } - } -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/commands/AddItemCommand.java b/src/main/java/de/kaktushose/levelbot/shop/commands/AddItemCommand.java deleted file mode 100644 index 7ab6084..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/commands/AddItemCommand.java +++ /dev/null @@ -1,245 +0,0 @@ -package de.kaktushose.levelbot.shop.commands; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.discord.reactionwaiter.EmoteType; -import de.kaktushose.discord.reactionwaiter.ReactionWaiter; -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.services.LevelService; -import de.kaktushose.levelbot.database.services.UserService; -import de.kaktushose.levelbot.shop.data.ShopService; -import de.kaktushose.levelbot.shop.data.items.Item; -import de.kaktushose.levelbot.util.NumberEmojis; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -@CommandController("additem") -@Permission("moderator") -public class AddItemCommand { - - private static final String BACK = "◀️"; - private static final String CANCEL = "❌"; - private static final String PREMIUM = "⭐"; - private static final String DJ = "\uD83C\uDFB5"; - private static final String NICKNAME = "\uD83D\uDC68\uD83C\uDFFD"; - private static final String COIN_BOOSTER = "\uD83D\uDCB0"; - private static final String XP_BOOSTER = "\uD83C\uDF1F"; - private final Map specificShops; - @Inject - private UserService userService; - @Inject - private ShopService shopService; - @Inject - private LevelService levelService; - @Inject - private EmbedCache embedCache; - @Inject - private Levelbot levelbot; - private EmbedBuilder shopOverview; - - public AddItemCommand() { - specificShops = new HashMap<>(); - } - - @Command( - name = "Level-Shop", - usage = "{prefix}additem ", - desc = "Fügt ein Item einem anderen Nutzer hinzu", - category = "Moderation" - ) - public void onAddItem(CommandEvent event, Member member) { - generateEmbeds(member.getAsMention()); - sendDefaultShop(event, null, member); - } - - private void sendDefaultShop(CommandEvent event, Message shopMessage, Member target) { - Consumer success = message -> { - - message.clearReactions() - .and(message.addReaction(PREMIUM)) - .and(message.addReaction(DJ)) - .and(message.addReaction(NICKNAME)) - .and(message.addReaction(COIN_BOOSTER)) - .and(message.addReaction(XP_BOOSTER)) - .and(message.addReaction(CANCEL)) - .queue(); - - ReactionWaiter reactionWaiter = new ReactionWaiter(message, event.getMember(), PREMIUM, DJ, NICKNAME, COIN_BOOSTER, XP_BOOSTER, CANCEL); - reactionWaiter.onEvent(reactionEvent -> { - switch (reactionEvent.getEmote()) { - case PREMIUM: - sendSpecificShop(event, message, target, ItemCategory.PREMIUM); - break; - case DJ: - sendSpecificShop(event, message, target, ItemCategory.DJ); - break; - case NICKNAME: - sendSpecificShop(event, message, target, ItemCategory.NICKNAME); - break; - case COIN_BOOSTER: - sendSpecificShop(event, message, target, ItemCategory.COINS_BOOSTER); - break; - case XP_BOOSTER: - sendSpecificShop(event, message, target, ItemCategory.XP_BOOSTER); - break; - case CANCEL: - message.delete().and(event.getMessage().delete()).queue(); - break; - } - reactionWaiter.stopWaiting(false); - }); - }; - - if (shopMessage == null) { - event.reply(shopOverview, success); - } else { - shopMessage.editMessage(shopOverview.build()).queue(success); - } - } - - private void sendSpecificShop(CommandEvent event, Message shopMessage, Member target, ItemCategory itemCategory) { - EmbedBuilder embedBuilder = specificShops.get(itemCategory); - List items = levelService.getItemsByCategoryId(itemCategory.id); - long amount = items.stream().filter(Item::isVisible).count(); - Consumer success = message -> { - message.clearReactions().queue(); - for (int i = 0; i < amount; i++) { - message.addReaction(EmoteType.getNumber(i + 1).unicode).queue(); - } - message.addReaction(BACK) - .and(message.addReaction(CANCEL)) - .queue(); - - ReactionWaiter reactionWaiter = new ReactionWaiter( - message, - event.getMember(), - BACK, CANCEL, - NumberEmojis.ONE, NumberEmojis.TWO, NumberEmojis.THREE, NumberEmojis.FOUR - ); - reactionWaiter.onEvent(reactionEvent -> { - int variant = 0; - switch (reactionEvent.getEmote()) { - case NumberEmojis.ONE: - variant = 1; - break; - case NumberEmojis.TWO: - variant = 2; - break; - case NumberEmojis.THREE: - variant = 3; - break; - case NumberEmojis.FOUR: - variant = 4; - break; - case BACK: - sendDefaultShop(event, message, target); - reactionWaiter.stopWaiting(false); - return; - case CANCEL: - message.delete().and(event.getMessage().delete()).queue(); - reactionWaiter.stopWaiting(false); - return; - } - if (amount < variant) { - return; - } - - Item item = items.get(variant - 1); - Optional buyResult = addItem(target, item); - - if (buyResult.isEmpty()) { - message.editMessage(embedCache.getEmbed("addItemSuccess") - .injectValue("user", target.getAsMention()) - .injectValue("item", item.getName()) - .injectValue("days", TimeUnit.MILLISECONDS.toDays(item.getDuration())) - .toMessageEmbed() - ).and(message.clearReactions()).queue(); - } else { - message.editMessage(embedCache.getEmbed("shopError") - .injectValue("message", buyResult.get()) - .toMessageEmbed() - ).queue(errorMessage -> { - errorMessage.clearReactions().queue(); - errorMessage.addReaction(BACK).and(errorMessage.addReaction(CANCEL)).queue(); - ReactionWaiter waiter = new ReactionWaiter(message, event.getMember(), BACK, CANCEL); - waiter.onEvent(errorMessageEvent -> { - if (errorMessageEvent.getEmote().equals(BACK)) { - sendDefaultShop(event, message, target); - } else { - message.delete().and(event.getMessage().delete()).queue(); - } - waiter.stopWaiting(false); - }); - }); - } - reactionWaiter.stopWaiting(false); - }); - }; - - if (shopMessage == null) { - event.reply(embedBuilder, success); - } else { - shopMessage.editMessage(embedBuilder.build()).queue(success); - } - } - - private Optional addItem(Member member, Item item) { - BotUser botUser = userService.getUserById(member.getIdLong()); - - if (shopService.hasItem(member.getIdLong(), item.getItemId())) { - return Optional.of(member.getAsMention() + " besitzt dieses Item bereits!"); - } - - shopService.addItem(botUser.getUserId(), item.getItemId()); - - return Optional.empty(); - } - - private void generateEmbeds(String mention) { - shopOverview = embedCache.getEmbed("shopOverview").injectValue("user", mention).toEmbedBuilder(); - - for (ItemCategory itemCategory : ItemCategory.values()) { - EmbedBuilder specificShop = embedCache.getEmbed("specificShop").toEmbedBuilder(); - List items = levelService.getItemsByCategoryId(itemCategory.id); - for (int i = 0; i < items.size(); i++) { - Item item = items.get(i); - if (!item.isVisible()) { - continue; - } - specificShop.addField( - String.format("%s: %s", EmoteType.getNumber(i + 1).unicode, item.getName()), - String.format("Preis: %s :moneybag:\nDauer: %d Tage", item.getPrice(), TimeUnit.MILLISECONDS.toDays(item.getDuration())), - false - ); - } - specificShops.put(itemCategory, specificShop); - } - } - - private enum ItemCategory { - PREMIUM(0), - DJ(1), - NICKNAME(2), - COINS_BOOSTER(3), - XP_BOOSTER(4); - - public final int id; - - ItemCategory(int id) { - this.id = id; - } - } -} \ No newline at end of file diff --git a/src/main/java/de/kaktushose/levelbot/shop/commands/InitShopCommand.java b/src/main/java/de/kaktushose/levelbot/shop/commands/InitShopCommand.java deleted file mode 100644 index de038ca..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/commands/InitShopCommand.java +++ /dev/null @@ -1,66 +0,0 @@ -package de.kaktushose.levelbot.shop.commands; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.SettingsService; -import de.kaktushose.levelbot.listener.DailyRewardListener; -import de.kaktushose.levelbot.shop.ShopListener; -import de.kaktushose.levelbot.util.NumberEmojis; -import net.dv8tion.jda.api.entities.TextChannel; - -@CommandController("initshop") -@Permission("moderator") -public class InitShopCommand { - - @Inject - private SettingsService settingsService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Reaction Shop einrichten", - usage = "{prefix}initshop ", - desc = "Fügt die benötigten Reactions für den Shop hinzu", - category = "Moderation" - ) - public void initShop(CommandEvent event, TextChannel channel) { - channel.retrieveMessageById(ShopListener.PREMIUM_MESSAGE_ID).queue(msg -> msg.clearReactions() - .and(msg.addReaction(NumberEmojis.ONE)) - .and(msg.addReaction(NumberEmojis.TWO)) - .and(msg.addReaction(NumberEmojis.THREE)) - .queue() - ); - channel.retrieveMessageById(ShopListener.DJ_MESSAGE_ID).queue(msg -> msg.clearReactions() - .and(msg.addReaction(NumberEmojis.ONE)) - .and(msg.addReaction(NumberEmojis.TWO)) - .and(msg.addReaction(NumberEmojis.THREE)) - .queue() - ); - channel.retrieveMessageById(ShopListener.NICKNAME_MESSAGE_ID).queue(msg -> msg.clearReactions() - .and(msg.addReaction(NumberEmojis.ONE)) - .and(msg.addReaction(NumberEmojis.TWO)) - .and(msg.addReaction(NumberEmojis.THREE)) - .queue() - ); - channel.retrieveMessageById(ShopListener.COINS_BOOSTER_MESSAGE_ID).queue(msg -> msg.clearReactions() - .and(msg.addReaction(NumberEmojis.ONE)) - .and(msg.addReaction(NumberEmojis.TWO)) - .queue() - ); - channel.retrieveMessageById(ShopListener.XP_BOOSTER_MESSAGE_ID).queue(msg -> msg.clearReactions() - .and(msg.addReaction(NumberEmojis.ONE)) - .and(msg.addReaction(NumberEmojis.TWO)) - .queue() - ); - channel.retrieveMessageById(DailyRewardListener.DAILY_REWARD_MESSAGE_ID).queue(msg -> msg.clearReactions() - .and(msg.addReaction("\uD83C\uDF81")) - .queue() - ); - event.reply(embedCache.getEmbed("shopInitSuccess")); - } -} - diff --git a/src/main/java/de/kaktushose/levelbot/shop/commands/RemoveItemCommand.java b/src/main/java/de/kaktushose/levelbot/shop/commands/RemoveItemCommand.java deleted file mode 100644 index 88fe981..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/commands/RemoveItemCommand.java +++ /dev/null @@ -1,85 +0,0 @@ -package de.kaktushose.levelbot.shop.commands; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.discord.reactionwaiter.EmoteType; -import de.kaktushose.discord.reactionwaiter.ReactionWaiter; -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.shop.data.ShopService; -import de.kaktushose.levelbot.shop.data.items.Item; -import de.kaktushose.levelbot.util.NumberEmojis; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.Member; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -@CommandController({"remove", "rm"}) -@Permission("moderator") -public class RemoveItemCommand { - - @Inject - private ShopService shopService; - @Inject - private EmbedCache embedCache; - @Inject - private Levelbot levelbot; - - @Command( - name = "Item entfernen", - usage = "{prefix}remove ", - desc = "Entfernt ein Item aus dem Besitz eines Benutzers", - category = "Moderation" - ) - public void onRemoveItem(CommandEvent event, Member member) { - List items = shopService.getItems(member.getIdLong()); - AtomicInteger counter = new AtomicInteger(1); - List emotes = new ArrayList<>(); - EmbedBuilder builder = embedCache.getEmbed("removeItem") - .injectValue("user", member.getAsMention()) - .toEmbedBuilder(); - - items.forEach(item -> { - String emote = EmoteType.getNumber(counter.getAndIncrement()).unicode; - emotes.add(emote); - builder.addField(emote + ": " + item.getName(), "", false); - }); - - event.reply(builder, chooseMessage -> { - emotes.forEach(emote -> chooseMessage.addReaction(emote).queue()); - - ReactionWaiter reactionWaiter = new ReactionWaiter(chooseMessage, event.getMember(), emotes); - reactionWaiter.onEvent(reactionEvent -> { - Item forRemoval; - switch (reactionEvent.getEmote()) { - case NumberEmojis.ONE: - forRemoval = items.get(0); - break; - case NumberEmojis.TWO: - forRemoval = items.get(1); - break; - case NumberEmojis.THREE: - forRemoval = items.get(2); - break; - case NumberEmojis.FOUR: - forRemoval = items.get(3); - break; - case NumberEmojis.FIVE: - forRemoval = items.get(4); - break; - default: - return; - } - shopService.removeItem(member.getIdLong(), forRemoval.getItemId()); - reactionWaiter.stopWaiting(false); - chooseMessage.delete().queue(); - event.reply(embedCache.getEmbed("removeItemSuccess").injectValue("user", member.getAsMention())); - }); - }); - } -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/commands/SetPriceCommand.java b/src/main/java/de/kaktushose/levelbot/shop/commands/SetPriceCommand.java deleted file mode 100644 index 79e6ec4..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/commands/SetPriceCommand.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.kaktushose.levelbot.shop.commands; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.Permission; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; -import de.kaktushose.levelbot.database.services.LevelService; - -@CommandController("setprice") -@Permission("moderator") -public class SetPriceCommand { - - @Inject - private LevelService levelService; - @Inject - private EmbedCache embedCache; - - @Command( - name = "Preis ändern", - usage = "{prefix}setprice ", - desc = "Setzt den Preis eines Items auf den angegebenen Wert", - category = "Moderation" - ) - public void onSetPrice(CommandEvent event, int itemId, int price) { - if (levelService.setItemPrice(itemId, price)) { - event.reply(embedCache.getEmbed("itemPriceChanged")); - } else { - event.reply(embedCache.getEmbed("itemNotFound").injectValue("id", itemId)); - } - } -} - diff --git a/src/main/java/de/kaktushose/levelbot/shop/commands/ShopDeprecatedCommand.java b/src/main/java/de/kaktushose/levelbot/shop/commands/ShopDeprecatedCommand.java deleted file mode 100644 index 6aaa884..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/commands/ShopDeprecatedCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -package de.kaktushose.levelbot.shop.commands; - -import com.github.kaktushose.jda.commands.annotations.Command; -import com.github.kaktushose.jda.commands.annotations.CommandController; -import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.dispatching.CommandEvent; -import com.github.kaktushose.jda.commands.embeds.EmbedCache; - -@CommandController("kaufen") -public class ShopDeprecatedCommand { - - @Inject - private EmbedCache embedCache; - - @Command - public void onBuy(CommandEvent event) { - event.reply(embedCache.getEmbed("shopCommandDeprecated")); - } - - @Command("dummy") - public void onDummy(CommandEvent event) { - onBuy(event); - } - -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/ShopService.java b/src/main/java/de/kaktushose/levelbot/shop/data/ShopService.java deleted file mode 100644 index 05c3576..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/ShopService.java +++ /dev/null @@ -1,163 +0,0 @@ -package de.kaktushose.levelbot.shop.data; - -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.repositories.UserRepository; -import de.kaktushose.levelbot.database.services.UserService; -import de.kaktushose.levelbot.shop.data.items.*; -import de.kaktushose.levelbot.shop.data.transactions.Transaction; -import de.kaktushose.levelbot.shop.data.transactions.TransactionRepository; -import de.kaktushose.levelbot.spring.ApplicationContextHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class ShopService { - - private final static Logger log = LoggerFactory.getLogger(ShopService.class); - private final UserRepository userRepository; - private final TransactionRepository transactionRepository; - private final ItemRepository itemRepository; - private final FrozenItemRepository frozenItemRepository; - private final UserService userService; - private final Levelbot levelbot; - - public ShopService(Levelbot levelbot) { - ApplicationContext context = ApplicationContextHolder.getContext(); - userRepository = context.getBean(UserRepository.class); - transactionRepository = context.getBean(TransactionRepository.class); - itemRepository = context.getBean(ItemRepository.class); - frozenItemRepository = context.getBean(FrozenItemRepository.class); - userService = levelbot.getUserService(); - this.levelbot = levelbot; - } - - public boolean hasItem(long userId, int itemId) { - return transactionRepository.existsByUserIdAndItemId(userId, itemId); - } - - public boolean hasItemOfCategory(long userId, ItemCategory category) { - return hasItemOfCategory(userId, category.getCategoryId()); - } - - public boolean hasItemOfCategory(long userId, int category) { - return transactionRepository.existsByUserIdAndCategoryId(userId, category); - } - - public List getItems(long userId) { - return itemRepository.findItemsByUserId(userId); - } - - public void buyItem(long userId, int itemId) { - BotUser botUser = userService.getUserById(userId); - Item item = itemRepository.findById(itemId).orElseThrow(); - transactionRepository.save(addItemToUser(botUser, item)); - botUser.setCoins(botUser.getCoins() - item.getPrice()); - userRepository.save(botUser); - levelbot.addItemRole(botUser.getUserId(), item.getItemId()); - } - - public void addItem(long userId, ItemCategory category, ItemVariant variant) { - addItem(userId, category.getItemId(variant)); - } - - public void addItem(long userId, int itemId) { - Item item = itemRepository.findById(itemId).orElseThrow(); - - if (itemId == 3) { - if (hasItemOfCategory(userId, ItemCategory.PREMIUM)) { - frozenItemRepository.save(FrozenItem.fromTransaction(userId, getTransaction(userId, itemId))); - removeItem(userId, itemId); - } - } - - BotUser botUser = userService.getUserById(userId); - Transaction transaction; - if (hasItemOfCategory(userId, item.getCategoryId())) { - // TODO add getItemByCategoryId method - int id = getItems(userId).stream().filter(i -> i.getCategoryId() == item.getCategoryId()) - .findFirst() - .orElseThrow() - .getItemId(); - transaction = getTransaction(userId, id); - transaction.setBuyTime(transaction.getBuyTime() + item.getDuration()); - } else { - transaction = addItemToUser(botUser, item); - levelbot.addItemRole(userId, item.getItemId()); - } - - transactionRepository.save(transaction); - userRepository.save(botUser); - } - - private Transaction addItemToUser(BotUser botUser, Item item) { - Transaction transaction = new Transaction(); - transaction.setBuyTime(System.currentTimeMillis()); - transaction.setItem(item); - transaction.setUserId(botUser.getUserId()); - botUser.getTransactions().add(transaction); - return transaction; - } - - public Transaction getTransaction(long userId, int itemId) { - return transactionRepository.findByUserIdAndItemId(userId, itemId).stream().findFirst().orElseThrow(); - } - - public void removeItem(long userId, ItemCategory category, ItemVariant variant) { - removeItem(userId, category.getItemId(variant)); - } - - public void removeItem(long userId, int itemId) { - transactionRepository.deleteByUserIdAndItemId(userId, itemId); - BotUser botUser = userService.getUserById(userId); - - if (itemId == 3) { - if (frozenItemRepository.existsById(userId)) { - FrozenItem frozenItem = frozenItemRepository.findById(userId).orElseThrow(); - - Transaction transaction = new Transaction(); - transaction.setBuyTime( - frozenItem.getBuyTime() + (System.currentTimeMillis() - frozenItem.getStartTime()) - ); - transaction.setItem(frozenItem.getItem()); - botUser.getTransactions().add(transaction); - - frozenItemRepository.delete(frozenItem); - transactionRepository.save(transaction); - userRepository.save(botUser); - return; - } - } - - levelbot.removeItemRole(userId, itemId); - } - - public void checkForExpiredItems() { - for (Transaction transaction : transactionRepository.findExpiringTransactions()) { - Item item = transaction.getItem(); - long remaining = item.getRemainingTimeAsLong(transaction.getBuyTime()); - long userId = transaction.getUserId(); - int itemId = item.getItemId(); - - if (itemId == ItemCategory.PREMIUM.getItemId(ItemVariant.UNLIMITED)) { - continue; - } - if (remaining <= 0) { - removeItem(userId, itemId); - levelbot.sendItemExpiredInformation(userId, itemId, transaction.getBuyTime()); - } else if (remaining < 86400000) { - levelbot.getTaskScheduler().addSingleTask(() -> { - try { - removeItem(userId, itemId); - levelbot.sendItemExpiredInformation(userId, itemId, transaction.getBuyTime()); - } catch (Throwable t) { - log.error("An exception has occurred while removing an item!", t); - } - }, remaining, TimeUnit.MILLISECONDS); - } - } - } -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/items/FrozenItem.java b/src/main/java/de/kaktushose/levelbot/shop/data/items/FrozenItem.java deleted file mode 100644 index 4ca0e78..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/items/FrozenItem.java +++ /dev/null @@ -1,68 +0,0 @@ -package de.kaktushose.levelbot.shop.data.items; - -import de.kaktushose.levelbot.shop.data.transactions.Transaction; - -import javax.persistence.*; - -/** - * This class covers the edge case where a user has normal premium but receives unlimited as well. Unlimited will become - * the active item and the normal premium will be moved to this table until unlimited gets removed again. - */ -@Entity -@Table(name = "frozen_items") -public class FrozenItem { - - @Id - private long userId; - private long startTime; - private long buyTime; - @OneToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "itemId", referencedColumnName = "itemId") - private Item item; - - public FrozenItem() { - } - - public FrozenItem(long userId, long startTime, long buyTime, Item item) { - this.userId = userId; - this.startTime = startTime; - this.buyTime = buyTime; - this.item = item; - } - - public static FrozenItem fromTransaction(long userId, Transaction transaction) { - return new FrozenItem(userId, System.currentTimeMillis(), transaction.getBuyTime(), transaction.getItem()); - } - - public long getUserId() { - return userId; - } - - public void setUserId(long userId) { - this.userId = userId; - } - - public long getStartTime() { - return startTime; - } - - public void setStartTime(long startTime) { - this.startTime = startTime; - } - - public long getBuyTime() { - return buyTime; - } - - public void setBuyTime(long buyTime) { - this.buyTime = buyTime; - } - - public Item getItem() { - return item; - } - - public void setItem(Item item) { - this.item = item; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/items/FrozenItemRepository.java b/src/main/java/de/kaktushose/levelbot/shop/data/items/FrozenItemRepository.java deleted file mode 100644 index 42afd16..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/items/FrozenItemRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package de.kaktushose.levelbot.shop.data.items; - -import org.springframework.data.repository.CrudRepository; - -public interface FrozenItemRepository extends CrudRepository { -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/items/Item.java b/src/main/java/de/kaktushose/levelbot/shop/data/items/Item.java deleted file mode 100644 index 9c1d5cb..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/items/Item.java +++ /dev/null @@ -1,106 +0,0 @@ -package de.kaktushose.levelbot.shop.data.items; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; -import java.util.concurrent.TimeUnit; - -@Entity -@Table(name = "items") -public class Item { - - @Id - private Integer itemId; - private String name; - private int price; - private long duration; - private int categoryId; - private boolean visible; - private long roleId; - - public Item() { - } - - public Item(int itemId, String name, int price, long duration, int categoryId, boolean visible, long roleId) { - this.itemId = itemId; - this.name = name; - this.price = price; - this.duration = duration; - this.categoryId = categoryId; - this.visible = visible; - this.roleId = roleId; - } - - public Integer getItemId() { - return itemId; - } - - public void setItemId(Integer itemId) { - this.itemId = itemId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getPrice() { - return price; - } - - public void setPrice(int price) { - this.price = price; - } - - public long getDuration() { - return duration; - } - - public void setDuration(long duration) { - this.duration = duration; - } - - public int getCategoryId() { - return categoryId; - } - - public void setCategoryId(int categoryId) { - this.categoryId = categoryId; - } - - public boolean isVisible() { - return visible; - } - - public void setVisible(boolean visible) { - this.visible = visible; - } - - public long getRoleId() { - return roleId; - } - - public void setRoleId(long roleId) { - this.roleId = roleId; - } - - public String getRemainingTimeAsDate(long buyTime) { - if (duration < 1) { - return "unbegrenzt"; - } - long millis = duration - (System.currentTimeMillis() - buyTime); - long days = TimeUnit.MILLISECONDS.toDays(millis); - long hours = TimeUnit.MILLISECONDS.toHours(millis) - TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(millis)); - String daysPattern = days != 1 ? "%d Tage" : "ein Tag"; - String hoursPattern = hours != 1 ? "%d Stunden" : "eine Stunde"; - return String.format(daysPattern, days) + " und " + String.format(hoursPattern, hours); - } - - public long getRemainingTimeAsLong(long buyTime) { - return duration - (System.currentTimeMillis() - buyTime); - } - -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemCategory.java b/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemCategory.java deleted file mode 100644 index 0a66ca3..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemCategory.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.kaktushose.levelbot.shop.data.items; - -public enum ItemCategory { - - PREMIUM(0, 0), - DJ_PERK(1, 4), - NICKNAME_PERK(2, 7), - COIN_BOOSTER(3, 10), - XP_BOOSTER(4, 12); - - final int categoryId; - final int itemIdBaseValue; - - ItemCategory(int categoryId, int itemIdBaseValue) { - this.categoryId = categoryId; - this.itemIdBaseValue = itemIdBaseValue; - } - - public int getCategoryId() { - return categoryId; - } - - public int getItemId(ItemVariant variant) { - return itemIdBaseValue + variant.getItemIdSummand(); - } - - -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemRepository.java b/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemRepository.java deleted file mode 100644 index c521bff..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package de.kaktushose.levelbot.shop.data.items; - -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; - -import java.util.List; - -public interface ItemRepository extends CrudRepository { - - List findByCategoryId(int categoryId); - - @Query(value = "SELECT * FROM items where items.item_id IN (SELECT transactions.item_id FROM transactions WHERE user_id = :userId)", nativeQuery = true) - List findItemsByUserId(@Param("userId") long userId); - -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemVariant.java b/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemVariant.java deleted file mode 100644 index d619a33..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/items/ItemVariant.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.kaktushose.levelbot.shop.data.items; - -public enum ItemVariant { - LIGHT(0), - BASIC(1), - GOLD(2), - UNLIMITED(3); - - private final int itemIdSummand; - - ItemVariant(int itemIdSummand) { - this.itemIdSummand = itemIdSummand; - } - - int getItemIdSummand() { - return itemIdSummand; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/transactions/Transaction.java b/src/main/java/de/kaktushose/levelbot/shop/data/transactions/Transaction.java deleted file mode 100644 index f3756d7..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/transactions/Transaction.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.kaktushose.levelbot.shop.data.transactions; - -import de.kaktushose.levelbot.shop.data.items.Item; - -import javax.persistence.*; - -@Entity -@Table(name = "transactions") -public class Transaction { - - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long transactionId; - @OneToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "itemId", referencedColumnName = "itemId") - private Item item; - private long userId; - private long buyTime; - - public Transaction() { - } - - public Transaction(Long transactionId, Item item, long userId, long buyTime) { - this.transactionId = transactionId; - this.item = item; - this.buyTime = buyTime; - this.userId = userId; - } - - public Long getTransactionId() { - return transactionId; - } - - public void setTransactionId(Long transactionId) { - this.transactionId = transactionId; - } - - public Item getItem() { - return item; - } - - public void setItem(Item item) { - this.item = item; - } - - public long getBuyTime() { - return buyTime; - } - - public void setBuyTime(long buyTime) { - this.buyTime = buyTime; - } - - public long getUserId() { - return userId; - } - - public void setUserId(long userId) { - this.userId = userId; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/shop/data/transactions/TransactionRepository.java b/src/main/java/de/kaktushose/levelbot/shop/data/transactions/TransactionRepository.java deleted file mode 100644 index 8db4ae6..0000000 --- a/src/main/java/de/kaktushose/levelbot/shop/data/transactions/TransactionRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.kaktushose.levelbot.shop.data.transactions; - -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; - -import javax.transaction.Transactional; -import java.util.List; - -public interface TransactionRepository extends CrudRepository { - - @Query(value = "SELECT new java.lang.Boolean(count(*) > 0) FROM Transaction WHERE user_id = :userId and item_id = :itemId") - boolean existsByUserIdAndItemId(@Param("userId") long userId, @Param("itemId") int itemId); - - @Query(value = "SELECT * from transactions WHERE user_id = :userId and item_id = :itemId", nativeQuery = true) - List findByUserIdAndItemId(@Param("userId") long userId, @Param("itemId") int itemId); - - @Modifying - @Transactional - @Query(value = "DELETE from transactions WHERE user_id = :userId and item_id = :itemId", nativeQuery = true) - void deleteByUserIdAndItemId(@Param("userId") long userId, @Param("itemId") int itemId); - - @Query("SELECT new java.lang.Boolean(count(*) > 0) FROM Transaction t WHERE t.userId = :userId AND t.item.itemId in (SELECT i.itemId from Item i where i.categoryId = :categoryId)") - boolean existsByUserIdAndCategoryId(@Param("userId") long userId, @Param("categoryId") int categoryId); - - @Query(value = "SELECT * FROM transactions INNER JOIN items ON items.item_id=transactions.item_id WHERE duration - ((UNIX_TIMESTAMP(NOW()) * 1000) - buy_time) < 86400000 AND transactions.item_id <> 3", nativeQuery = true) - List findExpiringTransactions(); -} diff --git a/src/main/java/de/kaktushose/levelbot/spring/ApplicationContextHolder.java b/src/main/java/de/kaktushose/levelbot/spring/ApplicationContextHolder.java deleted file mode 100644 index 541d238..0000000 --- a/src/main/java/de/kaktushose/levelbot/spring/ApplicationContextHolder.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.kaktushose.levelbot.spring; - -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -@Component -public class ApplicationContextHolder implements ApplicationContextAware { - - private static ApplicationContext context; - - public static ApplicationContext getContext() { - return context; - } - - @Override - public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { - context = applicationContext; - } -} diff --git a/src/main/java/de/kaktushose/levelbot/util/NumberEmojis.java b/src/main/java/de/kaktushose/levelbot/util/NumberEmojis.java deleted file mode 100644 index f6f2c81..0000000 --- a/src/main/java/de/kaktushose/levelbot/util/NumberEmojis.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.kaktushose.levelbot.util; - -/* -For some reason, number emojis don't behave like other normal emojis, so this class provides a little workaround to prevent -me from feeling that urgent need to delete this project and set my PC on fire every time I need to work with numbers - */ -public class NumberEmojis { - - public static final String ZERO = "0️⃣"; - public static final String ONE = "1️⃣"; - public static final String TWO = "2️⃣"; - public static final String THREE = "3️⃣"; - public static final String FOUR = "4️⃣"; - public static final String FIVE = "5️⃣"; - public static final String SIX = "6️⃣"; - public static final String SEVEN = "7️⃣"; - public static final String EIGHT = "8️⃣"; - public static final String NINE = "9️⃣"; - -} diff --git a/src/main/java/de/kaktushose/levelbot/util/Pageable.java b/src/main/java/de/kaktushose/levelbot/util/Pageable.java deleted file mode 100644 index 2e774ae..0000000 --- a/src/main/java/de/kaktushose/levelbot/util/Pageable.java +++ /dev/null @@ -1,9 +0,0 @@ -package de.kaktushose.levelbot.util; - -public interface Pageable { - - Long getUserId(); - - long getCount(Pagination.CurrencyType currencyType); - -} diff --git a/src/main/java/de/kaktushose/levelbot/util/Pagination.java b/src/main/java/de/kaktushose/levelbot/util/Pagination.java deleted file mode 100644 index 8c0b54a..0000000 --- a/src/main/java/de/kaktushose/levelbot/util/Pagination.java +++ /dev/null @@ -1,85 +0,0 @@ -package de.kaktushose.levelbot.util; - -import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.User; - -import java.util.List; -import java.util.stream.Collectors; - -public class Pagination { - - private final CurrencyType currencyType; - private final JDA jda; - private final List leaderboard; - private final int pageSize; // elements per page - private int index; // current page index - - public Pagination(int pageSize, List leaderboard, JDA jda, CurrencyType currencyType) { - this.pageSize = pageSize; - this.leaderboard = leaderboard; - this.jda = jda; - this.currencyType = currencyType; - this.index = 0; - } - - public void nextPage() { - index++; - if (index + 1 > pages()) { - index--; - } - } - - public void previousPage() { - index--; - if (index < 0) { - index = 0; - } - } - - public List getPage() { - int fromIndex = index == 0 ? 0 : index * pageSize; - int toIndex = index == 0 ? pageSize : index * pageSize + pageSize; - - // end of list reached, set toIndex to list size - if (toIndex > leaderboard.size()) { - toIndex = leaderboard.size(); - } - - return leaderboard.subList(fromIndex, toIndex).stream().map(this::format).collect(Collectors.toList()); - } - - public int size() { - return leaderboard.size(); - } - - public int index() { - return index; - } - - public int pages() { - return (int) Math.ceil((float) leaderboard.size() / (float) pageSize); - } - - private String format(Pageable pageable) { - User user = jda.getUserById(pageable.getUserId()); - switch (currencyType) { - case XP: - return String.format("%s#%s (%d XP)", user.getName(), user.getDiscriminator(), pageable.getCount(CurrencyType.XP)); - case COINS: - return String.format("%s#%s (%d Münzen)", user.getName(), user.getDiscriminator(), pageable.getCount(CurrencyType.COINS)); - case DIAMONDS: - return String.format("%s#%s (%d Diamanten)", user.getName(), user.getDiscriminator(), pageable.getCount(CurrencyType.DIAMONDS)); - case CONTEST: - return String.format("%s#%s (%d Votes)", user.getName(), user.getDiscriminator(), pageable.getCount(CurrencyType.CONTEST)); - default: - throw new IllegalArgumentException("Unsupported currency type!"); - } - } - - public enum CurrencyType { - XP, - COINS, - DIAMONDS, - CONTEST - } -} diff --git a/src/main/java/de/kaktushose/levelbot/util/Statistics.java b/src/main/java/de/kaktushose/levelbot/util/Statistics.java deleted file mode 100644 index 57e0071..0000000 --- a/src/main/java/de/kaktushose/levelbot/util/Statistics.java +++ /dev/null @@ -1,123 +0,0 @@ -package de.kaktushose.levelbot.util; - -import com.google.api.client.http.HttpRequestInitializer; -import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.google.api.services.youtube.YouTube; -import com.google.api.services.youtube.model.ChannelListResponse; -import com.google.api.services.youtube.model.ChannelStatistics; -import de.kaktushose.levelbot.bot.Levelbot; -import net.dv8tion.jda.api.OnlineStatus; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.utils.concurrent.Task; -import net.dv8tion.jda.internal.utils.concurrent.task.GatewayTask; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.text.NumberFormat; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -public class Statistics { - - private static final Logger log = LoggerFactory.getLogger(Statistics.class); - private final Levelbot levelbot; - private final String youtubeApiKey; - private int totalMemberCount; - private int onlineMemberCount; - private int lsMemberCount; - private int etsMemberCount; - private int notrufMemberCount; - private int omsiMemberCount; - private int wrsMemberCount; - private int gtaMemberCount; - private int boosterMemberCount; - private int boosterCount; - private int premiumMemberCount; - private String ytFollowerCount; - private String ytVideoCount; - private String ytViewCount; - private String boosterMemberList; - private String premiumMemberList; - - public Statistics(Levelbot levelbot, long guildId) { - this.levelbot = levelbot; - youtubeApiKey = levelbot.getSettingsService().getYoutubeApiKey(guildId); - } - - public Task queryStatistics() { - Guild guild = levelbot.getGuild(); - CompletableFuture future = new CompletableFuture<>(); - Task> reference = guild.loadMembers() - .onSuccess(members -> { - // general member count - totalMemberCount = members.size(); - onlineMemberCount = (int) members.stream().filter(member -> !member.getOnlineStatus().equals(OnlineStatus.OFFLINE)).count(); - // game count - lsMemberCount = getGameCount(members, "Farming Simulator"); - etsMemberCount = getGameCount(members, "Euro Truck Simulator"); - notrufMemberCount = getGameCount(members, "Notruf 112"); - omsiMemberCount = getGameCount(members, "OMSI"); - wrsMemberCount = getGameCount(members, "Winter Resort Simulator"); - gtaMemberCount = getGameCount(members, "Grand Theft Auto"); - // booster - List boosterList = members.stream().filter(member -> member.getTimeBoosted() != null).collect(Collectors.toList()); - boosterMemberCount = boosterList.size(); - StringBuilder boosters = new StringBuilder(); - boosterList.forEach(member -> boosters.append(member.getAsMention()).append(", ")); - boosterMemberList = boosters.length() > 1 ? boosters.substring(0, boosters.length() - 2) : ""; - boosterCount = guild.getBoostCount(); - // premium - List premiumMembers = members.stream() - .filter(member -> member.getRoles().contains(guild.getRolesByName("premium", true).get(0))) - .collect(Collectors.toList()); - premiumMemberCount = premiumMembers.size(); - StringBuilder premium = new StringBuilder(); - premiumMembers.forEach(member -> premium.append(member.getAsMention()).append(", ")); - premiumMemberList = premium.length() > 1 ? premium.substring(0, premium.length() - 2) : ""; - // yt - try { - NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.GERMANY); - ChannelStatistics channelStatistics = getYoutubeStatistics(); - ytFollowerCount = numberFormat.format(channelStatistics.getSubscriberCount().intValue()); - ytVideoCount = numberFormat.format(channelStatistics.getVideoCount().intValue()); - ytViewCount = numberFormat.format(channelStatistics.getViewCount().intValue()); - } catch (IOException e) { - log.error("Unable to query youtube follower count!", e); - } - future.complete(this); - }) - .onError(future::completeExceptionally); - return new GatewayTask<>(future, reference::cancel); - } - - private int getGameCount(List members, String name) { - int count = 0; - for (Member member : members) { - if (member.getActivities().isEmpty()) { - continue; - } - if (member.getActivities().get(0).getName().contains(name)) { - count++; - } - } - return count; - } - - private ChannelStatistics getYoutubeStatistics() throws IOException { - HttpRequestInitializer httpRequestInitializer = request -> { - }; - YouTube youTube = new YouTube.Builder(new NetHttpTransport(), new JacksonFactory(), httpRequestInitializer) - .setApplicationName("Levelbot") - .build(); - YouTube.Channels.List search = youTube.channels().list("statistics"); - search.setForUsername("nordrheintvplay"); - search.setKey(youtubeApiKey); - ChannelListResponse response = search.execute(); - return response.getItems().get(0).getStatistics(); - } -} diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF index 5c5daac..3702dff 100644 --- a/src/main/resources/META-INF/MANIFEST.MF +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -1,2 +1,2 @@ Manifest-Version: 1.0 -Main-Class: de.kaktushose.levelbot.Bootstrapper +Main-Class: com.github.kaktushose.nplaybot.Bootstrapper diff --git a/src/test/java/BoosterServiceTest.java b/src/test/java/BoosterServiceTest.java deleted file mode 100644 index 22c6d4f..0000000 --- a/src/test/java/BoosterServiceTest.java +++ /dev/null @@ -1,114 +0,0 @@ -import de.kaktushose.levelbot.Bootstrapper; -import de.kaktushose.levelbot.database.model.BotUser; -import de.kaktushose.levelbot.database.services.BoosterService; -import de.kaktushose.levelbot.database.services.UserService; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.jdbc.Sql; - -import java.util.NoSuchElementException; - -import static org.junit.jupiter.api.Assertions.*; - -@SpringBootConfiguration -@SpringBootTest(classes = Bootstrapper.class) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@Sql(scripts = "classpath:truncate_all.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) -public class BoosterServiceTest { - - private BoosterService boosterService; - private UserService userService; - - @BeforeAll - public void setup() { - LevelbotMock levelbot = new LevelbotMock(); - boosterService = levelbot.getBoosterService(); - userService = levelbot.getUserService(); - } - - @Test - public void getAllNitroBoosters_WithoutUsers_ShouldReturnEmptyList() { - assertTrue( boosterService.getAllNitroBoosters().isEmpty()); - } - - @Test - public void getAllNitroBoosters_WithUsers_ShouldReturnNotEmptyList() { - boosterService.createNewNitroBooster(0); - boosterService.createNewNitroBooster(1); - boosterService.createNewNitroBooster(2); - assertEquals(3, boosterService.getAllNitroBoosters().size()); - } - - @Test - public void createNewNitroBooster_ShouldBeActiveBooster() { - boosterService.createNewNitroBooster(0); - assertTrue(boosterService.isNitroBooster(0)); - } - - @Test - public void isNitroBooster_WithoutUsers_ShouldReturnFalse() { - assertFalse(boosterService.isNitroBooster(0)); - } - - @Test - public void isNitroBooster_WithUsers_ShouldReturnTrue() { - boosterService.createNewNitroBooster(0); - assertTrue(boosterService.isNitroBooster(0)); - } - - @Test - public void isActiveNitroBooster_WithoutUsers_ShouldReturnFalse() { - assertFalse(boosterService.isActiveNitroBooster(0)); - } - - @Test - public void isActiveNitroBooster_WithUsers_ShouldReturnTrue() { - boosterService.createNewNitroBooster(0); - assertTrue(boosterService.isActiveNitroBooster(0)); - } - - @Test - public void changeNitroBoosterStatus_WithTrue_ShouldBeActiveBooster() { - boosterService.createNewNitroBooster(0); - boosterService.changeNitroBoosterStatus(0, true); - assertTrue(boosterService.isNitroBooster(0)); - } - - @Test - public void addMonthlyReward_ShouldAddCurrencies() { - userService.createUser(0); - String rewardText = boosterService.addMonthlyReward(0); - BotUser botUser = userService.getUserById(0); - assertEquals(50, botUser.getXp()); - assertEquals(50, botUser.getCoins()); - assertEquals(5, botUser.getDiamonds()); - assertFalse(rewardText.isEmpty()); - } - - @Test - public void addMonthlyReward_WithoutUsers_ShouldThrow() { - assertThrows(NoSuchElementException.class, () -> boosterService.addMonthlyReward(0)); - } - - @Test - public void addOneTime_ShouldAddCurrencies() { - userService.createUser(0); - String rewardText = boosterService.addOneTimeReward(0); - BotUser botUser = userService.getUserById(0); - assertEquals(250, botUser.getXp()); - assertEquals(250, botUser.getCoins()); - assertEquals(25, botUser.getDiamonds()); - assertFalse(rewardText.isEmpty()); - } - - @Test - public void addOneTimeReward_WithoutUsers_ShouldThrow() { - assertThrows(NoSuchElementException.class, () -> boosterService.addOneTimeReward(0)); - userService.createUser(0); - userService.createUser(1); - } -} diff --git a/src/test/java/LevelbotMock.java b/src/test/java/LevelbotMock.java deleted file mode 100644 index bfa9f19..0000000 --- a/src/test/java/LevelbotMock.java +++ /dev/null @@ -1,32 +0,0 @@ -import de.kaktushose.levelbot.bot.Levelbot; -import de.kaktushose.levelbot.database.services.BoosterService; -import de.kaktushose.levelbot.database.services.SettingsService; -import de.kaktushose.levelbot.database.services.UserService; - -public class LevelbotMock extends Levelbot { - - private final UserService userService; - private final SettingsService settingsService; - private final BoosterService boosterService; - - public LevelbotMock() { - this.userService = new UserService(); - this.settingsService = new SettingsServiceMock(); - this.boosterService = new BoosterService(this); - } - - @Override - public UserService getUserService() { - return userService; - } - - @Override - public SettingsService getSettingsService() { - return settingsService; - } - - @Override - public BoosterService getBoosterService() { - return boosterService; - } -} diff --git a/src/test/java/SettingsServiceMock.java b/src/test/java/SettingsServiceMock.java deleted file mode 100644 index 97cd3b2..0000000 --- a/src/test/java/SettingsServiceMock.java +++ /dev/null @@ -1,16 +0,0 @@ -import de.kaktushose.levelbot.database.model.Reward; -import de.kaktushose.levelbot.database.services.SettingsService; - -public class SettingsServiceMock extends SettingsService { - - @Override - public Reward getMonthlyNitroBoosterReward() { - return new Reward(0, 50, 50, 5, null, "Reward"); - } - - @Override - public Reward getOneTimeNitroBoosterReward() { - return new Reward(1, 250, 250, 25, null, "Reward"); - } - -} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties deleted file mode 100644 index 8cac730..0000000 --- a/src/test/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -spring.datasource.url = jdbc:h2:mem:levelbot; -spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect \ No newline at end of file diff --git a/src/test/resources/truncate_all.sql b/src/test/resources/truncate_all.sql deleted file mode 100644 index e50f396..0000000 --- a/src/test/resources/truncate_all.sql +++ /dev/null @@ -1,2 +0,0 @@ -TRUNCATE TABLE nitro_boosters; -DELETE FROM users; \ No newline at end of file diff --git a/welcomeEmbeds.json b/welcomeEmbeds.json deleted file mode 100644 index 074134c..0000000 --- a/welcomeEmbeds.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "logo": { - "title": "Offizieller NPLAY Discord", - "description": "https://discord.gg/nplay", - "thumbnail": { - "url": "https://cdn.discordapp.com/attachments/850466263216947252/970283398063079464/nplay_logo_standard.jpg" - }, - "image": { - "url": "https://cdn.discordapp.com/attachments/850466263216947252/970283152163627008/Discord-LogoWordmark-White.png" - }, - "color": "#aeff00" - }, - "welcome": { - "title": "Der Simulatoren Discord", - "description":"Mit unserem NPLAY Discord-Server wollen wir Dir neue Möglichkeiten bieten: Diskutiere über die Welt der Simulatoren, tausche Dich über die neuesten Videos auf dem Kanal aus, nimm an exklusiven Gewinnspielen teil und finde andere Gamer zum gemeinsamen Zocken!\n\nUnd das alles auf einer modernen Plattform in angenehmer Atmosphäre.\n\nDie folgenden Informationen sollen Dir dabei helfen, dich auf dem Server schnell und unkompliziert zurechtzufinden.\n\nSollten dennoch Fragen offenbleiben, steht Dir unser Serverteam jederzeit zur Verfügung.\n\nWir wünschen Dir viel Spaß beim Chatten und Unterhalten!\nDein NPLAY Team", - "author": { - "name": "NPLAY", - "icon_url": "https://media.discordapp.net/attachments/510209791214223380/659861897360703488/regular_picture.png" - }, - "image":{ - "url":"https://cdn.discordapp.com/attachments/386959481558401025/960906185215733781/NPLAY_Entwurf.jpg" - }, - "color": "#aeff00" - }, - "statistics": { - "title": "Fehler!", - "description": "Da ist etwas schiefgelaufen. Bitte kontaktiere <@393843637437464588>!", - "color": "#aa0c14" - }, - "team": { - "title": "Serverteam", - "description": "Wir sind jederzeit für Dich da!\n\n<@278591435417059328> | Inhaber\n<@307973135746072578> | Serverleitung\n\n<@393843637437464588> | Moderation, Entwickler des Level-Systems\n<@315877643616649220> | Moderation\n<@522867524484726785> | Moderation\n<@142670727236026368> | Moderation\n<@427782609058398229> | Moderation\n\n*Bei Fragen oder anderen Anliegen freuen wir uns auf Deine Nachricht\nim Support oder via PN.*", - "color": "#aeff00" - }, - "categories": { - "title": "Serverkategorien", - "description":"Der Server ist in 4 übersichtliche Kategorien aufgeteilt:\n\n**- Informationen:**\nHier findest du alle wichtigen Kanäle zu den organisatorischen Aspekten des Servers sowie den Support.\n\n**- Projekte:**\nTausche Dich hier mit anderen Zuschauern über die Entwicklung des NPLAY YouTube-Kanals und dessen aktuelle Projekte aus.\n\n**- Simulatoren:**\nDiskutiere in den Spiele-Kanälen über Updates, teile deine Bilder und schönste Spielerlebnisse, oder erhalte Support. In <#606645000687583236> finden alle Spiele Platz, für die es keinen eigenen Kanal gibt!\n\n**- Community:**\nDer Bereich des Servers, in welchem alles abseits der Simulatoren diskutiert werden kann: Im <#575918262689333259> können interessante News gepostet und im <#386277336406032385> kann ausgelassen geplaudert werden. Die besten Beiträge des Servers sind jederzeit im <#690664642623701012> zu finden!\n\n*Zudem bieten wir einen Unbegrenzten Talk sowie einen 2 User Talk an.\nBei Bedarf können jederzeit weitere Kanäle eröffnet werden.*", - "color": "#aeff00" - }, - "rules": { - "title": "Serverregeln", - "description":"*Wir haben ein paar Regeln aufgestellt, um auf dem Server eine angenehme Diskussionskultur zu wahren. Mit dem Serverbeitritt akzeptierst Du die Einhaltung dieser Regeln.*", - "fields": [ - { - "name":"1. ALLGEMEINES", - "value":"a) Sei höflich zu anderen Nutzern auf dem Server – nutze ihn so, dass sich jeder in angenehmer Atmosphäre unterhalten kann! \nb) Den Anweisungen des Serverteams ist immer Folge zu leisten.\nc) Das mehrmalige Verlassen und Wiederbetreten des Servers ist nicht erlaubt.\nd) Private Daten wie Telefonnummern, Adressen, Passwörter usw. dürfen nicht öffentlich ausgetauscht werden.", - "inline":false - }, - { - "name":"2. CHATREGELN", - "value":"a) Jegliche Formen von Beleidigung, Missachtung, Diskriminierung, Hetze, Rassismus etc. gegenüber anderen Usern und Dritten sind in den Text- und Sprachkanälen strengstens untersagt.\nb) Werbung für eigene oder fremde Inhalte ist auf dem Server untersagt. Auch Werbung per Privatnachricht an Mitglieder des Servers ist verboten (Ausnahme: Inhalte von NPLAY). Eine Genehmigung kann in Ausnahmefällen bei der Serverleitung angefragt werden. Werbeverbote gelten insbesondere für fragwürdige Shops, wie MMOGA, G2A, Kinguin usw.\nc) In den Chats sind nur Inhalte erwünscht, die der Kanalbeschreibung entsprechen. Trolling, Spamming sowie die ausnahmslose Verwendung von Caps oder Emojis sind verboten. Das gilt auch für Privatnachrichten.\nd) Beiträge sind in der Regel auf Deutsch zu verfassen. Kurze Diskussionsstränge in anderen Sprachen sind zulässig.\ne) Für die Sprachkanäle gilt zudem: Keine Aufnahme von Gesprächen, keine Stimmverzerrer, Soundboards nur mit Einverständnis der anderen User. Kein Channel-Hopping.", - "inline":false - }, - { - "name":"3. IDENTITÄT", - "value":"a) Nur ein Account je Person auf dem Server erlaubt. Doppel-Accounts werden gesperrt! \nb) Der Nickname darf nicht zur Verwechslung mit anderen Persönlichkeiten führen und keine falsche Identität vortäuschen. Er darf keine Kürzel (z.b:[xyz]) enthalten, nicht nur aus einzelnen Zeichen bestehen (z.B. „!“ ) und muss aus lateinischen Schriftzeichen zusammengesetzt sein (keine Schnörkelschrift o.ä.). User mit anstößigen Namen werden gesperrt!\nc) Nicknameänderungen sind nach Möglichkeit zu vermeiden. Sie erfolgen im Falle eines Regelverstoßes durch das Serverteam. User, die ihren Nicknamen nicht selbstständig ändern können, fragen für eine Änderung das Serverteam. \n\n*Das Kopieren unserer Fassung der Serverregeln ist untersagt. Bei entsprechenden Zwischenfällen behalten wir uns einen Ausschluss vom Server vor.*", - "inline":false - } - ], - "color": "#aeff00" - }, - "rule violations": { - "title": "Regelverstöße", - "description":":bangbang: Im Falle eines Verstoßes müssen wir Maßnahmen ergreifen. Diese hängen von vielen individuellen Umständen ab: Wie schwer wiegt der Regelbruch? Ist der User bereits auffällig geworden? Und vor allem: Was muss getan werden, um auf dem Server eine angenehme Diskussionskultur zu bewahren? \n\nHäufig gehen wir dabei wie folgt vor:\n\n__1+ 2. Verstoß:__ **Warnung** über den Carl-Bot mit Hinweis auf das Fehlverhalten, ggf. klärendes Gespräch\n__Ab 3. Verstoß:__ **Mute für bis zu 30 Tage** über den Carl-Bot mit Angabe des Grundes\n__Spätestens ab 5. Verstoß:__ **Temporärer oder dauerhafter Bann** über den Carl-Bot mit Angabe des Grundes\n\nDiese Reihenfolge ist unverbindlich. So erfolgt beispielsweise bei einer schweren Beleidigung eines anderen Servermitglieds selbstverständlich direkt ein dauerhafter Bann, während das fünfmalige Verwenden von Caps höchstens zu einem Mute führt.\n\nBei Fragen dazu wende Dich gerne an das Moderationsteam.", - "color": "#aeff00" - }, - "links": { - "title": "Nützliche Links", - "fields": [ - { - "name": "Youtube", - "value": "https://bit.ly/2QkZHA6", - "inline": true - }, - { - "name": "Facebook-Seite:", - "value": "https://bit.ly/39i2jqS", - "inline": true - }, - { - "name": "Facebook-Gruppe:", - "value": "https://bit.ly/37bEzD1", - "inline": true - }, - { - "name": "Instagram:", - "value": "https://bit.ly/2EVMvwf", - "inline": true - }, - { - "name": "Twitter:", - "value": "https://bit.ly/35YgkYQ", - "inline": true - }, - { - "name": "Twitch:", - "value": "https://bit.ly/2tOJOtY", - "inline": true - }, - { - "name": "Website:", - "value": "https://nplay.de/", - "inline": true - }, - { - "name": "Nitrado:", - "value": "https://bit.ly/LSServerNitrado", - "inline": true - }, - { - "name": "Discord:", - "value": "https://discord.gg/nplay", - "inline": true - } - ], - "color": "#aeff00" - }, - "levelSystem": { - "title": "Level-System", - "description": "Unser selbst entwickeltes, abwechslungsreiches Level-System belohnt Dich mit tollen Geschenken, wie gratis Keys für aktuelle Simulatoren!\n\nDu hast NITRO? Booste unseren Server und erhalte ein tolles Supportpaket, unter anderem mit einer dauerhaften PREMIUM-Rolle!\n\nSchau dich im Kanal <#851388807239827466> um und erfahre, wie der Bot funktioniert!\nSteuere den Bot mit einfachen Befehlen in <#665625766444138537>.\n\nSchau auf unserem Entwickler-Discord vorbei, um auch über die vielen neuen Erweiterungen in der Zukunft informiert zu bleiben!\nhttps://discord.gg/JYWezvQ", - "image": { - "url": "https://media.discordapp.net/attachments/487343298910879767/668939091772964904/BannerHQ.png" - }, - "color": "#aeff00" - }, - "support": { - "title": "Support", - "description":"- In<#386279631189311500> helfen wir Dir bei Problemen und Fragen rund um den Discord-Server. Außerdem freuen wir uns jederzeit über Deine Wünsche - wir behalten die Zukunft im Auge und wollen das User-Erlebnis weiter verbessern.\n\n- Alle Fragen rund um Gaming und den Kanal NPLAY bitte in den entsprechenden Spiele-Kanälen in \"Simulatoren\" bzw. in <#459805064840740864> stellen und dort mit anderen Usern austauschen!", - "color": "#aeff00" - } -} From 1a0310c7b00a673fb224062316ca8ad7728454fe Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:09:51 +0100 Subject: [PATCH 02/66] dockerize bot --- .env.example | 7 +++++++ .gitignore | 2 ++ Dockerfile | 13 +++++++++++++ docker-compose.yml | 41 +++++++++++++++++++++++++++++++++++++++++ pom.xml | 1 + 5 files changed, 64 insertions(+) create mode 100644 .env.example create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d446086 --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +MYSQL_ROOT_PASSWORD=password +MYSQL_DATABASE=nplaybot +MYSQL_USER=bot +MYSQL_PASSWORD=password +GF_SECURITY_ADMIN_PASSWORD=password +BOT_TOKEN=token +BOT_PRODUCTION=true diff --git a/.gitignore b/.gitignore index 8e84f37..017595c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ buildNumber.properties .mvn/wrapper/maven-wrapper.jar ### Project ### +.env +/data diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2eb96a0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM maven:3.9.5-amazoncorretto-21-debian AS builder + +WORKDIR /bot + +COPY . . + +RUN mvn clean package -DskipTests + +FROM openjdk:21 + +COPY --from=builder /bot/target/NPLAY-Bot.jar ./NPLAY-Bot.jar + +CMD ["java", "-jar", "NPLAY-Bot.jar"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..de23645 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +version: "3" + +services: + mariadb: + image: mariadb + restart: always + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + volumes: + - ./data/db:/var/lib/mysql + ports: + - "3306:3306" + + grafana: + image: grafana/grafana + restart: always + environment: + GF_SECURITY_ADMIN_PASSWORD: ${GF_SECURITY_ADMIN_PASSWORD} + ports: + - "3000:3000" + depends_on: + - mariadb + + bot: + build: + context: . + dockerfile: Dockerfile + image: nplay-bot + restart: on-failure + environment: + BOT_TOKEN: ${BOT_TOKEN} + BOT_PRODUCTION: ${BOT_PRODUCTION} + DB_USER: ${MYSQL_USER} + DB_PASSWORD: ${MYSQL_PASSWORD} + ports: + - "8080:8080" + depends_on: + - mariadb diff --git a/pom.xml b/pom.xml index 5912a45..e295810 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ + NPLAY-Bot org.apache.maven.plugins From 08a103f13a96b0bcb537d28f4a817b069b04715f Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:26:26 +0100 Subject: [PATCH 03/66] update workflows --- .github/dependabot.yml | 17 ----------------- .github/workflows/deploy.yml | 33 +++++++++++++++++++++++---------- .github/workflows/maven.yml | 8 ++++---- 3 files changed, 27 insertions(+), 31 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 00c8e3b..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,17 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "maven" # See documentation for possible values - directory: "/" # Location of package manifests - target-branch: "dev" - schedule: - interval: "weekly" - ignore: - - dependency-name: "jda*" - update-types: ["version-update:semver-major"] - - dependency-name: "*" - update-types: ["version-update:semver-patch"] diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a40ec11..ad0fe2b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,20 +2,33 @@ name: Deploy on: push: - branches: [ master ] + branches: [ rewrite ] workflow_dispatch: jobs: deploy: - runs-on: ubuntu-latest - steps: - - name: Execute build script via ssh - uses: appleboy/ssh-action@master + - name: Build Image + id: build-image + uses: redhat-actions/buildah-build@v2 + with: + image: nplaybot + tags: latest ${{ github.sha }} + containerfiles: | + ./Dockerfile + oci: true + + - name: Push To ghcr.io + id: push-to-ghcr + uses: redhat-actions/push-to-registry@v2 with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USERNAME }} - key: ${{ secrets.SSH_KEY }} - port: ${{ secrets.SSH_PORT }} - script: nohup bash /home/levelbot/scripts/workflow.sh > /home/levelbot/scripts/nohup.log 2>&1 & tail -f /home/levelbot/scripts/nohup.log & + image: ${{ steps.build-image.outputs.image }} + tags: ${{ steps.build-image.outputs.tags }} + registry: ghcr.io/kaktushose/levelbot + username: Kaktushose + password: ${{ github.token }} + extra-args: | + --disable-content-trust + - name: Print image url + run: echo "Image pushed to ${{ steps.push-to-ghcr.outputs.registry-paths }}" diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index c1218cf..8b8a2d2 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -2,9 +2,9 @@ name: Java CI on: push: - branches: [ master, dev ] + branches: [ main, development, rewrite ] pull_request: - branches: [ master, dev ] + branches: [ main, development, rewrite ] jobs: build: @@ -13,9 +13,9 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 21 - name: Build with Maven run: mvn -B package --file pom.xml From 495a68e321c4a0c399c9f27047daf90c6e0811c0 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:36:11 +0100 Subject: [PATCH 04/66] fix deploy workflow --- .github/workflows/deploy.yml | 1 + README.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ad0fe2b..f2ca6eb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,6 +9,7 @@ jobs: deploy: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v2 - name: Build Image id: build-image uses: redhat-actions/buildah-build@v2 diff --git a/README.md b/README.md index a09f7d3..6aae679 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Java CI](https://github.com/Kaktushose/Levelbot/actions/workflows/maven.yml/badge.svg)](https://github.com/Kaktushose/Levelbot/actions/workflows/maven.yml) -[![Deploy](https://github.com/Kaktushose/Levelbot/actions/workflows/deploy.yml/badge.svg)](https://github.com/Kaktushose/Levelbot/actions/workflows/deploy.yml) +[![Java CI](https://github.com/Kaktushose/Levelbot/actions/workflows/maven.yml/badge.svg?branch=rewrite)](https://github.com/Kaktushose/Levelbot/actions/workflows/maven.yml) +[![Deploy](https://github.com/Kaktushose/Levelbot/actions/workflows/deploy.yml/badge.svg?branch=rewrite)](https://github.com/Kaktushose/Levelbot/actions/workflows/deploy.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/6eaa0127de99428795b4f5f759da188a)](https://www.codacy.com/gh/Kaktushose/Levelbot/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Kaktushose/Levelbot&utm_campaign=Badge_Grade) ![Generic badge](https://img.shields.io/badge/Version-2.3.0-86c240".svg) [![license-shield](https://img.shields.io/badge/License-Apache%202.0-lightgrey.svg)]() From d803339c67984add4841b24e4fbb215d6afad340 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:39:38 +0100 Subject: [PATCH 05/66] fix deploy workflow --- .github/workflows/deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f2ca6eb..864b946 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,6 +10,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + ref: ${{ github.ref }} - name: Build Image id: build-image uses: redhat-actions/buildah-build@v2 From 62db8c035969314c0c291e09012f6391db3df868 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:03:49 +0100 Subject: [PATCH 06/66] update compose file to use ghcr --- docker-compose.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index de23645..10de536 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,10 +25,7 @@ services: - mariadb bot: - build: - context: . - dockerfile: Dockerfile - image: nplay-bot + image: ghcr.io/kaktushose/levelbot/nplaybot:latest restart: on-failure environment: BOT_TOKEN: ${BOT_TOKEN} From cca5ae3cbb0f364dce310bee43db526862a85ba7 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:04:41 +0100 Subject: [PATCH 07/66] use different port for mariadb while in rewrite --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 10de536..51ee909 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,7 @@ services: volumes: - ./data/db:/var/lib/mysql ports: - - "3306:3306" + - "3307:3307" grafana: image: grafana/grafana From 70a77b8f4fb5da0e9fc22bdae5fb9d7ed659c9f2 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:47:07 +0100 Subject: [PATCH 08/66] bootstrap jda bot --- docker-compose.yml | 2 -- .../kaktushose/nplaybot/Bootstrapper.java | 14 +++++++- .../com/github/kaktushose/nplaybot/Bot.java | 33 +++++++++++++++++++ .../nplaybot/HelloWorldCommand.java | 14 ++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/Bot.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/HelloWorldCommand.java diff --git a/docker-compose.yml b/docker-compose.yml index 51ee909..acdf496 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,7 +32,5 @@ services: BOT_PRODUCTION: ${BOT_PRODUCTION} DB_USER: ${MYSQL_USER} DB_PASSWORD: ${MYSQL_PASSWORD} - ports: - - "8080:8080" depends_on: - mariadb diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java index 7879bd1..4153a44 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java @@ -9,6 +9,18 @@ public class Bootstrapper { private final static Logger log = LoggerFactory.getLogger(Bootstrapper.class); public static void main(String[] args) { - log.info("Hello World!"); + long startTime = System.currentTimeMillis(); + try { + log.info("Starting NPLAY-Bot..."); + + Bot bot = Bot.start(System.getenv("BOT_TOKEN")); + Thread.setDefaultUncaughtExceptionHandler((t, e) -> log.error("An uncaught exception has occurred!", e)); + Runtime.getRuntime().addShutdownHook(new Thread(bot::shutdown)); + + log.info("Successfully started NPLAY-Bot! Took {} ms", System.currentTimeMillis() - startTime); + } catch (InterruptedException e) { + log.error("Failed to start!", e); + System.exit(1); + } } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java new file mode 100644 index 0000000..8e4d035 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -0,0 +1,33 @@ +package com.github.kaktushose.nplaybot; + +import com.github.kaktushose.jda.commands.JDACommands; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.OnlineStatus; +import net.dv8tion.jda.api.entities.Activity; + +public class Bot { + + private final JDA jda; + private final JDACommands jdaCommands; + + private Bot(String token) throws InterruptedException { + jda = JDABuilder.createDefault(token) + .setActivity(Activity.customStatus("starting...")) + .setStatus(OnlineStatus.IDLE) + .build().awaitReady(); + + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); + + jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); + } + + public static Bot start(String token) throws InterruptedException { + return new Bot(token); + } + + public void shutdown() { + jdaCommands.shutdown(); + jda.shutdown(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/HelloWorldCommand.java b/src/main/java/com/github/kaktushose/nplaybot/HelloWorldCommand.java new file mode 100644 index 0000000..523d913 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/HelloWorldCommand.java @@ -0,0 +1,14 @@ +package com.github.kaktushose.nplaybot; + +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; + +@Interaction +public class HelloWorldCommand { + + @SlashCommand("hello") + public void onCommand(CommandEvent event) { + event.reply("Hello World!"); + } +} From fb6d2b3f265adcabc84c88140e3f501b5dba7b66 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 22:50:33 +0100 Subject: [PATCH 09/66] add logs to docker --- .github/workflows/deploy.yml | 2 +- .github/workflows/maven.yml | 2 +- docker-compose.yml | 2 ++ src/main/resources/logback.xml | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 864b946..6aebffc 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,7 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: ref: ${{ github.ref }} - name: Build Image diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 8b8a2d2..6c592df 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 21 uses: actions/setup-java@v1 with: diff --git a/docker-compose.yml b/docker-compose.yml index acdf496..ff02634 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,5 +32,7 @@ services: BOT_PRODUCTION: ${BOT_PRODUCTION} DB_USER: ${MYSQL_USER} DB_PASSWORD: ${MYSQL_PASSWORD} + volumes: + - ./data/logs:/bot/logs depends_on: - mariadb diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 65cb6dd..8022665 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -8,7 +8,7 @@ - ./levelbot.log + ./logs/bot.log %date [%t] %level %logger - %msg%n From 25fa18f310f4ab759a7a0383cb43758438cf1166 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 22:56:59 +0100 Subject: [PATCH 10/66] test deployment --- src/main/java/com/github/kaktushose/nplaybot/Bot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 8e4d035..983c61a 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -17,7 +17,7 @@ private Bot(String token) throws InterruptedException { .setStatus(OnlineStatus.IDLE) .build().awaitReady(); - jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.1")); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); } From c986edc45621cd352007cb0ec8248071e1234023 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 23:03:42 +0100 Subject: [PATCH 11/66] test deployment --- src/main/java/com/github/kaktushose/nplaybot/Bot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 983c61a..1a26986 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -17,7 +17,7 @@ private Bot(String token) throws InterruptedException { .setStatus(OnlineStatus.IDLE) .build().awaitReady(); - jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.1")); + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.2")); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); } From bcdcf7f1080ba85c7622e0185744893132d48f82 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 23:45:39 +0100 Subject: [PATCH 12/66] test deployment --- .github/workflows/deploy.yml | 2 +- src/main/java/com/github/kaktushose/nplaybot/Bot.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6aebffc..5b0c957 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,7 +20,7 @@ jobs: tags: latest ${{ github.sha }} containerfiles: | ./Dockerfile - oci: true + oci: false - name: Push To ghcr.io id: push-to-ghcr diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 1a26986..6ca1c7c 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -17,7 +17,7 @@ private Bot(String token) throws InterruptedException { .setStatus(OnlineStatus.IDLE) .build().awaitReady(); - jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.2")); + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.3")); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); } From 6df2e1ced89408db194c1dc112b736413796e1ef Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 27 Nov 2023 23:51:07 +0100 Subject: [PATCH 13/66] test deployment --- src/main/java/com/github/kaktushose/nplaybot/Bot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 6ca1c7c..f06cb45 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -17,7 +17,7 @@ private Bot(String token) throws InterruptedException { .setStatus(OnlineStatus.IDLE) .build().awaitReady(); - jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.3")); + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.4")); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); } From 44f441a028e0b2172f863ccfa60245ad4732cba7 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 30 Nov 2023 22:17:41 +0100 Subject: [PATCH 14/66] add flyway for database migrations --- .env.example | 2 +- Dockerfile | 2 ++ docker-compose.yml | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index d446086..05840f9 100644 --- a/.env.example +++ b/.env.example @@ -4,4 +4,4 @@ MYSQL_USER=bot MYSQL_PASSWORD=password GF_SECURITY_ADMIN_PASSWORD=password BOT_TOKEN=token -BOT_PRODUCTION=true +MYSQL_URL=jdbc:mariadb://localhost:3306/DB diff --git a/Dockerfile b/Dockerfile index 2eb96a0..7e951de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,4 +10,6 @@ FROM openjdk:21 COPY --from=builder /bot/target/NPLAY-Bot.jar ./NPLAY-Bot.jar +COPY src/main/resources/db/migration ./db/migration + CMD ["java", "-jar", "NPLAY-Bot.jar"] diff --git a/docker-compose.yml b/docker-compose.yml index ff02634..9ac060e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,7 @@ version: "3" services: mariadb: image: mariadb + container_name: nplay-database restart: always environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} @@ -12,10 +13,11 @@ services: volumes: - ./data/db:/var/lib/mysql ports: - - "3307:3307" + - "3307:3306" grafana: image: grafana/grafana + container_name: nplay-grafana restart: always environment: GF_SECURITY_ADMIN_PASSWORD: ${GF_SECURITY_ADMIN_PASSWORD} @@ -24,8 +26,23 @@ services: depends_on: - mariadb + flyway: + image: redgate/flyway + container_name: nplay-flyway + restart: on-failure + environment: + FLYWAY_URL: ${MYSQL_URL} + FLYWAY_USER: ${MYSQL_USER} + FLYWAY_PASSWORD: ${MYSQL_PASSWORD} + volumes: + - db-migrations:/db/migration + command: -locations=filesystem:/db/migration migrate + depends_on: + - mariadb + bot: image: ghcr.io/kaktushose/levelbot/nplaybot:latest + container_name: nplay-bot restart: on-failure environment: BOT_TOKEN: ${BOT_TOKEN} @@ -33,6 +50,20 @@ services: DB_USER: ${MYSQL_USER} DB_PASSWORD: ${MYSQL_PASSWORD} volumes: - - ./data/logs:/bot/logs + - db-migrations:/db/migration + - ./data/logs:/logs depends_on: - mariadb + + watchtower: + image: containrrr/watchtower + container_name: nplay-watchtower + environment: + TZ: Europe/Berlin + WATCHTOWER_CLEANUP: true + WATCHTOWER_POLL_INTERVAL: 30 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + +volumes: + db-migrations: From 1ce5f49ee17e8f8d7f58320f479da7d282be0797 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:30:57 +0100 Subject: [PATCH 15/66] update logback file policy --- .gitignore | 1 + src/main/resources/logback.xml | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 017595c..4ac50ea 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ buildNumber.properties ### Project ### .env /data +/logs diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 8022665..efed044 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,5 +1,4 @@ - @@ -7,11 +6,16 @@ - - ./logs/bot.log + + ./logs/nplaybot.log - %date [%t] %level %logger - %msg%n + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + ./logs/nplaybot.%d{yyyy-MM-dd}.log + 30 + From 6e219a5bafa694c49af3c0130d66d4888da5a522 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sun, 3 Dec 2023 20:15:03 +0100 Subject: [PATCH 16/66] first pass on leveling system --- pom.xml | 10 +++ .../kaktushose/nplaybot/Bootstrapper.java | 4 +- .../com/github/kaktushose/nplaybot/Bot.java | 12 +++- .../github/kaktushose/nplaybot/Database.java | 34 ++++++++++ .../nplaybot/HelloWorldCommand.java | 14 ---- .../nplaybot/rank/MessageListener.java | 4 ++ .../kaktushose/nplaybot/rank/RankService.java | 65 +++++++++++++++++++ .../nplaybot/rank/UserStatisticsTask.java | 14 ++++ .../rank/commands/RankInfoCommand.java | 4 ++ .../rank/commands/SetRankCommand.java | 4 ++ .../rank/commands/SwitchDailyCommand.java | 4 ++ .../kaktushose/nplaybot/rank/model/Rank.java | 4 ++ .../nplaybot/rank/model/UserInfo.java | 3 + .../nplaybot/scheduler/ScheduledTask.java | 4 ++ .../nplaybot/scheduler/TaskScheduler.java | 5 ++ .../migration/V1.0.0__setup_rank_system.sql | 35 ++++++++++ 16 files changed, 202 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/Database.java delete mode 100644 src/main/java/com/github/kaktushose/nplaybot/HelloWorldCommand.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/MessageListener.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/UserStatisticsTask.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/commands/SetRankCommand.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/commands/SwitchDailyCommand.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/model/Rank.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/scheduler/ScheduledTask.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java create mode 100644 src/main/resources/db/migration/V1.0.0__setup_rank_system.sql diff --git a/pom.xml b/pom.xml index e295810..2cbf584 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,16 @@ jda-commands 4.0.0-beta.1 + + com.zaxxer + HikariCP + 5.1.0 + + + org.mariadb.jdbc + mariadb-java-client + 3.3.1 + org.slf4j slf4j-api diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java index 4153a44..f842ac9 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java @@ -13,12 +13,12 @@ public static void main(String[] args) { try { log.info("Starting NPLAY-Bot..."); - Bot bot = Bot.start(System.getenv("BOT_TOKEN")); + var bot = Bot.start(System.getenv("BOT_TOKEN")); Thread.setDefaultUncaughtExceptionHandler((t, e) -> log.error("An uncaught exception has occurred!", e)); Runtime.getRuntime().addShutdownHook(new Thread(bot::shutdown)); log.info("Successfully started NPLAY-Bot! Took {} ms", System.currentTimeMillis() - startTime); - } catch (InterruptedException e) { + } catch (Exception e) { log.error("Failed to start!", e); System.exit(1); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index f06cb45..f098d0e 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -10,14 +10,21 @@ public class Bot { private final JDA jda; private final JDACommands jdaCommands; + private final Database database; + + private Bot(String token) throws InterruptedException, RuntimeException { + try { + database = new Database(); + } catch (Exception e) { + throw new RuntimeException("Unable to connect to database!", e); + } - private Bot(String token) throws InterruptedException { jda = JDABuilder.createDefault(token) .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) .build().awaitReady(); - jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.4")); + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); } @@ -29,5 +36,6 @@ public static Bot start(String token) throws InterruptedException { public void shutdown() { jdaCommands.shutdown(); jda.shutdown(); + database.closeDataSource(); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java new file mode 100644 index 0000000..fc9804f --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -0,0 +1,34 @@ +package com.github.kaktushose.nplaybot; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import java.sql.Connection; +import java.sql.SQLException; + +public class Database { + + private final HikariDataSource dataSource; + + public Database() { + var config = new HikariConfig(); + + config.setJdbcUrl(System.getenv("MYSQL_URL")); + config.setUsername(System.getenv("MYSQL_USER")); + config.setPassword(System.getenv("MYSQL_PASSWORD")); + config.addDataSourceProperty("databaseName", System.getenv("MYSQL_DATABASE")); + + dataSource = new HikariDataSource(config); + } + + public Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + public void closeDataSource() { + if (dataSource != null) { + dataSource.close(); + } + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/HelloWorldCommand.java b/src/main/java/com/github/kaktushose/nplaybot/HelloWorldCommand.java deleted file mode 100644 index 523d913..0000000 --- a/src/main/java/com/github/kaktushose/nplaybot/HelloWorldCommand.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.kaktushose.nplaybot; - -import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; -import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; -import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; - -@Interaction -public class HelloWorldCommand { - - @SlashCommand("hello") - public void onCommand(CommandEvent event) { - event.reply("Hello World!"); - } -} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/MessageListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/MessageListener.java new file mode 100644 index 0000000..45aa28a --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/MessageListener.java @@ -0,0 +1,4 @@ +package com.github.kaktushose.nplaybot.rank; + +public class MessageListener { +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java new file mode 100644 index 0000000..68b7e6f --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -0,0 +1,65 @@ +package com.github.kaktushose.nplaybot.rank; + +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.rank.model.Rank; +import com.github.kaktushose.nplaybot.rank.model.UserInfo; + +import java.sql.Connection; +import java.sql.SQLException; + +public class RankService { + + private final Database database; + + public RankService(Database database) { + this.database = database; + } + + public UserInfo getUserInfo(long userId) { + try (Connection connection = database.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT xp, rank, message_count, start_xp, role_id, color, bound + FROM users JOIN ranks + WHERE user_id = ? AND rank_id = rank + """ + ); + statement.setLong(1, userId); + + var result = statement.executeQuery(); + result.next(); + var currentRank = new Rank( + result.getLong("role_id"), + result.getString("color"), + result.getInt("bound") - result.getInt("xp") + ); + + statement = connection.prepareStatement(""" + SELECT role_id, color, bound + FROM ranks + WHERE rank_id = ? + """ + ); + statement.setLong(1, result.getInt("rank") + 1); + result = statement.executeQuery(); + Rank nextRank = null; + if (result.next()) { + nextRank = new Rank( + result.getLong("role_id"), + result.getString("color"), + result.getInt("bound") - result.getInt("xp") + ); + } + + return new UserInfo( + result.getInt("xp"), + currentRank, + nextRank, + result.getInt("message_count"), + result.getInt("xp") - result.getInt("start_xp") + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/UserStatisticsTask.java b/src/main/java/com/github/kaktushose/nplaybot/rank/UserStatisticsTask.java new file mode 100644 index 0000000..7f475d4 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/UserStatisticsTask.java @@ -0,0 +1,14 @@ +package com.github.kaktushose.nplaybot.rank; + +import com.github.kaktushose.nplaybot.scheduler.ScheduledTask; + +import java.util.function.Consumer; + +@ScheduledTask +public class UserStatisticsTask implements Consumer { + + @Override + public void accept(RankService rankService) { + + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java new file mode 100644 index 0000000..7a3077c --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -0,0 +1,4 @@ +package com.github.kaktushose.nplaybot.rank.commands; + +public class RankInfoCommand { +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SetRankCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SetRankCommand.java new file mode 100644 index 0000000..4934346 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SetRankCommand.java @@ -0,0 +1,4 @@ +package com.github.kaktushose.nplaybot.rank.commands; + +public class SetRankCommand { +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SwitchDailyCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SwitchDailyCommand.java new file mode 100644 index 0000000..a1eca88 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SwitchDailyCommand.java @@ -0,0 +1,4 @@ +package com.github.kaktushose.nplaybot.rank.commands; + +public class SwitchDailyCommand { +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/Rank.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/Rank.java new file mode 100644 index 0000000..e9a257e --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/Rank.java @@ -0,0 +1,4 @@ +package com.github.kaktushose.nplaybot.rank.model; + +public record Rank(long roleId, String color, int missingXp) { +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java new file mode 100644 index 0000000..be8816f --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java @@ -0,0 +1,3 @@ +package com.github.kaktushose.nplaybot.rank.model; + +public record UserInfo(int xp, Rank currentRank, Rank nextRank, int messageCount, int xpGain) { } diff --git a/src/main/java/com/github/kaktushose/nplaybot/scheduler/ScheduledTask.java b/src/main/java/com/github/kaktushose/nplaybot/scheduler/ScheduledTask.java new file mode 100644 index 0000000..6e943c0 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/scheduler/ScheduledTask.java @@ -0,0 +1,4 @@ +package com.github.kaktushose.nplaybot.scheduler; + +public @interface ScheduledTask { +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java new file mode 100644 index 0000000..4718ffc --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java @@ -0,0 +1,5 @@ +package com.github.kaktushose.nplaybot.scheduler; + +public class TaskScheduler { +// TODO add via annotations +} diff --git a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql new file mode 100644 index 0000000..f349f0a --- /dev/null +++ b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql @@ -0,0 +1,35 @@ +create table guild_settings ( + guild_id bigint(20) not null primary key, + bot_token varchar(255) not null, + bot_channel_id bigint(20) not null, + log_channel_id bigint(20) not null, + message_cooldown int(11) not null +); +create table users ( + user_id bigint(20) not null primary key, + permission_level int(11) not null, + daily_message boolean not null, + xp int(11) not null, + rank int(11) not null, + last_valid_message bigint(20) not null, + message_count bigint(20) not null, + start_xp int(11) not null, +); +create table ranks ( + rank_id int(11) not null primary key, + name varchar(255) not null, + color varchar(255) not null, + bound int(11) not null, + role_id bigint(20) not null +); +create table xp_chances ( + amount int (11) not null primary key, + chance int(11) not null +); +create table rank_statistics ( + timestamp bigint(20) not null primary key, + total_message_count int(11) not null, + valid_message_count int (11) not null, + total_xp_gain int(11) not null, + total_rank_ups int (11) not null +) From d77496ad9ab844aba59d189d8224024f555460dd Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:39:08 +0100 Subject: [PATCH 17/66] switch from postgres to mariadb --- .env.example | 11 ++-- .gitignore | 3 +- Dockerfile | 14 +++-- docker-compose.yml | 52 +++++++------------ entrypoint.sh | 9 ++++ pom.xml | 6 +-- .../kaktushose/nplaybot/Bootstrapper.java | 2 +- .../com/github/kaktushose/nplaybot/Bot.java | 8 +-- .../github/kaktushose/nplaybot/Database.java | 18 ++++--- 9 files changed, 65 insertions(+), 58 deletions(-) create mode 100644 entrypoint.sh diff --git a/.env.example b/.env.example index 05840f9..a4573c8 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,6 @@ -MYSQL_ROOT_PASSWORD=password -MYSQL_DATABASE=nplaybot -MYSQL_USER=bot -MYSQL_PASSWORD=password +POSTGRES_DB=database +POSTGRES_USER=user +POSTGRES_PASSWORD=password +POSTGRES_URL=jdbc:postgresql://postgres:5432/database GF_SECURITY_ADMIN_PASSWORD=password -BOT_TOKEN=token -MYSQL_URL=jdbc:mariadb://localhost:3306/DB +BOT_GUILD=0123456789 diff --git a/.gitignore b/.gitignore index 4ac50ea..0306ffc 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ buildNumber.properties .mvn/wrapper/maven-wrapper.jar ### Project ### -.env +*.env /data /logs +wait-for-it.sh diff --git a/Dockerfile b/Dockerfile index 7e951de..ec363af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,23 @@ +FROM redgate/flyway as flyway + +WORKDIR /flyway + + FROM maven:3.9.5-amazoncorretto-21-debian AS builder WORKDIR /bot - COPY . . - RUN mvn clean package -DskipTests FROM openjdk:21 +COPY --from=flyway /flyway ./flyway +COPY src/main/resources/db/migration ./db/migration + +COPY --from=builder /bot/*.sh . COPY --from=builder /bot/target/NPLAY-Bot.jar ./NPLAY-Bot.jar -COPY src/main/resources/db/migration ./db/migration +RUN chmod +x ./wait-for-it.sh ./entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] CMD ["java", "-jar", "NPLAY-Bot.jar"] diff --git a/docker-compose.yml b/docker-compose.yml index 9ac060e..ab11df0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,19 +1,18 @@ version: "3" services: - mariadb: - image: mariadb + postgres: + image: postgres container_name: nplay-database restart: always environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE: ${MYSQL_DATABASE} - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} volumes: - - ./data/db:/var/lib/mysql + - ./data/db:/var/lib/postgresql/data ports: - - "3307:3306" + - "5432:5432" grafana: image: grafana/grafana @@ -24,46 +23,33 @@ services: ports: - "3000:3000" depends_on: - - mariadb - - flyway: - image: redgate/flyway - container_name: nplay-flyway - restart: on-failure - environment: - FLYWAY_URL: ${MYSQL_URL} - FLYWAY_USER: ${MYSQL_USER} - FLYWAY_PASSWORD: ${MYSQL_PASSWORD} - volumes: - - db-migrations:/db/migration - command: -locations=filesystem:/db/migration migrate - depends_on: - - mariadb + - postgres bot: - image: ghcr.io/kaktushose/levelbot/nplaybot:latest + build: + context: . container_name: nplay-bot restart: on-failure environment: - BOT_TOKEN: ${BOT_TOKEN} - BOT_PRODUCTION: ${BOT_PRODUCTION} - DB_USER: ${MYSQL_USER} - DB_PASSWORD: ${MYSQL_PASSWORD} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_URL: ${POSTGRES_URL} + POSTGRES_DB: ${POSTGRES_DB} + BOT_GUILD: ${BOT_GUILD} + labels: + - com.centurylinklabs.watchtower.enable="true" volumes: - - db-migrations:/db/migration - ./data/logs:/logs depends_on: - - mariadb + - postgres watchtower: image: containrrr/watchtower container_name: nplay-watchtower environment: TZ: Europe/Berlin + WATCHTOWER_LABEL_ENABLE: true WATCHTOWER_CLEANUP: true WATCHTOWER_POLL_INTERVAL: 30 volumes: - /var/run/docker.sock:/var/run/docker.sock - -volumes: - db-migrations: diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..4b83591 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e + +/wait-for-it.sh -t 30 postgres:5432 + +/flyway/flyway migrate -url="$POSTGRES_URL" -user="$POSTGRES_USER" -password="$POSTGRES_PASSWORD" -locations=filesystem:/db/migration + +exec "$@" diff --git a/pom.xml b/pom.xml index 2cbf584..6f263cc 100644 --- a/pom.xml +++ b/pom.xml @@ -78,9 +78,9 @@ 5.1.0 - org.mariadb.jdbc - mariadb-java-client - 3.3.1 + org.postgresql + postgresql + 42.7.0 org.slf4j diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java index f842ac9..9003757 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java @@ -13,7 +13,7 @@ public static void main(String[] args) { try { log.info("Starting NPLAY-Bot..."); - var bot = Bot.start(System.getenv("BOT_TOKEN")); + var bot = Bot.start(Long.parseLong(System.getenv("BOT_GUILD"))); Thread.setDefaultUncaughtExceptionHandler((t, e) -> log.error("An uncaught exception has occurred!", e)); Runtime.getRuntime().addShutdownHook(new Thread(bot::shutdown)); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index f098d0e..7fb73b2 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -12,14 +12,14 @@ public class Bot { private final JDACommands jdaCommands; private final Database database; - private Bot(String token) throws InterruptedException, RuntimeException { + private Bot(long guildId) throws InterruptedException, RuntimeException { try { database = new Database(); } catch (Exception e) { throw new RuntimeException("Unable to connect to database!", e); } - jda = JDABuilder.createDefault(token) + jda = JDABuilder.createDefault(database.getSettingsService().getBotToken(guildId)) .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) .build().awaitReady(); @@ -29,8 +29,8 @@ private Bot(String token) throws InterruptedException, RuntimeException { jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); } - public static Bot start(String token) throws InterruptedException { - return new Bot(token); + public static Bot start(long guildId) throws InterruptedException { + return new Bot(guildId); } public void shutdown() { diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index fc9804f..771b098 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot; +import com.github.kaktushose.nplaybot.settings.SettingsService; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -9,20 +10,20 @@ public class Database { private final HikariDataSource dataSource; + private final SettingsService settingsService; public Database() { var config = new HikariConfig(); - config.setJdbcUrl(System.getenv("MYSQL_URL")); - config.setUsername(System.getenv("MYSQL_USER")); - config.setPassword(System.getenv("MYSQL_PASSWORD")); - config.addDataSourceProperty("databaseName", System.getenv("MYSQL_DATABASE")); + System.out.println(System.getenv("POSTGRES_URL")); + config.setJdbcUrl(System.getenv("POSTGRES_URL")); + config.setUsername(System.getenv("POSTGRES_USER")); + config.setPassword(System.getenv("POSTGRES_PASSWORD")); + config.addDataSourceProperty("databaseName", System.getenv("POSTGRES_DB")); dataSource = new HikariDataSource(config); - } - public Connection getConnection() throws SQLException { - return dataSource.getConnection(); + settingsService = new SettingsService(dataSource); } public void closeDataSource() { @@ -31,4 +32,7 @@ public void closeDataSource() { } } + public SettingsService getSettingsService() { + return settingsService; + } } From d2476201c79ecf124d557cfce78ea6461f951646 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:51:01 +0100 Subject: [PATCH 18/66] first pass on rank system --- .../com/github/kaktushose/nplaybot/Bot.java | 10 ++ .../github/kaktushose/nplaybot/Database.java | 10 +- .../nplaybot/rank/MessageListener.java | 4 - .../nplaybot/rank/RankListener.java | 26 +++ .../kaktushose/nplaybot/rank/RankService.java | 66 +++++++- .../nplaybot/settings/SettingsService.java | 32 ++++ .../migration/V1.0.0__setup_rank_system.sql | 152 ++++++++++++++---- 7 files changed, 257 insertions(+), 43 deletions(-) delete mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/MessageListener.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 7fb73b2..b44f421 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -1,10 +1,12 @@ package com.github.kaktushose.nplaybot; import com.github.kaktushose.jda.commands.JDACommands; +import com.github.kaktushose.nplaybot.rank.RankListener; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; +import net.dv8tion.jda.api.requests.GatewayIntent; public class Bot { @@ -20,8 +22,16 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { } jda = JDABuilder.createDefault(database.getSettingsService().getBotToken(guildId)) + .enableIntents( + GatewayIntent.GUILD_MEMBERS, + GatewayIntent.GUILD_PRESENCES, + GatewayIntent.MESSAGE_CONTENT + ) .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) + .addEventListeners( + new RankListener(database.getRankService()) + ) .build().awaitReady(); jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 771b098..437987c 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -1,16 +1,15 @@ package com.github.kaktushose.nplaybot; +import com.github.kaktushose.nplaybot.rank.RankService; import com.github.kaktushose.nplaybot.settings.SettingsService; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import java.sql.Connection; -import java.sql.SQLException; - public class Database { private final HikariDataSource dataSource; private final SettingsService settingsService; + private final RankService rankService; public Database() { var config = new HikariConfig(); @@ -24,6 +23,7 @@ public Database() { dataSource = new HikariDataSource(config); settingsService = new SettingsService(dataSource); + rankService = new RankService(dataSource); } public void closeDataSource() { @@ -35,4 +35,8 @@ public void closeDataSource() { public SettingsService getSettingsService() { return settingsService; } + + public RankService getRankService() { + return rankService; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/MessageListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/MessageListener.java deleted file mode 100644 index 45aa28a..0000000 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/MessageListener.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.github.kaktushose.nplaybot.rank; - -public class MessageListener { -} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java new file mode 100644 index 0000000..e80e6d4 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -0,0 +1,26 @@ +package com.github.kaktushose.nplaybot.rank; + +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; + +public class RankListener extends ListenerAdapter { + + private final RankService rankService; + + public RankListener(RankService rankService) { + this.rankService = rankService; + } + + @Override + public void onMessageReceived(@NotNull MessageReceivedEvent event) { + var author = event.getAuthor(); + var message = event.getMessage(); + + if (author.isBot()) { + return; + } + + event.getChannel().sendMessage("valid message: " + rankService.isValidMessage(message)).queue(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 68b7e6f..d08b9af 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -1,22 +1,26 @@ package com.github.kaktushose.nplaybot.rank; -import com.github.kaktushose.nplaybot.Database; import com.github.kaktushose.nplaybot.rank.model.Rank; import com.github.kaktushose.nplaybot.rank.model.UserInfo; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; public class RankService { - private final Database database; + private final DataSource dataSource; - public RankService(Database database) { - this.database = database; + public RankService(DataSource dataSource) { + this.dataSource = dataSource; } public UserInfo getUserInfo(long userId) { - try (Connection connection = database.getConnection()) { + try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" SELECT xp, rank, message_count, start_xp, role_id, color, bound FROM users JOIN ranks @@ -62,4 +66,56 @@ public UserInfo getUserInfo(long userId) { } } + public boolean isValidMessage(Message message) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT rank_settings.*, users.last_valid_message + FROM rank_settings JOIN users ON user_id = ? + WHERE guild_id = ? + """ + ); + statement.setLong(1, message.getAuthor().getIdLong()); + statement.setLong(2, message.getGuildIdLong()); + + var result = statement.executeQuery(); + result.next(); + var lastMessage = result.getLong("last_valid_message"); + var messageCooldown = result.getInt("message_cooldown"); + var minimumLength = result.getInt("min_message_length"); + var validChannels = Arrays.asList((Long[]) result.getArray("valid_channels").getArray()); + + if (System.currentTimeMillis() - lastMessage < messageCooldown) { + return false; + } + + if (message.getContentDisplay().length() < minimumLength) { + return false; + } + + var channelId = message.getChannelIdLong(); + if (message.getChannelType().isThread()) { + channelId = message.getChannel().asThreadChannel().getParentChannel().getIdLong(); + } + return validChannels.contains(channelId); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getRandomXp() { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT * + FROM get_random_xp() + """ + ); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } diff --git a/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java b/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java new file mode 100644 index 0000000..f8a19c3 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java @@ -0,0 +1,32 @@ +package com.github.kaktushose.nplaybot.settings; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public class SettingsService { + + private final DataSource dataSource; + + public SettingsService(DataSource dataSource) { + this.dataSource = dataSource; + } + + public String getBotToken(long guildId) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT bot_token + FROM guild_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guildId); + + var result = statement.executeQuery(); + result.next(); + return result.getString(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql index f349f0a..4716697 100644 --- a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql +++ b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql @@ -1,35 +1,125 @@ -create table guild_settings ( - guild_id bigint(20) not null primary key, - bot_token varchar(255) not null, - bot_channel_id bigint(20) not null, - log_channel_id bigint(20) not null, - message_cooldown int(11) not null +CREATE TABLE guild_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + bot_token VARCHAR(255) NOT NULL, + bot_channel_id BIGINT NOT NULL, + log_channel_id BIGINT NOT NULL ); -create table users ( - user_id bigint(20) not null primary key, - permission_level int(11) not null, - daily_message boolean not null, - xp int(11) not null, - rank int(11) not null, - last_valid_message bigint(20) not null, - message_count bigint(20) not null, - start_xp int(11) not null, + +CREATE TABLE rank_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + message_cooldown INT NOT NULL, + min_message_length INT NOT NULL, + valid_channels BIGINT[] NOT NULL ); -create table ranks ( - rank_id int(11) not null primary key, - name varchar(255) not null, - color varchar(255) not null, - bound int(11) not null, - role_id bigint(20) not null + +CREATE TABLE ranks ( + rank_id INT NOT NULL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL, + bound INT NOT NULL, + role_id BIGINT NOT NULL ); -create table xp_chances ( - amount int (11) not null primary key, - chance int(11) not null + +CREATE TABLE users ( + user_id BIGINT NOT NULL PRIMARY KEY, + daily_message BOOLEAN NOT NULL, + xp INT NOT NULL, + rank_id INT NOT NULL REFERENCES ranks(rank_id), + last_valid_message BIGINT NOT NULL, + message_count BIGINT NOT NULL, + start_xp INT NOT NULL ); -create table rank_statistics ( - timestamp bigint(20) not null primary key, - total_message_count int(11) not null, - valid_message_count int (11) not null, - total_xp_gain int(11) not null, - total_rank_ups int (11) not null -) + +CREATE TABLE xp_chances ( + amount INT NOT NULL PRIMARY KEY, + chance INT NOT NULL +); + +CREATE TABLE rank_statistics ( + timestamp BIGINT NOT NULL PRIMARY KEY, + total_message_count INT NOT NULL, + valid_message_count INT NOT NULL, + total_xp_gain INT NOT NULL, + total_rank_ups INT NOT NULL +); + +CREATE FUNCTION update_rank_trigger() +RETURNS TRIGGER AS +$$ +DECLARE + new_rank ranks; +BEGIN + SELECT INTO new_rank * FROM ranks WHERE NEW.xp >= bound ORDER BY bound DESC LIMIT 1; + + UPDATE users SET rank_id = new_rank.rank_id WHERE user_id = NEW.user_id; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER update_rank_trigger +AFTER UPDATE OF xp ON users +FOR EACH ROW +EXECUTE FUNCTION update_rank_trigger(); + +CREATE FUNCTION set_xp(id BIGINT, new_xp INT) +RETURNS TABLE (rank_changed BOOLEAN, current_rank int, next_rank int) AS +$$ +DECLARE + old_rank INT; + new_rank INT; +BEGIN + SELECT INTO old_rank users.rank_id FROM users WHERE users.user_id = id; + UPDATE users SET xp = new_xp WHERE users.user_id = id; + SELECT INTO new_rank users.rank_id FROM users WHERE users.user_id = id; + + + SELECT INTO rank_changed old_rank <> new_rank; + SELECT INTO current_rank new_rank; + SELECT INTO next_rank new_rank + 1; + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION add_xp(id BIGINT, xp_to_add INT) +RETURNS TABLE (rank_changed BOOLEAN, current_rank int, next_rank INT, current_xp INT) AS +$$ +DECLARE +BEGIN + SELECT xp_to_add + users.xp INTO current_xp FROM users WHERE users.user_id = id; + SELECT INTO rank_changed, current_rank, next_rank * FROM set_xp(id, current_xp); + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION get_random_xp() +RETURNS INT AS +$$ +DECLARE + result_row RECORD; + xp_chance INT; + chance_sum INT; +BEGIN + xp_chance := floor(random() * 100) + 1; + chance_sum := 0; + FOR result_row IN SELECT * FROM xp_chances + LOOP + chance_sum := chance_sum + result_row.chance; + IF chance_sum >= xp_chance THEN + RETURN result_row.amount; + END IF; + END LOOP; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION add_random_xp(id BIGINT) +RETURNS TABLE (rank_changed BOOLEAN, current_rank int, next_rank INT, current_xp INT) AS +$$ +DECLARE + xp INT; +BEGIN + SELECT get_random_xp INTO xp FROM get_random_xp(); + SELECT INTO rank_changed, current_rank, next_rank, current_xp * FROM add_xp(id, xp); + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; From 77ac1ef057cfd0e4541818ae3212db78ddee246c Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Tue, 12 Dec 2023 17:47:50 +0100 Subject: [PATCH 19/66] implement xp gain, cooldown and rank up --- docker-compose.yml | 2 +- embeds.json | 32 ++++++ .../com/github/kaktushose/nplaybot/Bot.java | 15 ++- .../nplaybot/rank/RankListener.java | 38 ++++++- .../kaktushose/nplaybot/rank/RankService.java | 99 +++++++++++++------ .../kaktushose/nplaybot/rank/model/Rank.java | 4 - .../nplaybot/rank/model/RankInfo.java | 4 + .../nplaybot/rank/model/UserInfo.java | 5 +- .../nplaybot/rank/model/XpChangeResult.java | 23 +++++ 9 files changed, 183 insertions(+), 39 deletions(-) create mode 100644 embeds.json delete mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/model/Rank.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java diff --git a/docker-compose.yml b/docker-compose.yml index ab11df0..a0a1f9a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,7 +37,7 @@ services: POSTGRES_DB: ${POSTGRES_DB} BOT_GUILD: ${BOT_GUILD} labels: - - com.centurylinklabs.watchtower.enable="true" + - com.centurylinklabs.watchtower.enable="true" volumes: - ./data/logs:/logs depends_on: diff --git a/embeds.json b/embeds.json new file mode 100644 index 0000000..797ce8e --- /dev/null +++ b/embeds.json @@ -0,0 +1,32 @@ +{ + "rankChangeIncrease": { + "title": ":arrow_up: Stufenaufstieg!", + "description": "{user}", + "color": "{color}", + "fields": [ + { + "name": "Neue Stufe:", + "value": ":level_slider: {currentRank}" + }, + { + "name": "Nächste Stufe:", + "value": ":dart: {nextRank} (noch {xp} XP)" + } + ] + }, + "rankChangeMax": { + "title": ":arrow_up: Stufenaufstieg!", + "description": "{user}", + "color": "{color}", + "fields": [ + { + "name": "Neue Stufe:", + "value": ":level_slider: {currentRank}" + }, + { + "name": "Herzlichen Glückwunsch", + "value": ":confetti_ball: Du hast hiermit die maximale Stufe erreicht" + } + ] + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index b44f421..9fa7e22 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot; import com.github.kaktushose.jda.commands.JDACommands; +import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.rank.RankListener; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; @@ -13,6 +14,7 @@ public class Bot { private final JDA jda; private final JDACommands jdaCommands; private final Database database; + private final EmbedCache embedCache; private Bot(long guildId) throws InterruptedException, RuntimeException { try { @@ -21,6 +23,8 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { throw new RuntimeException("Unable to connect to database!", e); } + embedCache = new EmbedCache("embeds.json"); + jda = JDABuilder.createDefault(database.getSettingsService().getBotToken(guildId)) .enableIntents( GatewayIntent.GUILD_MEMBERS, @@ -30,10 +34,9 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) .addEventListeners( - new RankListener(database.getRankService()) + new RankListener(database.getRankService(), embedCache) ) .build().awaitReady(); - jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); @@ -48,4 +51,12 @@ public void shutdown() { jda.shutdown(); database.closeDataSource(); } + + public Database getDatabase() { + return database; + } + + public EmbedCache getEmbedCache() { + return embedCache; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index e80e6d4..f3c1ef9 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -1,15 +1,24 @@ package com.github.kaktushose.nplaybot.rank; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.jetbrains.annotations.NotNull; +import java.util.List; + +@SuppressWarnings("DataFlowIssue") public class RankListener extends ListenerAdapter { private final RankService rankService; + private final EmbedCache embedCache; - public RankListener(RankService rankService) { + public RankListener(RankService rankService, EmbedCache embedCache) { this.rankService = rankService; + this.embedCache = embedCache; } @Override @@ -20,7 +29,32 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (author.isBot()) { return; } + if (!event.isFromGuild()) { + return; + } + if (!rankService.isValidMessage(message)) { + return; + } + + rankService.updateValidMessage(author); + var result = rankService.addRandomXp(author); + updateRankRoles(event.getMember(), event.getGuild(), result); + + if (!result.rankChanged()) { + return; + } + String embed = result.nextRank().isPresent() ? "rankChangeIncrease" : "rankChangeMax"; + event.getChannel().sendMessage( + embedCache.getEmbed(embed).injectValues(result.getEmbedValues(author)).toMessageCreateData() + ).queue(); + } - event.getChannel().sendMessage("valid message: " + rankService.isValidMessage(message)).queue(); + private void updateRankRoles(Member member, Guild guild, XpChangeResult result) { + var validRole = guild.getRoleById(result.currentRank().roleId()); + var invalidRoles = rankService.getRankRoleIds().stream() + .map(guild::getRoleById) + .filter(it -> it != validRole) + .toList(); + guild.modifyMemberRoles(member, List.of(validRole), invalidRoles).queue(); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index d08b9af..251a85d 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -1,15 +1,18 @@ package com.github.kaktushose.nplaybot.rank; -import com.github.kaktushose.nplaybot.rank.model.Rank; +import com.github.kaktushose.nplaybot.rank.model.RankInfo; import com.github.kaktushose.nplaybot.rank.model.UserInfo; +import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.User; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.Optional; public class RankService { @@ -19,40 +22,46 @@ public RankService(DataSource dataSource) { this.dataSource = dataSource; } - public UserInfo getUserInfo(long userId) { + private Optional getRankInfo(int rankId) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT xp, rank, message_count, start_xp, role_id, color, bound - FROM users JOIN ranks - WHERE user_id = ? AND rank_id = rank - """ - ); - statement.setLong(1, userId); - - var result = statement.executeQuery(); - result.next(); - var currentRank = new Rank( - result.getLong("role_id"), - result.getString("color"), - result.getInt("bound") - result.getInt("xp") - ); - - statement = connection.prepareStatement(""" SELECT role_id, color, bound FROM ranks WHERE rank_id = ? """ ); - statement.setLong(1, result.getInt("rank") + 1); - result = statement.executeQuery(); - Rank nextRank = null; + statement.setLong(1, rankId); + + var result = statement.executeQuery(); + if (result.next()) { - nextRank = new Rank( + return Optional.of(new RankInfo( result.getLong("role_id"), result.getString("color"), - result.getInt("bound") - result.getInt("xp") - ); + result.getInt("bound") + )); } + return Optional.empty(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public UserInfo getUserInfo(long userId) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT xp, rank_id, message_count, start_xp + FROM users + WHERE user_id = ? + """ + ); + statement.setLong(1, userId); + + var result = statement.executeQuery(); + result.next(); + + var currentRank = getRankInfo(result.getInt("rank_id")).orElseThrow(); + var nextRank = getRankInfo(result.getInt("rank_id") + 1); return new UserInfo( result.getInt("xp"), @@ -102,17 +111,49 @@ public boolean isValidMessage(Message message) { } } - public int getRandomXp() { + public void updateValidMessage(User user) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT * - FROM get_random_xp() + UPDATE users + SET last_valid_message = ? + WHERE user_id = ? """ ); + statement.setLong(1, System.currentTimeMillis()); + statement.setLong(2, user.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public XpChangeResult addRandomXp(User user) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM add_random_xp(?)"); + statement.setLong(1, user.getIdLong()); var result = statement.executeQuery(); result.next(); - return result.getInt(1); + return new XpChangeResult( + result.getBoolean("rank_changed"), + getRankInfo(result.getInt("current_rank")).orElseThrow(), + getRankInfo(result.getInt("next_rank")), + result.getInt("current_xp") + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public List getRankRoleIds() { + try (Connection connection = dataSource.getConnection()) { + var result = connection.prepareStatement("SELECT role_id FROM ranks").executeQuery(); + var roleIds = new ArrayList(); + while (result.next()) { + roleIds.add(result.getLong(1)); + } + return roleIds; } catch (SQLException e) { throw new RuntimeException(e); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/Rank.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/Rank.java deleted file mode 100644 index e9a257e..0000000 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/Rank.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.github.kaktushose.nplaybot.rank.model; - -public record Rank(long roleId, String color, int missingXp) { -} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java new file mode 100644 index 0000000..01317d6 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java @@ -0,0 +1,4 @@ +package com.github.kaktushose.nplaybot.rank.model; + +public record RankInfo(long roleId, String color, int xpBound) { +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java index be8816f..7577817 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java @@ -1,3 +1,6 @@ package com.github.kaktushose.nplaybot.rank.model; -public record UserInfo(int xp, Rank currentRank, Rank nextRank, int messageCount, int xpGain) { } +import java.util.Optional; + +public record UserInfo(int xp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain) { +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java new file mode 100644 index 0000000..47bbafa --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java @@ -0,0 +1,23 @@ +package com.github.kaktushose.nplaybot.rank.model; + +import net.dv8tion.jda.api.entities.User; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public record XpChangeResult(boolean rankChanged, RankInfo currentRank, Optional nextRank, int currentXp) { + + public Map getEmbedValues(User user) { + var result = new HashMap() {{ + put("user", String.format("<@%d>", user.getIdLong())); + put("color", currentRank.color()); + put("currentRank", String.format("<@&%d>", currentRank.roleId())); + }}; + nextRank.ifPresent(rank -> { + result.put("nextRank", String.format("<@&%d>", rank.roleId())); + result.put("xp", rank.xpBound() - currentXp); + }); + return result; + } +} From 1e064cc9fd1b961ac688e59ace32268c6e400a76 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:10:23 +0100 Subject: [PATCH 20/66] implement rank info command --- embeds.json | 65 ++++++++++++++++++- .../com/github/kaktushose/nplaybot/Bot.java | 5 ++ .../nplaybot/rank/RankListener.java | 2 +- .../kaktushose/nplaybot/rank/RankService.java | 5 +- .../nplaybot/rank/UserStatisticsTask.java | 14 ---- .../rank/commands/RankInfoCommand.java | 25 +++++++ .../nplaybot/rank/model/UserInfo.java | 23 ++++++- 7 files changed, 119 insertions(+), 20 deletions(-) delete mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/UserStatisticsTask.java diff --git a/embeds.json b/embeds.json index 797ce8e..f579b22 100644 --- a/embeds.json +++ b/embeds.json @@ -1,5 +1,5 @@ { - "rankChangeIncrease": { + "rankIncrease": { "title": ":arrow_up: Stufenaufstieg!", "description": "{user}", "color": "{color}", @@ -14,7 +14,7 @@ } ] }, - "rankChangeMax": { + "rankIncreaseMax": { "title": ":arrow_up: Stufenaufstieg!", "description": "{user}", "color": "{color}", @@ -28,5 +28,66 @@ "value": ":confetti_ball: Du hast hiermit die maximale Stufe erreicht" } ] + }, + "rankInfo": { + "title": ":information_source: Kontoinformation für", + "description": "{user}", + "color": "{color}", + "thumbnail": { + "url": "{avatarUrl}" + }, + "fields": [ + { + "name": "Stufe", + "value": ":level_slider: {currentRank}" + }, + { + "name": "Nächste Stufe", + "value": ":dart: {nextRank} (noch {missingXp} XP)" + }, + { + "name": "Aktuelle XP", + "value": ":star2: {currentXp}" + }, + { + "name": "XP-Zuwachs (24h):", + "value": ":chart_with_upwards_trend: {xpGain}" + }, + { + "name": "Gewertete Nachrichten:", + "value": ":bar_chart: {messageCount}" + } + ] + }, + "rankInfoMax": { + "title": ":information_source: Kontoinformation für", + "description": "{user}", + "color": "{color}", + "thumbnail": { + "url": "{avatarUrl}" + }, + "fields": [ + { + "name": "Stufe", + "value": ":level_slider: {currentRank}" + }, + { + "name": "Aktuelle XP", + "value": ":star2: {currentXp}" + }, + { + "name": "XP-Zuwachs (24h):", + "value": ":chart_with_upwards_trend: {xpGain}" + }, + { + "name": "Gewertete Nachrichten:", + "value": ":bar_chart: {messageCount}" + } + ] + }, + "embedCacheReload": { + "title": "Erfolg", + "description": "Der EmbedCache wurde aktualisiert", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 9fa7e22..5c1b560 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot; import com.github.kaktushose.jda.commands.JDACommands; +import com.github.kaktushose.jda.commands.annotations.Produces; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.rank.RankListener; import net.dv8tion.jda.api.JDA; @@ -40,6 +41,8 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); + + jdaCommands.getDependencyInjector().registerProvider(this); } public static Bot start(long guildId) throws InterruptedException { @@ -52,10 +55,12 @@ public void shutdown() { database.closeDataSource(); } + @Produces public Database getDatabase() { return database; } + @Produces public EmbedCache getEmbedCache() { return embedCache; } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index f3c1ef9..90c0c35 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -43,7 +43,7 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (!result.rankChanged()) { return; } - String embed = result.nextRank().isPresent() ? "rankChangeIncrease" : "rankChangeMax"; + var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; event.getChannel().sendMessage( embedCache.getEmbed(embed).injectValues(result.getEmbedValues(author)).toMessageCreateData() ).queue(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 251a85d..ed39ccc 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -3,6 +3,7 @@ import com.github.kaktushose.nplaybot.rank.model.RankInfo; import com.github.kaktushose.nplaybot.rank.model.UserInfo; import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.User; @@ -47,7 +48,7 @@ private Optional getRankInfo(int rankId) { } } - public UserInfo getUserInfo(long userId) { + public UserInfo getUserInfo(Member member) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" SELECT xp, rank_id, message_count, start_xp @@ -55,7 +56,7 @@ public UserInfo getUserInfo(long userId) { WHERE user_id = ? """ ); - statement.setLong(1, userId); + statement.setLong(1, member.getIdLong()); var result = statement.executeQuery(); result.next(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/UserStatisticsTask.java b/src/main/java/com/github/kaktushose/nplaybot/rank/UserStatisticsTask.java deleted file mode 100644 index 7f475d4..0000000 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/UserStatisticsTask.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.kaktushose.nplaybot.rank; - -import com.github.kaktushose.nplaybot.scheduler.ScheduledTask; - -import java.util.function.Consumer; - -@ScheduledTask -public class UserStatisticsTask implements Consumer { - - @Override - public void accept(RankService rankService) { - - } -} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java index 7a3077c..53c6b78 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -1,4 +1,29 @@ package com.github.kaktushose.nplaybot.rank.commands; +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Optional; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.rank.model.UserInfo; +import net.dv8tion.jda.api.entities.Member; + +@Interaction public class RankInfoCommand { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + + @SlashCommand(value = "rank", isGuildOnly = true, desc = "Zeigt die Kontoinformationen zu einem User an") + public void onCommand(CommandEvent event, @Optional Member member) { + var target = member == null ? event.getMember() : member; + UserInfo userInfo = database.getRankService().getUserInfo(target); + + var embed = userInfo.nextRank().isPresent() ? "rankInfo" : "rankInfoMax"; + event.reply(embedCache.getEmbed(embed).injectValues(userInfo.getEmbedValues(target))); + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java index 7577817..1ef364b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java @@ -1,6 +1,27 @@ package com.github.kaktushose.nplaybot.rank.model; +import net.dv8tion.jda.api.entities.Member; + +import java.util.HashMap; +import java.util.Map; import java.util.Optional; -public record UserInfo(int xp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain) { +public record UserInfo(int currentXp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain) { + + public Map getEmbedValues(Member member) { + var result = new HashMap() {{ + put("user", String.format("<@%d>", member.getIdLong())); + put("color", currentRank.color()); + put("avatarUrl", member.getEffectiveAvatarUrl()); + put("currentRank", String.format("<@&%d>", currentRank.roleId())); + put("currentXp", currentXp); + put("xpGain", xpGain); + put("messageCount", messageCount); + }}; + nextRank.ifPresent(rank -> { + result.put("nextRank", String.format("<@&%d>", rank.roleId())); + result.put("missingXp", rank.xpBound() - currentXp); + }); + return result; + } } From 4e8472c0b7fb5bf9a5abbf06ad9d516e63718a67 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:31:20 +0100 Subject: [PATCH 21/66] implement member database binding --- .../com/github/kaktushose/nplaybot/Bot.java | 11 +++++--- .../nplaybot/rank/JoinLeaveListener.java | 25 +++++++++++++++++ .../kaktushose/nplaybot/rank/RankService.java | 27 +++++++++++++++++++ .../migration/V1.0.0__setup_rank_system.sql | 12 ++++----- 4 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/JoinLeaveListener.java diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 5c1b560..49aff4a 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -3,6 +3,7 @@ import com.github.kaktushose.jda.commands.JDACommands; import com.github.kaktushose.jda.commands.annotations.Produces; import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.rank.JoinLeaveListener; import com.github.kaktushose.nplaybot.rank.RankListener; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; @@ -17,6 +18,7 @@ public class Bot { private final Database database; private final EmbedCache embedCache; + @SuppressWarnings("DataFlowIssue") private Bot(long guildId) throws InterruptedException, RuntimeException { try { database = new Database(); @@ -35,14 +37,17 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) .addEventListeners( - new RankListener(database.getRankService(), embedCache) + new RankListener(database.getRankService(), embedCache), + new JoinLeaveListener(database.getRankService()) ) .build().awaitReady(); - jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); - jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); + database.getRankService().indexMembers(jda.getGuildById(guildId)); + jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); jdaCommands.getDependencyInjector().registerProvider(this); + + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); } public static Bot start(long guildId) throws InterruptedException { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/JoinLeaveListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/JoinLeaveListener.java new file mode 100644 index 0000000..c2aeaec --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/JoinLeaveListener.java @@ -0,0 +1,25 @@ +package com.github.kaktushose.nplaybot.rank; + +import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent; +import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; + +public class JoinLeaveListener extends ListenerAdapter { + + private final RankService rankService; + + public JoinLeaveListener(RankService rankService) { + this.rankService = rankService; + } + + @Override + public void onGuildMemberJoin(@NotNull GuildMemberJoinEvent event) { + rankService.addUser(event.getUser()); + } + + @Override + public void onGuildMemberRemove(@NotNull GuildMemberRemoveEvent event) { + rankService.removeUser(event.getUser()); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index ed39ccc..224374a 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -3,12 +3,15 @@ import com.github.kaktushose.nplaybot.rank.model.RankInfo; import com.github.kaktushose.nplaybot.rank.model.UserInfo; import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; +import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.User; import javax.sql.DataSource; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -23,6 +26,30 @@ public RankService(DataSource dataSource) { this.dataSource = dataSource; } + public void addUser(User user) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("INSERT INTO users VALUES(?) ON CONFLICT DO NOTHING"); + statement.setLong(1, user.getIdLong()); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void removeUser(User user) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("DELETE FROM users WHERE user_id = ?"); + statement.setLong(1, user.getIdLong()); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void indexMembers(Guild guild) { + guild.loadMembers(member -> addUser(member.getUser())); + } + private Optional getRankInfo(int rankId) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" diff --git a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql index 4716697..ea2124e 100644 --- a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql +++ b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql @@ -22,12 +22,12 @@ CREATE TABLE ranks ( CREATE TABLE users ( user_id BIGINT NOT NULL PRIMARY KEY, - daily_message BOOLEAN NOT NULL, - xp INT NOT NULL, - rank_id INT NOT NULL REFERENCES ranks(rank_id), - last_valid_message BIGINT NOT NULL, - message_count BIGINT NOT NULL, - start_xp INT NOT NULL + daily_message BOOLEAN NOT NULL DEFAULT FALSE, + xp INT NOT NULL DEFAULT 0, + rank_id INT NOT NULL REFERENCES ranks(rank_id) DEFAULT 1, + last_valid_message BIGINT NOT NULL DEFAULT 0, + message_count BIGINT NOT NULL DEFAULT 0, + start_xp INT NOT NULL DEFAULT 0 ); CREATE TABLE xp_chances ( From 6f5db1ced5fcd829a6bd37ed1b6828623935de00 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:51:15 +0100 Subject: [PATCH 22/66] bump jda-commands version and mark producers as skipIndex --- pom.xml | 2 +- src/main/java/com/github/kaktushose/nplaybot/Bot.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6f263cc..88f2e39 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ com.github.Kaktushose jda-commands - 4.0.0-beta.1 + 4f948157b4 com.zaxxer diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 49aff4a..5cd3960 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -60,12 +60,12 @@ public void shutdown() { database.closeDataSource(); } - @Produces + @Produces(skipIndexing = true) public Database getDatabase() { return database; } - @Produces + @Produces(skipIndexing = true) public EmbedCache getEmbedCache() { return embedCache; } From 390e931aed6080cf3a1889ad9508cf97057b45b4 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:54:50 +0100 Subject: [PATCH 23/66] compile with parameters flag --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 88f2e39..cc5bb6c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,13 @@ org.apache.maven.plugins maven-compiler-plugin 3.10.1 + + + -parameters + + 16 + 16 + org.apache.maven.plugins From 1517077b0113974071dd02b56836126a88d46db0 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:55:28 +0100 Subject: [PATCH 24/66] make xp amount non primary key --- src/main/resources/db/migration/V1.0.0__setup_rank_system.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql index ea2124e..8c6f63f 100644 --- a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql +++ b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql @@ -31,7 +31,8 @@ CREATE TABLE users ( ); CREATE TABLE xp_chances ( - amount INT NOT NULL PRIMARY KEY, + chance_id INT NOT NULL PRIMARY KEY, + amount INT NOT NULL, chance INT NOT NULL ); From f803b7c76dea809b45746e103acdae86cad5d0a8 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:55:40 +0100 Subject: [PATCH 25/66] add reload embeds command --- .../internal/MaintenanceCommands.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java diff --git a/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java b/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java new file mode 100644 index 0000000..25fd2dd --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java @@ -0,0 +1,22 @@ +package com.github.kaktushose.nplaybot.internal; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import net.dv8tion.jda.api.Permission; + +@Interaction +public class MaintenanceCommands { + + @Inject + private EmbedCache embedCache; + + @SlashCommand(value = "reload embeds", desc = "Aktualisiert den EmbedCache", enabledFor = Permission.BAN_MEMBERS) + public void onReload(CommandEvent event) { + embedCache.loadEmbeds(); + event.reply(embedCache.getEmbed("embedCacheReload")); + } + +} From 296805ad1ed9be62cf0cc23cba1022639b155770 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 14 Dec 2023 20:18:02 +0100 Subject: [PATCH 26/66] add commands for altering user xp --- embeds.json | 10 +++ .../com/github/kaktushose/nplaybot/Bot.java | 2 +- .../nplaybot/rank/RankListener.java | 33 ++++------ .../kaktushose/nplaybot/rank/RankService.java | 64 ++++++++++++++++--- .../rank/commands/ModifyXpCommands.java | 62 ++++++++++++++++++ .../rank/commands/SetRankCommand.java | 4 -- .../nplaybot/rank/model/XpChangeResult.java | 4 +- .../nplaybot/settings/SettingsService.java | 21 ++++++ 8 files changed, 162 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java delete mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/commands/SetRankCommand.java diff --git a/embeds.json b/embeds.json index f579b22..3822212 100644 --- a/embeds.json +++ b/embeds.json @@ -89,5 +89,15 @@ "title": "Erfolg", "description": "Der EmbedCache wurde aktualisiert", "color": "#67c94f" + }, + "setXpResult" : { + "title": "Erfolg", + "description": "Die XP von {user} wurden auf {xp} XP gesetzt", + "color": "#67c94f" + }, + "addXpResult" : { + "title": "Erfolg", + "description": "{user} wurden {xp} XP hinzugefügt", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 5cd3960..ce6fc31 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -37,7 +37,7 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) .addEventListeners( - new RankListener(database.getRankService(), embedCache), + new RankListener(database, embedCache), new JoinLeaveListener(database.getRankService()) ) .build().awaitReady(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index 90c0c35..a1d1f95 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -1,23 +1,22 @@ package com.github.kaktushose.nplaybot.rank; import com.github.kaktushose.jda.commands.data.EmbedCache; -import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.settings.SettingsService; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.jetbrains.annotations.NotNull; -import java.util.List; - -@SuppressWarnings("DataFlowIssue") public class RankListener extends ListenerAdapter { private final RankService rankService; + private final SettingsService settingsService; private final EmbedCache embedCache; - public RankListener(RankService rankService, EmbedCache embedCache) { - this.rankService = rankService; + public RankListener(Database database, EmbedCache embedCache) { + this.rankService = database.getRankService(); + this.settingsService = database.getSettingsService(); this.embedCache = embedCache; } @@ -38,23 +37,15 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { rankService.updateValidMessage(author); var result = rankService.addRandomXp(author); - updateRankRoles(event.getMember(), event.getGuild(), result); + rankService.updateRankRoles(event.getMember(), event.getGuild(), result); if (!result.rankChanged()) { return; } var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; - event.getChannel().sendMessage( - embedCache.getEmbed(embed).injectValues(result.getEmbedValues(author)).toMessageCreateData() - ).queue(); - } - - private void updateRankRoles(Member member, Guild guild, XpChangeResult result) { - var validRole = guild.getRoleById(result.currentRank().roleId()); - var invalidRoles = rankService.getRankRoleIds().stream() - .map(guild::getRoleById) - .filter(it -> it != validRole) - .toList(); - guild.modifyMemberRoles(member, List.of(validRole), invalidRoles).queue(); + var messageData = new MessageCreateBuilder().addContent(author.getAsMention()) + .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(author)).toMessageEmbed()) + .build(); + settingsService.getBotChannel(event.getGuild()).sendMessage(messageData).queue(); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 224374a..1fd2df8 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -6,12 +6,10 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.UserSnowflake; import javax.sql.DataSource; import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -26,7 +24,7 @@ public RankService(DataSource dataSource) { this.dataSource = dataSource; } - public void addUser(User user) { + public void addUser(UserSnowflake user) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("INSERT INTO users VALUES(?) ON CONFLICT DO NOTHING"); statement.setLong(1, user.getIdLong()); @@ -36,7 +34,7 @@ public void addUser(User user) { } } - public void removeUser(User user) { + public void removeUser(UserSnowflake user) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("DELETE FROM users WHERE user_id = ?"); statement.setLong(1, user.getIdLong()); @@ -75,7 +73,7 @@ private Optional getRankInfo(int rankId) { } } - public UserInfo getUserInfo(Member member) { + public UserInfo getUserInfo(UserSnowflake user) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" SELECT xp, rank_id, message_count, start_xp @@ -83,7 +81,7 @@ public UserInfo getUserInfo(Member member) { WHERE user_id = ? """ ); - statement.setLong(1, member.getIdLong()); + statement.setLong(1, user.getIdLong()); var result = statement.executeQuery(); result.next(); @@ -139,7 +137,7 @@ public boolean isValidMessage(Message message) { } } - public void updateValidMessage(User user) { + public void updateValidMessage(UserSnowflake user) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" UPDATE users @@ -156,7 +154,7 @@ public void updateValidMessage(User user) { } } - public XpChangeResult addRandomXp(User user) { + public XpChangeResult addRandomXp(UserSnowflake user) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("SELECT * FROM add_random_xp(?)"); statement.setLong(1, user.getIdLong()); @@ -174,6 +172,53 @@ public XpChangeResult addRandomXp(User user) { } } + public XpChangeResult addXp(UserSnowflake user, int amount) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM add_xp(?, ?)"); + statement.setLong(1, user.getIdLong()); + statement.setInt(2, amount); + + var result = statement.executeQuery(); + result.next(); + return new XpChangeResult( + result.getBoolean("rank_changed"), + getRankInfo(result.getInt("current_rank")).orElseThrow(), + getRankInfo(result.getInt("next_rank")), + result.getInt("current_xp") + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public XpChangeResult setXp(UserSnowflake user, int value) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM set_xp(?, ?)"); + statement.setLong(1, user.getIdLong()); + statement.setInt(2, value); + + var result = statement.executeQuery(); + result.next(); + return new XpChangeResult( + result.getBoolean("rank_changed"), + getRankInfo(result.getInt("current_rank")).orElseThrow(), + getRankInfo(result.getInt("next_rank")), + value + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void updateRankRoles(Member member, Guild guild, XpChangeResult result) { + var validRole = guild.getRoleById(result.currentRank().roleId()); + var invalidRoles = getRankRoleIds().stream() + .map(guild::getRoleById) + .filter(it -> it != validRole) + .toList(); + guild.modifyMemberRoles(member, List.of(validRole), invalidRoles).queue(); + } + public List getRankRoleIds() { try (Connection connection = dataSource.getConnection()) { var result = connection.prepareStatement("SELECT role_id FROM ranks").executeQuery(); @@ -186,5 +231,4 @@ public List getRankRoleIds() { throw new RuntimeException(e); } } - } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java new file mode 100644 index 0000000..705d5ea --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java @@ -0,0 +1,62 @@ +package com.github.kaktushose.nplaybot.rank.commands; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.constraints.Max; +import com.github.kaktushose.jda.commands.annotations.constraints.Min; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; + +@Interaction(ephemeral = true) +public class ModifyXpCommands { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + + @SlashCommand(value = "add xp", desc = "Fügt einem User XP hinzu", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + public void onAddXp(CommandEvent event, Member target, @Min(1) @Max(Integer.MAX_VALUE) int amount) { + var result = database.getRankService().addXp(target, amount); + + event.reply(embedCache.getEmbed("addXpResult") + .injectValue("user", target.getAsMention()) + .injectValue("xp", amount) + ); + + checkRankUpdate(result, target, event.getGuild()); + } + + @SlashCommand(value = "set xp", desc = "Setzt die XP von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + public void onSetXp(CommandEvent event, Member target, @Min(0) @Max(Integer.MAX_VALUE) int value) { + var result = database.getRankService().setXp(target, value); + + event.reply(embedCache.getEmbed("setXpResult") + .injectValue("user", target.getAsMention()) + .injectValue("xp", value) + ); + + checkRankUpdate(result, target, event.getGuild()); + } + + private void checkRankUpdate(XpChangeResult result, Member member, Guild guild) { + database.getRankService().updateRankRoles(member, guild, result); + + if (!result.rankChanged()) { + return; + } + + var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; + var messageData = new MessageCreateBuilder().addContent(member.getAsMention()) + .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member.getUser())).toMessageEmbed()) + .build(); + database.getSettingsService().getBotChannel(guild).sendMessage(messageData).queue(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SetRankCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SetRankCommand.java deleted file mode 100644 index 4934346..0000000 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SetRankCommand.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.github.kaktushose.nplaybot.rank.commands; - -public class SetRankCommand { -} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java index 47bbafa..0ad257e 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java @@ -1,6 +1,6 @@ package com.github.kaktushose.nplaybot.rank.model; -import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.UserSnowflake; import java.util.HashMap; import java.util.Map; @@ -8,7 +8,7 @@ public record XpChangeResult(boolean rankChanged, RankInfo currentRank, Optional nextRank, int currentXp) { - public Map getEmbedValues(User user) { + public Map getEmbedValues(UserSnowflake user) { var result = new HashMap() {{ put("user", String.format("<@%d>", user.getIdLong())); put("color", currentRank.color()); diff --git a/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java b/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java index f8a19c3..72debcf 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java @@ -1,5 +1,8 @@ package com.github.kaktushose.nplaybot.settings; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; @@ -29,4 +32,22 @@ public String getBotToken(long guildId) { throw new RuntimeException(e); } } + + public TextChannel getBotChannel(Guild guild) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT bot_channel_id + FROM guild_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return guild.getTextChannelById(result.getLong(1)); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } } From 467103bee9d692c0f5a6bb8db16cd6f771d9c6f3 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Fri, 15 Dec 2023 20:37:28 +0100 Subject: [PATCH 27/66] implement leaderboard command --- embeds.json | 5 + pom.xml | 2 +- .../com/github/kaktushose/nplaybot/Bot.java | 2 + .../kaktushose/nplaybot/rank/RankService.java | 33 +++++++ .../rank/leaderboard/LeaderboardCommand.java | 95 +++++++++++++++++++ .../rank/leaderboard/LeaderboardPage.java | 35 +++++++ 6 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java diff --git a/embeds.json b/embeds.json index 3822212..aec9acf 100644 --- a/embeds.json +++ b/embeds.json @@ -99,5 +99,10 @@ "title": "Erfolg", "description": "{user} wurden {xp} XP hinzugefügt", "color": "#67c94f" + }, + "leaderboard": { + "title": "Leaderboard", + "description": "{leaderboard}", + "color": "#67c94f" } } diff --git a/pom.xml b/pom.xml index cc5bb6c..6835783 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ com.github.Kaktushose jda-commands - 4f948157b4 + 3d823b1173 com.zaxxer diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index ce6fc31..aa0f957 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -10,6 +10,7 @@ import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.MemberCachePolicy; public class Bot { @@ -34,6 +35,7 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { GatewayIntent.GUILD_PRESENCES, GatewayIntent.MESSAGE_CONTENT ) + .setMemberCachePolicy(MemberCachePolicy.ALL) .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) .addEventListeners( diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 1fd2df8..4c2718e 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot.rank; +import com.github.kaktushose.nplaybot.rank.leaderboard.LeaderboardPage; import com.github.kaktushose.nplaybot.rank.model.RankInfo; import com.github.kaktushose.nplaybot.rank.model.UserInfo; import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; @@ -231,4 +232,36 @@ public List getRankRoleIds() { throw new RuntimeException(e); } } + + public List getLeaderboard() { + try (Connection connection = dataSource.getConnection()) { + var result = connection.prepareStatement(""" + SELECT users.xp, users.user_id, ranks.role_id + FROM users JOIN ranks ON ranks.rank_id = users.rank_id + ORDER BY xp DESC; + """ + ).executeQuery(); + + List pages = new ArrayList<>(); + List rows = new ArrayList<>(); + int rowCount = 1; + while (result.next()) { + if (rowCount == 11) { + pages.add(new LeaderboardPage(rows)); + rows = new ArrayList<>(); + rowCount = 1; + } + rows.add(new LeaderboardPage.LeaderboardRow( + result.getInt("xp"), + result.getLong("user_id"), + result.getLong("role_id") + )); + rowCount++; + } + return pages; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java new file mode 100644 index 0000000..cf0f407 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java @@ -0,0 +1,95 @@ +package com.github.kaktushose.nplaybot.rank.leaderboard; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.Button; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.jda.commands.dispatching.interactions.components.ComponentEvent; +import com.github.kaktushose.jda.commands.dispatching.reply.Replyable; +import com.github.kaktushose.jda.commands.dispatching.reply.components.Buttons; +import com.github.kaktushose.jda.commands.dispatching.reply.components.Component; +import com.github.kaktushose.nplaybot.Database; +import net.dv8tion.jda.api.entities.Guild; + +import java.util.List; + + +@Interaction +public class LeaderboardCommand { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + + private final int minIndex = 1; + private int index = 1; + private int maxIndex; + private List leaderboard; + private Guild guild; + + @SlashCommand(value = "leaderboard", desc = "Zeigt eine Rangliste der Benutzer mit den meisten XP", isGuildOnly = true) + public void onCommand(CommandEvent event) { + guild = event.getGuild(); + leaderboard = database.getRankService().getLeaderboard(); + maxIndex = leaderboard.size(); + reply(event); + } + + @Button(emoji = "⏪") + public void onStart(ComponentEvent event) { + index = 1; + reply(event); + } + + @Button(emoji = "◀️") + public void onBackward(ComponentEvent event) { + if (index > minIndex) { + index--; + } + reply(event); + } + + @Button(emoji = "▶️") + public void onForward(ComponentEvent event) { + if (index < maxIndex) { + index++; + } + reply(event); + } + + @Button(emoji = "⏩") + public void onEnd(ComponentEvent event) { + index = maxIndex; + reply(event); + } + + private void reply(Replyable event) { + event.with(getButtons()).reply( + embedCache.getEmbed("leaderboard").injectValue("leaderboard", leaderboard.get(index - 1).getPage(guild)) + .toEmbedBuilder() + .setFooter(String.format("Seite (%d/%d)", index, maxIndex)) + ); + } + + private Component[] getButtons() { + if (index == minIndex) { + return List.of( + Buttons.disabled("onStart", "onBackward"), + Buttons.enabled("onForward", "onEnd") + ).toArray(new Component[0]); + } + if (index == maxIndex) { + return List.of( + Buttons.enabled("onStart", "onBackward"), + Buttons.disabled("onForward", "onEnd") + ).toArray(new Component[0]); + } + return List.of( + Buttons.enabled("onStart", "onBackward"), + Buttons.enabled("onForward", "onEnd") + ).toArray(new Component[0]); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java new file mode 100644 index 0000000..0c64b3b --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java @@ -0,0 +1,35 @@ +package com.github.kaktushose.nplaybot.rank.leaderboard; + +import net.dv8tion.jda.api.entities.Guild; + +import java.util.List; +import java.util.Optional; + +public record LeaderboardPage(List rows) { + public record LeaderboardRow(int xp, long userId, long roleId) { + } + + public String getPage(Guild guild) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < rows.size(); i++) { + var row = rows.get(i); + appendRow(builder, i + 1, resolveName(guild, row.userId), row.xp, String.format("<@&%d>", row.roleId)); + } + return builder.toString(); + } + + private void appendRow(StringBuilder builder, int index, String username, int xp, String role) { + builder.append(String.format("%d. %s %d XP (%s)\n", index, username, xp, role)); + } + + private String resolveName(Guild guild, long userId) { + var member = Optional.ofNullable(guild.getMemberById(userId)); + if (member.isPresent()) { + return member.get().getEffectiveName(); + } + // retrieve member thus it gets loaded to cache + guild.retrieveMemberById(userId).queue(); + return String.format("<@%d>", userId); + } + +} From b946651c16c29f150f9cfb85cc885490bdb73e94 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sat, 30 Dec 2023 22:04:18 +0100 Subject: [PATCH 28/66] implement task scheduler --- .../kaktushose/nplaybot/Bootstrapper.java | 2 + .../com/github/kaktushose/nplaybot/Bot.java | 12 +++ .../github/kaktushose/nplaybot/Database.java | 1 - .../nplaybot/scheduler/ScheduledTask.java | 32 +++++++- .../nplaybot/scheduler/TaskScheduler.java | 75 ++++++++++++++++++- 5 files changed, 116 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java index 9003757..6262b7b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java @@ -9,6 +9,8 @@ public class Bootstrapper { private final static Logger log = LoggerFactory.getLogger(Bootstrapper.class); public static void main(String[] args) { + Thread.currentThread().setName("Bot"); + long startTime = System.currentTimeMillis(); try { log.info("Starting NPLAY-Bot..."); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index aa0f957..2da7e35 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -5,6 +5,7 @@ import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.rank.JoinLeaveListener; import com.github.kaktushose.nplaybot.rank.RankListener; +import com.github.kaktushose.nplaybot.scheduler.TaskScheduler; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.OnlineStatus; @@ -12,12 +13,15 @@ import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.utils.MemberCachePolicy; +import java.util.concurrent.TimeUnit; + public class Bot { private final JDA jda; private final JDACommands jdaCommands; private final Database database; private final EmbedCache embedCache; + private final TaskScheduler taskScheduler; @SuppressWarnings("DataFlowIssue") private Bot(long guildId) throws InterruptedException, RuntimeException { @@ -49,6 +53,8 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); jdaCommands.getDependencyInjector().registerProvider(this); + taskScheduler = new TaskScheduler(this); + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); } @@ -57,8 +63,14 @@ public static Bot start(long guildId) throws InterruptedException { } public void shutdown() { + taskScheduler.shutdown(); jdaCommands.shutdown(); jda.shutdown(); + try { + jda.awaitShutdown(1000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } database.closeDataSource(); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 437987c..0ff33bb 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -14,7 +14,6 @@ public class Database { public Database() { var config = new HikariConfig(); - System.out.println(System.getenv("POSTGRES_URL")); config.setJdbcUrl(System.getenv("POSTGRES_URL")); config.setUsername(System.getenv("POSTGRES_USER")); config.setPassword(System.getenv("POSTGRES_PASSWORD")); diff --git a/src/main/java/com/github/kaktushose/nplaybot/scheduler/ScheduledTask.java b/src/main/java/com/github/kaktushose/nplaybot/scheduler/ScheduledTask.java index 6e943c0..046b8b1 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/scheduler/ScheduledTask.java +++ b/src/main/java/com/github/kaktushose/nplaybot/scheduler/ScheduledTask.java @@ -1,4 +1,28 @@ -package com.github.kaktushose.nplaybot.scheduler; - -public @interface ScheduledTask { -} +package com.github.kaktushose.nplaybot.scheduler; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ScheduledTask { + + boolean repeat() default true; + + /** + * This will override {@link #initialDelay()}! + * + * @return whether this scheduled task should start at midnight + */ + boolean startAtMidnight() default false; + + long initialDelay() default 0; + + long period(); + + TimeUnit unit(); + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java index 4718ffc..6b0d1ff 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java +++ b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java @@ -1,5 +1,78 @@ package com.github.kaktushose.nplaybot.scheduler; +import com.github.kaktushose.nplaybot.Bot; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; +import org.reflections.util.FilterBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Calendar; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + public class TaskScheduler { -// TODO add via annotations + + private static final Logger log = LoggerFactory.getLogger(TaskScheduler.class); + private static final String BASE_PACKAGE = "com.github.kaktushose.nplaybot"; + private final Reflections reflections; + private final ScheduledExecutorService executor; + private final Bot bot; + + public TaskScheduler(Bot bot) { + this.bot = bot; + executor = Executors.newScheduledThreadPool(4, runnable -> new Thread(runnable, "TaskScheduler")); + ConfigurationBuilder config = new ConfigurationBuilder() + .setScanners(Scanners.SubTypes, Scanners.MethodsAnnotated) + .setUrls(ClasspathHelper.forClass(Bot.class)) + .filterInputsBy(new FilterBuilder().includePackage(BASE_PACKAGE)); + reflections = new Reflections(config); + + indexTasks(); + } + + public void shutdown() { + executor.shutdown(); + try { + executor.awaitTermination(1000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void indexTasks() { + var methods = reflections.getMethodsAnnotatedWith(ScheduledTask.class); + + for (var method : methods) { + var scheduledTask = method.getAnnotation(ScheduledTask.class); + + var delay = scheduledTask.initialDelay(); + if (scheduledTask.startAtMidnight()) { + delay = TimeUnit.HOURS.toMinutes(24) + - (TimeUnit.HOURS.toMinutes(Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) + + Calendar.getInstance().get(Calendar.MINUTE)); + } + + if (scheduledTask.repeat()) { + executor.scheduleAtFixedRate(execute(method), delay, scheduledTask.period(), scheduledTask.unit()); + } else { + executor.scheduleAtFixedRate(execute(method), delay, scheduledTask.period(), scheduledTask.unit()); + } + } + } + + private Runnable execute(Method method) { + return () -> { + try { + method.invoke(method.getDeclaringClass().getConstructors()[0].newInstance(), bot); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + log.error("Exception in scheduled task!", e); + } + }; + } } From f905c95ce4f39c11351913a28229ad40f606f655 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sun, 31 Dec 2023 14:38:57 +0100 Subject: [PATCH 29/66] update xp statistics --- .../kaktushose/nplaybot/rank/RankService.java | 8 ++++++++ .../kaktushose/nplaybot/rank/StatisticsTask.java | 14 ++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/StatisticsTask.java diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 4c2718e..61264a6 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -264,4 +264,12 @@ public List getLeaderboard() { } } + public void resetDailyStatistics() { + try (Connection connection = dataSource.getConnection()) { + connection.prepareStatement("UPDATE users SET start_xp = xp").execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/StatisticsTask.java b/src/main/java/com/github/kaktushose/nplaybot/rank/StatisticsTask.java new file mode 100644 index 0000000..ae9f921 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/StatisticsTask.java @@ -0,0 +1,14 @@ +package com.github.kaktushose.nplaybot.rank; + +import com.github.kaktushose.nplaybot.Bot; +import com.github.kaktushose.nplaybot.scheduler.ScheduledTask; + +import java.util.concurrent.TimeUnit; + +public class StatisticsTask { + + @ScheduledTask(period = 24, unit = TimeUnit.HOURS, startAtMidnight = true) + public void accept(Bot bot) { + bot.getDatabase().getRankService().resetDailyStatistics(); + } +} From a63b6b6b252a48fe6da1256e4282a90693ec0e4e Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sun, 31 Dec 2023 14:43:51 +0100 Subject: [PATCH 30/66] update message count on valid message --- .../java/com/github/kaktushose/nplaybot/rank/RankService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 61264a6..1bb9fed 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -142,7 +142,8 @@ public void updateValidMessage(UserSnowflake user) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" UPDATE users - SET last_valid_message = ? + SET last_valid_message = ?, + message_count = message_count + 1 WHERE user_id = ? """ ); From aa0e5b12aa3b39f2b60a380cf8d378a31b6c68ac Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sun, 31 Dec 2023 15:43:56 +0100 Subject: [PATCH 31/66] implement rank statistics --- .../nplaybot/rank/RankListener.java | 3 + .../kaktushose/nplaybot/rank/RankService.java | 8 +++ .../migration/V1.0.0__setup_rank_system.sql | 64 +++++++++++++++++-- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index a1d1f95..8988a33 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -31,6 +31,9 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (!event.isFromGuild()) { return; } + + rankService.increaseTotalMessageCount(); + if (!rankService.isValidMessage(message)) { return; } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 1bb9fed..6cbe933 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -273,4 +273,12 @@ public void resetDailyStatistics() { } } + public void increaseTotalMessageCount() { + try (Connection connection = dataSource.getConnection()) { + connection.prepareStatement("SELECT * FROM increase_total_message_count()").execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } diff --git a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql index 8c6f63f..d137593 100644 --- a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql +++ b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql @@ -37,11 +37,11 @@ CREATE TABLE xp_chances ( ); CREATE TABLE rank_statistics ( - timestamp BIGINT NOT NULL PRIMARY KEY, - total_message_count INT NOT NULL, - valid_message_count INT NOT NULL, - total_xp_gain INT NOT NULL, - total_rank_ups INT NOT NULL + date date NOT NULL PRIMARY KEY, + total_message_count INT NOT NULL DEFAULT 0, + valid_message_count INT NOT NULL DEFAULT 0, + total_xp_gain INT NOT NULL DEFAULT 0, + total_rank_ups INT NOT NULL DEFAULT 0 ); CREATE FUNCTION update_rank_trigger() @@ -124,3 +124,57 @@ BEGIN RETURN NEXT; END; $$ LANGUAGE plpgsql; + +CREATE FUNCTION increase_valid_message_statistics() +RETURNS TRIGGER AS +$$ +BEGIN + INSERT INTO rank_statistics (DATE, valid_message_count) VALUES (CURRENT_DATE, NEW.message_count - OLD.message_count) + ON CONFLICT (DATE) DO UPDATE SET valid_message_count = rank_statistics.valid_message_count + (NEW.message_count - OLD.message_count); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER valid_message_trigger +BEFORE UPDATE OF message_count ON users +FOR EACH ROW +EXECUTE FUNCTION increase_valid_message_statistics(); + +CREATE FUNCTION increase_total_xp_gain() +RETURNS TRIGGER AS +$$ +BEGIN + INSERT INTO rank_statistics (DATE, total_xp_gain) VALUES (CURRENT_DATE, NEW.xp - OLD.xp) + ON CONFLICT (DATE) DO UPDATE SET total_xp_gain = rank_statistics.total_xp_gain + (NEW.xp - OLD.xp); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER xp_gain_trigger +BEFORE UPDATE OF xp ON users +FOR EACH ROW +EXECUTE FUNCTION increase_total_xp_gain(); + +CREATE FUNCTION increase_total_rank_ups() +RETURNS TRIGGER AS +$$ +BEGIN + INSERT INTO rank_statistics (DATE, total_rank_ups) VALUES (CURRENT_DATE, NEW.rank_id - OLD.rank_id) + ON CONFLICT (DATE) DO UPDATE SET total_rank_ups = rank_statistics.total_rank_ups + (NEW.rank_id - OLD.rank_id); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER rank_up_trigger +AFTER UPDATE OF rank_id ON users +FOR EACH ROW +EXECUTE FUNCTION increase_total_rank_ups(); + +CREATE FUNCTION increase_total_message_count() +RETURNS VOID AS +$$ +BEGIN + INSERT INTO rank_statistics (DATE, total_message_count) VALUES (CURRENT_DATE, 1) + ON CONFLICT (DATE) DO UPDATE SET total_message_count = rank_statistics.total_message_count + 1; +END; +$$ LANGUAGE plpgsql; From 2b26edf884b394526e8a8b263561ee70c2724e93 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:48:17 +0100 Subject: [PATCH 32/66] add more logging --- .../github/kaktushose/nplaybot/Database.java | 4 +- .../nplaybot/rank/JoinLeaveListener.java | 2 +- .../nplaybot/rank/RankListener.java | 12 ++++++ .../kaktushose/nplaybot/rank/RankService.java | 38 +++++++++++++++++-- .../rank/commands/ModifyXpCommands.java | 7 ++++ .../rank/leaderboard/LeaderboardCommand.java | 13 +++++++ .../nplaybot/rank/model/XpChangeResult.java | 10 +++++ .../nplaybot/scheduler/TaskScheduler.java | 8 ++-- 8 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 0ff33bb..26333d3 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -26,9 +26,7 @@ public Database() { } public void closeDataSource() { - if (dataSource != null) { - dataSource.close(); - } + dataSource.close(); } public SettingsService getSettingsService() { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/JoinLeaveListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/JoinLeaveListener.java index c2aeaec..548227f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/JoinLeaveListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/JoinLeaveListener.java @@ -15,7 +15,7 @@ public JoinLeaveListener(RankService rankService) { @Override public void onGuildMemberJoin(@NotNull GuildMemberJoinEvent event) { - rankService.addUser(event.getUser()); + rankService.createUser(event.getUser()); } @Override diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index 8988a33..83ed176 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -7,9 +7,12 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RankListener extends ListenerAdapter { + private static final Logger log = LoggerFactory.getLogger(RankListener.class); private final RankService rankService; private final SettingsService settingsService; private final EmbedCache embedCache; @@ -22,29 +25,38 @@ public RankListener(Database database, EmbedCache embedCache) { @Override public void onMessageReceived(@NotNull MessageReceivedEvent event) { + log.debug("Received message event"); var author = event.getAuthor(); var message = event.getMessage(); if (author.isBot()) { + log.trace("Author is bot"); return; } if (!event.isFromGuild()) { + log.trace("Event is not from guild"); return; } rankService.increaseTotalMessageCount(); if (!rankService.isValidMessage(message)) { + log.trace("Message doesn't meet rank criteria"); return; } rankService.updateValidMessage(author); var result = rankService.addRandomXp(author); + + log.debug("Checking for rank up: {}", author); rankService.updateRankRoles(event.getMember(), event.getGuild(), result); if (!result.rankChanged()) { + log.debug("Rank hasn't changed"); return; } + log.debug("Applying changes. New rank: {}", result.currentRank()); + var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; var messageData = new MessageCreateBuilder().addContent(author.getAsMention()) .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(author)).toMessageEmbed()) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 6cbe933..fe0647c 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -8,6 +8,8 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.UserSnowflake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.sql.Connection; @@ -19,13 +21,15 @@ public class RankService { + private static final Logger log = LoggerFactory.getLogger(RankListener.class); private final DataSource dataSource; public RankService(DataSource dataSource) { this.dataSource = dataSource; } - public void addUser(UserSnowflake user) { + public void createUser(UserSnowflake user) { + log.debug("Inserting user: {}", user); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("INSERT INTO users VALUES(?) ON CONFLICT DO NOTHING"); statement.setLong(1, user.getIdLong()); @@ -36,6 +40,7 @@ public void addUser(UserSnowflake user) { } public void removeUser(UserSnowflake user) { + log.debug("Deleting user: {}", user); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("DELETE FROM users WHERE user_id = ?"); statement.setLong(1, user.getIdLong()); @@ -46,10 +51,11 @@ public void removeUser(UserSnowflake user) { } public void indexMembers(Guild guild) { - guild.loadMembers(member -> addUser(member.getUser())); + guild.loadMembers(member -> createUser(member.getUser())); } private Optional getRankInfo(int rankId) { + log.debug("Querying rank info for rank: {}", rankId); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" SELECT role_id, color, bound @@ -75,6 +81,7 @@ private Optional getRankInfo(int rankId) { } public UserInfo getUserInfo(UserSnowflake user) { + log.debug("Querying user info for user: {}", user); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" SELECT xp, rank_id, message_count, start_xp @@ -103,6 +110,7 @@ public UserInfo getUserInfo(UserSnowflake user) { } public boolean isValidMessage(Message message) { + log.debug("Checking message: {}", message); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" SELECT rank_settings.*, users.last_valid_message @@ -121,10 +129,12 @@ public boolean isValidMessage(Message message) { var validChannels = Arrays.asList((Long[]) result.getArray("valid_channels").getArray()); if (System.currentTimeMillis() - lastMessage < messageCooldown) { + log.trace("User still has cooldown ({} < {})", System.currentTimeMillis() - lastMessage, messageCooldown); return false; } if (message.getContentDisplay().length() < minimumLength) { + log.trace("Message is too short (Length: {}, Required: {}", message.getContentDisplay().length(), minimumLength); return false; } @@ -132,13 +142,22 @@ public boolean isValidMessage(Message message) { if (message.getChannelType().isThread()) { channelId = message.getChannel().asThreadChannel().getParentChannel().getIdLong(); } - return validChannels.contains(channelId); + var valid = validChannels.contains(channelId); + + if (valid) { + log.debug("Message is valid"); + } else { + log.trace("Invalid message channel"); + } + + return valid; } catch (SQLException e) { throw new RuntimeException(e); } } public void updateValidMessage(UserSnowflake user) { + log.debug("Setting last_valid_message for user {} to {}", user, System.currentTimeMillis()); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" UPDATE users @@ -157,12 +176,14 @@ public void updateValidMessage(UserSnowflake user) { } public XpChangeResult addRandomXp(UserSnowflake user) { + log.debug("Adding random xp to user: {}", user); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("SELECT * FROM add_random_xp(?)"); statement.setLong(1, user.getIdLong()); var result = statement.executeQuery(); result.next(); + log.debug("New xp: {}", result.getInt("current_xp")); return new XpChangeResult( result.getBoolean("rank_changed"), getRankInfo(result.getInt("current_rank")).orElseThrow(), @@ -175,6 +196,7 @@ public XpChangeResult addRandomXp(UserSnowflake user) { } public XpChangeResult addXp(UserSnowflake user, int amount) { + log.debug("Adding {} xp to {}", amount, user); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("SELECT * FROM add_xp(?, ?)"); statement.setLong(1, user.getIdLong()); @@ -194,6 +216,7 @@ public XpChangeResult addXp(UserSnowflake user, int amount) { } public XpChangeResult setXp(UserSnowflake user, int value) { + log.debug("Setting xp of user {} to {}", user, value); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("SELECT * FROM set_xp(?, ?)"); statement.setLong(1, user.getIdLong()); @@ -218,10 +241,12 @@ public void updateRankRoles(Member member, Guild guild, XpChangeResult result) { .map(guild::getRoleById) .filter(it -> it != validRole) .toList(); + log.debug("Updating roles for {}. Valid role: {}, invalid Roles {}", member, validRole, invalidRoles); guild.modifyMemberRoles(member, List.of(validRole), invalidRoles).queue(); } public List getRankRoleIds() { + log.debug("Querying all role ids"); try (Connection connection = dataSource.getConnection()) { var result = connection.prepareStatement("SELECT role_id FROM ranks").executeQuery(); var roleIds = new ArrayList(); @@ -235,6 +260,7 @@ public List getRankRoleIds() { } public List getLeaderboard() { + log.debug("Querying leaderboard"); try (Connection connection = dataSource.getConnection()) { var result = connection.prepareStatement(""" SELECT users.xp, users.user_id, ranks.role_id @@ -247,7 +273,9 @@ public List getLeaderboard() { List rows = new ArrayList<>(); int rowCount = 1; while (result.next()) { + log.trace("Adding row {}", rowCount); if (rowCount == 11) { + log.trace("Page is full, starting new page"); pages.add(new LeaderboardPage(rows)); rows = new ArrayList<>(); rowCount = 1; @@ -259,6 +287,7 @@ public List getLeaderboard() { )); rowCount++; } + log.debug("Result of leaderboard query: {} pages", pages.size()); return pages; } catch (SQLException e) { throw new RuntimeException(e); @@ -266,6 +295,7 @@ public List getLeaderboard() { } public void resetDailyStatistics() { + log.debug("Resetting start_xp for all users"); try (Connection connection = dataSource.getConnection()) { connection.prepareStatement("UPDATE users SET start_xp = xp").execute(); } catch (SQLException e) { @@ -274,11 +304,11 @@ public void resetDailyStatistics() { } public void increaseTotalMessageCount() { + log.debug("Increasing total message count by one"); try (Connection connection = dataSource.getConnection()) { connection.prepareStatement("SELECT * FROM increase_total_message_count()").execute(); } catch (SQLException e) { throw new RuntimeException(e); } } - } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java index 705d5ea..70474cc 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java @@ -13,10 +13,14 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Interaction(ephemeral = true) public class ModifyXpCommands { + private static final Logger log = LoggerFactory.getLogger(ModifyXpCommands.class); + @Inject private Database database; @Inject @@ -47,11 +51,14 @@ public void onSetXp(CommandEvent event, Member target, @Min(0) @Max(Integer.MAX_ } private void checkRankUpdate(XpChangeResult result, Member member, Guild guild) { + log.debug("Checking for rank up: {}", member); database.getRankService().updateRankRoles(member, guild, result); if (!result.rankChanged()) { + log.debug("Rank hasn't changed"); return; } + log.debug("Applying changes. New rank: {}", result.currentRank()); var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; var messageData = new MessageCreateBuilder().addContent(member.getAsMention()) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java index cf0f407..25f56d5 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java @@ -12,6 +12,8 @@ import com.github.kaktushose.jda.commands.dispatching.reply.components.Component; import com.github.kaktushose.nplaybot.Database; import net.dv8tion.jda.api.entities.Guild; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.List; @@ -19,6 +21,8 @@ @Interaction public class LeaderboardCommand { + private static final Logger log = LoggerFactory.getLogger(LeaderboardCommand.class); + @Inject private Database database; @Inject @@ -41,6 +45,7 @@ public void onCommand(CommandEvent event) { @Button(emoji = "⏪") public void onStart(ComponentEvent event) { index = 1; + log.trace("Leaderboard#onStart pressed, new index: {}", index); reply(event); } @@ -49,6 +54,7 @@ public void onBackward(ComponentEvent event) { if (index > minIndex) { index--; } + log.trace("Leaderboard#onBackward pressed, new index: {}", index); reply(event); } @@ -57,16 +63,19 @@ public void onForward(ComponentEvent event) { if (index < maxIndex) { index++; } + log.trace("Leaderboard#onForward pressed, new index: {}", index); reply(event); } @Button(emoji = "⏩") public void onEnd(ComponentEvent event) { index = maxIndex; + log.trace("Leaderboard#onEnd pressed, new index: {}", index); reply(event); } private void reply(Replyable event) { + log.debug("Sending new leaderboard with index {}/{}", index, maxIndex); event.with(getButtons()).reply( embedCache.getEmbed("leaderboard").injectValue("leaderboard", leaderboard.get(index - 1).getPage(guild)) .toEmbedBuilder() @@ -75,18 +84,22 @@ private void reply(Replyable event) { } private Component[] getButtons() { + log.trace("Selecting buttons for index: {}, minIndex: {}, maxIndex: {}", index, minIndex, maxIndex); if (index == minIndex) { + log.trace("Enabling bof buttons"); return List.of( Buttons.disabled("onStart", "onBackward"), Buttons.enabled("onForward", "onEnd") ).toArray(new Component[0]); } if (index == maxIndex) { + log.trace("Enabling eof buttons"); return List.of( Buttons.enabled("onStart", "onBackward"), Buttons.disabled("onForward", "onEnd") ).toArray(new Component[0]); } + log.trace("Enabling all buttons"); return List.of( Buttons.enabled("onStart", "onBackward"), Buttons.enabled("onForward", "onEnd") diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java index 0ad257e..9b1e7d4 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java @@ -20,4 +20,14 @@ public Map getEmbedValues(UserSnowflake user) { }); return result; } + + @Override + public String toString() { + return "XpChangeResult{" + + "rankChanged=" + rankChanged + + ", currentRank=" + currentRank + + ", nextRank=" + nextRank + + ", currentXp=" + currentXp + + '}'; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java index 6b0d1ff..9d446c6 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java +++ b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java @@ -47,7 +47,7 @@ public void shutdown() { private void indexTasks() { var methods = reflections.getMethodsAnnotatedWith(ScheduledTask.class); - + log.debug("Indexing tasks:"); for (var method : methods) { var scheduledTask = method.getAnnotation(ScheduledTask.class); @@ -57,9 +57,10 @@ private void indexTasks() { - (TimeUnit.HOURS.toMinutes(Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) + Calendar.getInstance().get(Calendar.MINUTE)); } - + // TODO dealy and period have to be same unit if (scheduledTask.repeat()) { - executor.scheduleAtFixedRate(execute(method), delay, scheduledTask.period(), scheduledTask.unit()); + log.debug("Scheduling repeating task {}.{}. Starts in {} minutes. Repeats every {} minutes"); + executor.scheduleAtFixedRate(execute(method), delay, scheduledTask.period(), scheduledTask.unit(); } else { executor.scheduleAtFixedRate(execute(method), delay, scheduledTask.period(), scheduledTask.unit()); } @@ -69,6 +70,7 @@ private void indexTasks() { private Runnable execute(Method method) { return () -> { try { + log.debug("Invoking task: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName()); method.invoke(method.getDeclaringClass().getConstructors()[0].newInstance(), bot); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { log.error("Exception in scheduled task!", e); From 01b2763ef0d6349a88277733c1b0153ea3a1dd3e Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:57:24 +0100 Subject: [PATCH 33/66] fix time unit bug in TaskScheduler --- .../nplaybot/scheduler/TaskScheduler.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java index 9d446c6..70f2ddd 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java +++ b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java @@ -51,18 +51,22 @@ private void indexTasks() { for (var method : methods) { var scheduledTask = method.getAnnotation(ScheduledTask.class); - var delay = scheduledTask.initialDelay(); + var delay = scheduledTask.unit().toMillis(scheduledTask.initialDelay()); + var period = scheduledTask.unit().toMillis(scheduledTask.period()); if (scheduledTask.startAtMidnight()) { delay = TimeUnit.HOURS.toMinutes(24) - (TimeUnit.HOURS.toMinutes(Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) + Calendar.getInstance().get(Calendar.MINUTE)); + delay = TimeUnit.MINUTES.toMillis(delay); } - // TODO dealy and period have to be same unit + if (scheduledTask.repeat()) { - log.debug("Scheduling repeating task {}.{}. Starts in {} minutes. Repeats every {} minutes"); - executor.scheduleAtFixedRate(execute(method), delay, scheduledTask.period(), scheduledTask.unit(); + log.debug("Scheduling repeating task {}.{}. Starts in {} ms. Repeats every {} ms", + method.getDeclaringClass().getSimpleName(), method.getName(), delay, period); + executor.scheduleAtFixedRate(execute(method), delay, period, scheduledTask.unit()); } else { - executor.scheduleAtFixedRate(execute(method), delay, scheduledTask.period(), scheduledTask.unit()); + log.debug("Scheduling task {}.{}. Starts in {} ms", method.getDeclaringClass().getSimpleName(), method.getName(), period); + executor.schedule(execute(method), delay, scheduledTask.unit()); } } } From ee3a12f636f7e9ca3fcfe63746811a241cdbd00d Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:36:51 +0100 Subject: [PATCH 34/66] implement daily messages --- embeds.json | 5 ++ .../com/github/kaktushose/nplaybot/Bot.java | 4 ++ .../kaktushose/nplaybot/rank/RankService.java | 56 +++++++++++++++---- .../rank/commands/SwitchDailyCommand.java | 4 -- .../nplaybot/rank/daily/DailyMessageTask.java | 25 +++++++++ .../rank/daily/SwitchDailyMessageCommand.java | 24 ++++++++ .../nplaybot/rank/model/RankInfo.java | 2 +- .../nplaybot/rank/model/UserInfo.java | 18 ++++++ 8 files changed, 123 insertions(+), 15 deletions(-) delete mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/commands/SwitchDailyCommand.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/daily/DailyMessageTask.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/daily/SwitchDailyMessageCommand.java diff --git a/embeds.json b/embeds.json index aec9acf..2055809 100644 --- a/embeds.json +++ b/embeds.json @@ -104,5 +104,10 @@ "title": "Leaderboard", "description": "{leaderboard}", "color": "#67c94f" + }, + "switchDaily" : { + "title": "Erfolg", + "description": "Die tägliche Kontoinformation wurde {switch}", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 2da7e35..8196d27 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -74,6 +74,10 @@ public void shutdown() { database.closeDataSource(); } + public JDA getJda() { + return jda; + } + @Produces(skipIndexing = true) public Database getDatabase() { return database; diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index fe0647c..60d251b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -4,20 +4,14 @@ import com.github.kaktushose.nplaybot.rank.model.RankInfo; import com.github.kaktushose.nplaybot.rank.model.UserInfo; import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.entities.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; +import java.util.*; public class RankService { @@ -58,7 +52,7 @@ private Optional getRankInfo(int rankId) { log.debug("Querying rank info for rank: {}", rankId); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT role_id, color, bound + SELECT role_id, name, color, bound FROM ranks WHERE rank_id = ? """ @@ -70,6 +64,7 @@ private Optional getRankInfo(int rankId) { if (result.next()) { return Optional.of(new RankInfo( result.getLong("role_id"), + result.getString("name"), result.getString("color"), result.getInt("bound") )); @@ -134,7 +129,7 @@ public boolean isValidMessage(Message message) { } if (message.getContentDisplay().length() < minimumLength) { - log.trace("Message is too short (Length: {}, Required: {}", message.getContentDisplay().length(), minimumLength); + log.trace("Message is too short (Length: {}, Required: {}", message.getContentDisplay().length(), minimumLength); return false; } @@ -311,4 +306,45 @@ public void increaseTotalMessageCount() { throw new RuntimeException(e); } } + + public void switchDaily(UserSnowflake user, boolean enabled) { + log.debug("Setting daily message for user {} to {}", user, enabled); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("UPDATE users SET daily_message = ? where user_id = ?"); + statement.setBoolean(1, enabled); + statement.setLong(2, user.getIdLong()); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public Map getDailyRankInfos() { + log.debug("Querying daily rank infos"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT user_id, xp, rank_id, message_count, start_xp + FROM users + WHERE daily_message = true + """ + ); + var result = statement.executeQuery(); + var users = new HashMap(); + while (result.next()) { + var currentRank = getRankInfo(result.getInt("rank_id")).orElseThrow(); + var nextRank = getRankInfo(result.getInt("rank_id") + 1); + + users.put(result.getLong("user_id"), new UserInfo( + result.getInt("xp"), + currentRank, + nextRank, + result.getInt("message_count"), + result.getInt("xp") - result.getInt("start_xp") + )); + } + return users; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SwitchDailyCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SwitchDailyCommand.java deleted file mode 100644 index a1eca88..0000000 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/SwitchDailyCommand.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.github.kaktushose.nplaybot.rank.commands; - -public class SwitchDailyCommand { -} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/daily/DailyMessageTask.java b/src/main/java/com/github/kaktushose/nplaybot/rank/daily/DailyMessageTask.java new file mode 100644 index 0000000..b6408ee --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/daily/DailyMessageTask.java @@ -0,0 +1,25 @@ +package com.github.kaktushose.nplaybot.rank.daily; + +import com.github.kaktushose.nplaybot.Bot; +import com.github.kaktushose.nplaybot.scheduler.ScheduledTask; +import net.dv8tion.jda.api.entities.User; + +import java.util.concurrent.TimeUnit; + +public class DailyMessageTask { + + @ScheduledTask(period = 24, unit = TimeUnit.HOURS, startAtMidnight = true) + public void onSendDailyMessages(Bot bot) { + bot.getDatabase().getRankService().getDailyRankInfos().forEach((userId, userInfo) -> + bot.getJda().retrieveUserById(userId) + .flatMap(User::openPrivateChannel) + .flatMap(channel -> + channel.sendMessage( + bot.getEmbedCache().getEmbed(userInfo.nextRank().isPresent() ? "rankInfo" : "rankInfoMax") + .injectValues(userInfo.getEmbedValues(channel.getUser())).toMessageCreateData() + ) + ).queue() + ); + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/daily/SwitchDailyMessageCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/daily/SwitchDailyMessageCommand.java new file mode 100644 index 0000000..8301a57 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/daily/SwitchDailyMessageCommand.java @@ -0,0 +1,24 @@ +package com.github.kaktushose.nplaybot.rank.daily; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Param; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.Database; + +@Interaction +public class SwitchDailyMessageCommand { + + @Inject + private EmbedCache embedCache; + @Inject + private Database database; + + @SlashCommand(value = "täglich", desc = "Ändert die Einstellungen für die tägliche Kontoinformation") + public void onCommand(CommandEvent event, @Param(value = "Ob du eine tägliche Nachricht erhalten möchtest oder nicht", name = "aktivieren") boolean enabled) { + database.getRankService().switchDaily(event.getUser(), enabled); + event.reply(embedCache.getEmbed("switchDaily").injectValue("switch", enabled ? "aktiviert" : "deaktiviert")); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java index 01317d6..d0ed97d 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java @@ -1,4 +1,4 @@ package com.github.kaktushose.nplaybot.rank.model; -public record RankInfo(long roleId, String color, int xpBound) { +public record RankInfo(long roleId, String name, String color, int xpBound) { } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java index 1ef364b..af6e222 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot.rank.model; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.User; import java.util.HashMap; import java.util.Map; @@ -24,4 +25,21 @@ public Map getEmbedValues(Member member) { }); return result; } + + public Map getEmbedValues(User user) { + var result = new HashMap() {{ + put("user", String.format("<@%d>", user.getIdLong())); + put("color", currentRank.color()); + put("avatarUrl", user.getEffectiveAvatarUrl()); + put("currentRank", currentRank.name()); + put("currentXp", currentXp); + put("xpGain", xpGain); + put("messageCount", messageCount); + }}; + nextRank.ifPresent(rank -> { + result.put("nextRank", rank.name()); + result.put("missingXp", rank.xpBound() - currentXp); + }); + return result; + } } From 1a859bf6a60a5cd688d3dc82fffee459481e0aa6 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sun, 7 Jan 2024 15:20:26 +0100 Subject: [PATCH 35/66] implement xp loot drops --- .env.example | 1 - .gitignore | 1 - embeds.json | 11 +- .../nplaybot/rank/RankListener.java | 89 ++++++++- .../kaktushose/nplaybot/rank/RankService.java | 73 +++++-- .../migration/V1.0.0__setup_rank_system.sql | 20 +- wait-for-it.sh | 183 ++++++++++++++++++ 7 files changed, 346 insertions(+), 32 deletions(-) create mode 100755 wait-for-it.sh diff --git a/.env.example b/.env.example index a4573c8..02112a6 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,3 @@ POSTGRES_USER=user POSTGRES_PASSWORD=password POSTGRES_URL=jdbc:postgresql://postgres:5432/database GF_SECURITY_ADMIN_PASSWORD=password -BOT_GUILD=0123456789 diff --git a/.gitignore b/.gitignore index 0306ffc..c0611b1 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,3 @@ buildNumber.properties *.env /data /logs -wait-for-it.sh diff --git a/embeds.json b/embeds.json index 2055809..2dc865e 100644 --- a/embeds.json +++ b/embeds.json @@ -90,12 +90,12 @@ "description": "Der EmbedCache wurde aktualisiert", "color": "#67c94f" }, - "setXpResult" : { + "setXpResult": { "title": "Erfolg", "description": "Die XP von {user} wurden auf {xp} XP gesetzt", "color": "#67c94f" }, - "addXpResult" : { + "addXpResult": { "title": "Erfolg", "description": "{user} wurden {xp} XP hinzugefügt", "color": "#67c94f" @@ -105,9 +105,14 @@ "description": "{leaderboard}", "color": "#67c94f" }, - "switchDaily" : { + "switchDaily": { "title": "Erfolg", "description": "Die tägliche Kontoinformation wurde {switch}", "color": "#67c94f" + }, + "xpLootDropClaimed": { + "title": "Glückwunsch :tada:", + "description": "{user} hat {xp} XP :star2: gefunden!", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index 83ed176..e4d6020 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -2,32 +2,43 @@ import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; import com.github.kaktushose.nplaybot.settings.SettingsService; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + public class RankListener extends ListenerAdapter { private static final Logger log = LoggerFactory.getLogger(RankListener.class); private final RankService rankService; private final SettingsService settingsService; private final EmbedCache embedCache; + private final Map xpLootDrops; public RankListener(Database database, EmbedCache embedCache) { this.rankService = database.getRankService(); this.settingsService = database.getSettingsService(); this.embedCache = embedCache; + xpLootDrops = new HashMap<>(); } @Override public void onMessageReceived(@NotNull MessageReceivedEvent event) { log.debug("Received message event"); var author = event.getAuthor(); - var message = event.getMessage(); if (author.isBot()) { log.trace("Author is bot"); @@ -40,16 +51,30 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { rankService.increaseTotalMessageCount(); - if (!rankService.isValidMessage(message)) { - log.trace("Message doesn't meet rank criteria"); + if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { return; } - rankService.updateValidMessage(author); - var result = rankService.addRandomXp(author); + onXpLootDrop(event); + + if (!rankService.isValidMessage(event.getMessage())) { + log.debug("Message doesn't meet rank criteria"); + return; + } + + onAddRegularXp(event); + } + + private void onAddRegularXp(MessageReceivedEvent event) { + rankService.updateValidMessage(event.getAuthor()); + var result = rankService.addRandomXp(event.getAuthor()); - log.debug("Checking for rank up: {}", author); - rankService.updateRankRoles(event.getMember(), event.getGuild(), result); + onXpChange(result, event.getMember(), event.getGuild()); + } + + private void onXpChange(XpChangeResult result, Member member, Guild guild) { + log.debug("Checking for rank up: {}", member); + rankService.updateRankRoles(member, guild, result); if (!result.rankChanged()) { log.debug("Rank hasn't changed"); @@ -58,9 +83,53 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { log.debug("Applying changes. New rank: {}", result.currentRank()); var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; - var messageData = new MessageCreateBuilder().addContent(author.getAsMention()) - .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(author)).toMessageEmbed()) + var messageData = new MessageCreateBuilder().addContent(member.getAsMention()) + .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).toMessageEmbed()) .build(); - settingsService.getBotChannel(event.getGuild()).sendMessage(messageData).queue(); + settingsService.getBotChannel(guild).sendMessage(messageData).queue(); + } + + private void onXpLootDrop(MessageReceivedEvent event) { + var xp = rankService.getXpLootDrop(event.getMessage()); + + if (xp < 1) { + log.debug("No xp loot drop for this message"); + return; + } + + xpLootDrops.put(event.getMessageIdLong(), xp); + event.getMessage().addReaction(Emoji.fromUnicode("\uD83C\uDF1F")).queue(); + } + + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + if (event.getUser().isBot()) { + return; + } + + var messageId = event.getMessageIdLong(); + if (!xpLootDrops.containsKey(messageId)) { + return; + } + if (!event.getEmoji().equals(Emoji.fromUnicode("\uD83C\uDF1F"))) { + return; + } + + log.debug("Xp loot drop got claimed by {}", event.getMember()); + + var xp = xpLootDrops.get(messageId); + var result = rankService.addXp(event.getMember(), xp); + onXpChange(result, event.getMember(), event.getGuild()); + xpLootDrops.remove(messageId); + + event.retrieveMessage().queue(message -> { + message.reply( + embedCache.getEmbed("xpLootDropClaimed") + .injectValue("user", event.getMember().getAsMention()) + .injectValue("xp", xp) + .toMessageCreateData() + ).queue(it -> it.delete().queueAfter(10, TimeUnit.SECONDS)); + message.clearReactions().queue(); + }); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 60d251b..194ee0c 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -5,6 +5,10 @@ import com.github.kaktushose.nplaybot.rank.model.UserInfo; import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.channel.Channel; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -121,31 +125,19 @@ public boolean isValidMessage(Message message) { var lastMessage = result.getLong("last_valid_message"); var messageCooldown = result.getInt("message_cooldown"); var minimumLength = result.getInt("min_message_length"); - var validChannels = Arrays.asList((Long[]) result.getArray("valid_channels").getArray()); if (System.currentTimeMillis() - lastMessage < messageCooldown) { - log.trace("User still has cooldown ({} < {})", System.currentTimeMillis() - lastMessage, messageCooldown); + log.debug("User still has cooldown ({} < {})", System.currentTimeMillis() - lastMessage, messageCooldown); return false; } if (message.getContentDisplay().length() < minimumLength) { - log.trace("Message is too short (Length: {}, Required: {}", message.getContentDisplay().length(), minimumLength); + log.debug("Message is too short (Length: {}, Required: {}", message.getContentDisplay().length(), minimumLength); return false; } - var channelId = message.getChannelIdLong(); - if (message.getChannelType().isThread()) { - channelId = message.getChannel().asThreadChannel().getParentChannel().getIdLong(); - } - var valid = validChannels.contains(channelId); - - if (valid) { - log.debug("Message is valid"); - } else { - log.trace("Invalid message channel"); - } - - return valid; + log.debug("Message is valid"); + return true; } catch (SQLException e) { throw new RuntimeException(e); } @@ -347,4 +339,53 @@ public Map getDailyRankInfos() { throw new RuntimeException(e); } } + + public int getXpLootDrop(Message message) { + log.debug("Querying xp loot drop for message {}", message); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM get_xp_loot_drop(?)"); + statement.setLong(1, message.getGuildIdLong()); + + var result = statement.executeQuery(); + result.next(); + var xp = result.getInt(1); + log.debug("Xp loot drop: {} xp", xp); + return xp; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public boolean isValidChannel(MessageChannelUnion channel, Guild guild) { + log.debug("Checking channel: {}", channel); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT rank_settings.valid_channels + FROM rank_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + var validChannels = Arrays.asList((Long[]) result.getArray("valid_channels").getArray()); + + var channelId = channel.getIdLong(); + if (channel.getType().isThread()) { + channelId = channel.asThreadChannel().getParentChannel().getIdLong(); + } + var valid = validChannels.contains(channelId); + + if (valid) { + log.debug("Channel is valid"); + } else { + log.debug("Invalid message channel"); + } + + return valid; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql index d137593..458d1cc 100644 --- a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql +++ b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql @@ -9,7 +9,8 @@ CREATE TABLE rank_settings ( guild_id BIGINT NOT NULL PRIMARY KEY, message_cooldown INT NOT NULL, min_message_length INT NOT NULL, - valid_channels BIGINT[] NOT NULL + valid_channels BIGINT[] NOT NULL, + xp_loot_chance REAL NOT NULL ); CREATE TABLE ranks ( @@ -178,3 +179,20 @@ BEGIN ON CONFLICT (DATE) DO UPDATE SET total_message_count = rank_statistics.total_message_count + 1; END; $$ LANGUAGE plpgsql; + +CREATE FUNCTION get_xp_loot_drop(id BIGINT) +RETURNS TABLE (xp INT) AS +$$ +DECLARE + chance INT; + drop_chance REAL; +BEGIN + SELECT rank_settings.xp_loot_chance INTO drop_chance FROM rank_settings WHERE guild_id = id; + chance := floor(random() * 100) + 1; + IF ROUND(drop_chance) >= chance THEN + RETURN QUERY SELECT get_random_xp FROM get_random_xp(); + ELSE + RETURN QUERY SELECT 0; + END IF; +END; +$$ LANGUAGE plpgsql; diff --git a/wait-for-it.sh b/wait-for-it.sh new file mode 100755 index 0000000..64019e8 --- /dev/null +++ b/wait-for-it.sh @@ -0,0 +1,183 @@ +#!/usr/bin/env bash +# https://github.com/vishnubob/wait-for-it +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi From fa6fc9583d99783ad8db18ed7208c63dc1210039 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sun, 7 Jan 2024 15:37:30 +0100 Subject: [PATCH 36/66] add embeds.json to docker build --- .env.example | 1 + Dockerfile | 1 + docker-compose.yml | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 02112a6..a4573c8 100644 --- a/.env.example +++ b/.env.example @@ -3,3 +3,4 @@ POSTGRES_USER=user POSTGRES_PASSWORD=password POSTGRES_URL=jdbc:postgresql://postgres:5432/database GF_SECURITY_ADMIN_PASSWORD=password +BOT_GUILD=0123456789 diff --git a/Dockerfile b/Dockerfile index ec363af..f07efc2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,7 @@ COPY src/main/resources/db/migration ./db/migration COPY --from=builder /bot/*.sh . COPY --from=builder /bot/target/NPLAY-Bot.jar ./NPLAY-Bot.jar +COPY --from=builder /bot/embeds.json . RUN chmod +x ./wait-for-it.sh ./entrypoint.sh diff --git a/docker-compose.yml b/docker-compose.yml index a0a1f9a..4477b44 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,8 +26,7 @@ services: - postgres bot: - build: - context: . + image: ghcr.io/kaktushose/levelbot/nplaybot:latest container_name: nplay-bot restart: on-failure environment: From 9bb999de08956491997c178797a117ebb1182498 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Tue, 23 Apr 2024 19:51:59 +0200 Subject: [PATCH 37/66] implement contest events --- embeds.json | 10 + pom.xml | 10 +- .../com/github/kaktushose/nplaybot/Bot.java | 4 +- .../github/kaktushose/nplaybot/Database.java | 7 + .../events/contest/ContestCommands.java | 53 ++++++ .../events/contest/ContestEventService.java | 178 ++++++++++++++++++ .../events/contest/ContestListener.java | 104 ++++++++++ .../nplaybot/rank/RankListener.java | 3 +- .../kaktushose/nplaybot/rank/RankService.java | 10 +- .../rank/leaderboard/LeaderboardPage.java | 10 +- .../nplaybot/settings/SettingsService.java | 5 + .../migration/V2.0.0__setup_event_system.sql | 10 + 12 files changed, 387 insertions(+), 17 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java create mode 100644 src/main/resources/db/migration/V2.0.0__setup_event_system.sql diff --git a/embeds.json b/embeds.json index 2dc865e..c190376 100644 --- a/embeds.json +++ b/embeds.json @@ -114,5 +114,15 @@ "title": "Glückwunsch :tada:", "description": "{user} hat {xp} XP :star2: gefunden!", "color": "#67c94f" + }, + "contestEventStart": { + "title": "Okay", + "description": "Das Content-Event wurde in {channel} gestartet", + "color": "#67c94f" + }, + "contestEventStop": { + "title": "Leaderboard", + "description": "{leaderboard}", + "color": "#67c94f" } } diff --git a/pom.xml b/pom.xml index 6835783..83f3499 100644 --- a/pom.xml +++ b/pom.xml @@ -72,12 +72,12 @@ net.dv8tion JDA - 5.0.0-beta.18 + 5.0.0-beta.22 com.github.Kaktushose jda-commands - 3d823b1173 + 4.0.0-beta.2 com.zaxxer @@ -87,7 +87,7 @@ org.postgresql postgresql - 42.7.0 + 42.7.3 org.slf4j @@ -97,12 +97,12 @@ ch.qos.logback logback-classic - 1.4.11 + 1.4.14 ch.qos.logback logback-core - 1.4.11 + 1.4.12 diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 8196d27..9cc8730 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -3,6 +3,7 @@ import com.github.kaktushose.jda.commands.JDACommands; import com.github.kaktushose.jda.commands.annotations.Produces; import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.events.contest.ContestListener; import com.github.kaktushose.nplaybot.rank.JoinLeaveListener; import com.github.kaktushose.nplaybot.rank.RankListener; import com.github.kaktushose.nplaybot.scheduler.TaskScheduler; @@ -44,7 +45,8 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { .setStatus(OnlineStatus.IDLE) .addEventListeners( new RankListener(database, embedCache), - new JoinLeaveListener(database.getRankService()) + new JoinLeaveListener(database.getRankService()), + new ContestListener(database.getContestEventService()) ) .build().awaitReady(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 26333d3..617fdf4 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot; +import com.github.kaktushose.nplaybot.events.contest.ContestEventService; import com.github.kaktushose.nplaybot.rank.RankService; import com.github.kaktushose.nplaybot.settings.SettingsService; import com.zaxxer.hikari.HikariConfig; @@ -10,6 +11,7 @@ public class Database { private final HikariDataSource dataSource; private final SettingsService settingsService; private final RankService rankService; + private final ContestEventService contestEventService; public Database() { var config = new HikariConfig(); @@ -23,6 +25,7 @@ public Database() { settingsService = new SettingsService(dataSource); rankService = new RankService(dataSource); + contestEventService = new ContestEventService(dataSource); } public void closeDataSource() { @@ -36,4 +39,8 @@ public SettingsService getSettingsService() { public RankService getRankService() { return rankService; } + + public ContestEventService getContestEventService() { + return contestEventService; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java new file mode 100644 index 0000000..bf0a53c --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java @@ -0,0 +1,53 @@ +package com.github.kaktushose.nplaybot.events.contest; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Param; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.Database; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + +import java.util.Optional; + +@Interaction +public class ContestCommands { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + + @SlashCommand(value = "contest event start", desc = "Startet ein Contest-Event", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + public void onContestEventStart(CommandEvent event, + @Param("Der Textkanal, in dem das Contest-Event stattfinden soll") TextChannel channel, + @Param("Der Emoji, mit dem abgestimmt werden soll") String emoji) { + database.getContestEventService().startContestEvent(channel, emoji); + event.reply(embedCache.getEmbed("contestEventStart").injectValue("channel", channel.getAsMention())); + } + + @SlashCommand(value = "contest event stop", desc = "Stoppt das aktuelle Contest-Event und zeigt die Gewinner an", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + public void onContestEventStop(CommandEvent event) { + var result = database.getContestEventService().stopContestEvent(event.getGuild()); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < result.size(); i++) { + var row = result.get(i); + builder.append(String.format("%d. %s (%d Votes)\n", i, resolveName(event.getGuild(), row.userId()), row.votes())); + } + event.reply(embedCache.getEmbed("contestEventStop").injectValue("leaderboard", builder.toString())); + } + + + private String resolveName(Guild guild, long userId) { + var member = Optional.ofNullable(guild.getMemberById(userId)); + if (member.isPresent()) { + return member.get().getEffectiveName(); + } + // retrieve member thus it gets loaded to cache + guild.retrieveMemberById(userId).queue(); + return String.format("<@%d>", userId); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java new file mode 100644 index 0000000..433ddd1 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java @@ -0,0 +1,178 @@ +package com.github.kaktushose.nplaybot.events.contest; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class ContestEventService { + + private static final Logger log = LoggerFactory.getLogger(ContestEventService.class); + private final DataSource dataSource; + public ContestEventService(DataSource dataSource) { + this.dataSource = dataSource; + } + + public long getContestEventChannel(Guild guild) { + log.debug("Querying contest event channel"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT contest_channel_id + FROM event_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getLong(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public String getVoteEmoji(Guild guild) { + log.debug("Querying vote emoji"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT contest_vote_emoji + FROM event_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getString(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void startContestEvent(TextChannel channel, String emoji) { + log.debug("Starting new contest event in {} with vote emoji {}", channel, emoji); + try (Connection connection = dataSource.getConnection()) { + log.debug("Truncating old votes"); + connection.prepareStatement("TRUNCATE TABLE contest_entries;").execute(); + + var statement = connection.prepareStatement(""" + UPDATE event_settings + SET contest_channel_id = ?, + contest_vote_emoji = ? + WHERE guild_id = ? + """ + ); + statement.setLong(1, channel.getIdLong()); + statement.setString(2, emoji); + statement.setLong(3, channel.getGuild().getIdLong()); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public List stopContestEvent(Guild guild) { + log.debug("Stopping current contest event"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE event_settings + SET contest_channel_id = -1, + contest_vote_emoji = '' + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + statement.execute(); + + log.debug("Querying top 10 entries"); + statement = connection.prepareStatement(""" + SELECT user_id, votes + FROM contest_entries + ORDER BY votes DESC LIMIT 10 + """ + ); + List users = new ArrayList<>(); + var result = statement.executeQuery(); + while (result.next()) { + users.add(new ContestLeaderboardRow(result.getInt("votes"), result.getLong("user_id"))); + } + return users; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public boolean createContestEntry(Message message) { + log.debug("Inserting contest entry: {}", message); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("INSERT INTO contest_entries VALUES(?, 0, ?) ON CONFLICT DO NOTHING"); + statement.setLong(1, message.getAuthor().getIdLong()); + statement.setLong(2, message.getIdLong()); + return statement.executeUpdate() > 0; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void deleteContestEntry(long messageId) { + log.debug("Deleting contest entry: {}", messageId); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("DELETE FROM contest_entries WHERE message_id = ?"); + statement.setLong(1, messageId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * Increases the vote count for a contest entry. + * + * @param messageId the message id representing the contest entry + * @param userId the id of the user that voted for the entry + */ + public void increaseVoteCount(long messageId, long userId) { + log.debug("Increasing total votes for {} by one", messageId); + try (Connection connection = dataSource.getConnection()) { + // this AND clause prevents a self vote of the message author + var statement = connection.prepareStatement("UPDATE contest_entries SET votes = votes + 1 where message_id = ? AND user_id != ?"); + statement.setLong(1, messageId); + statement.setLong(2, userId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * Decreases the vote count for a contest entry. + * + * @param messageId the message id representing the contest entry + * @param userId the id of the user that removed his vote for the entry + */ + public void decreaseVoteCount(long messageId, long userId) { + log.debug("Decreasing total votes for {} by one", messageId); + try (Connection connection = dataSource.getConnection()) { + // this AND clause prevents a self vote of the message author + var statement = connection.prepareStatement("UPDATE contest_entries SET votes = votes - 1 where message_id = ? AND user_id != ?"); + statement.setLong(1, messageId); + statement.setLong(2, userId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public record ContestLeaderboardRow(int votes, long userId) { + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java new file mode 100644 index 0000000..6713bc5 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java @@ -0,0 +1,104 @@ +package com.github.kaktushose.nplaybot.events.contest; + +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.events.message.MessageDeleteEvent; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveAllEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEmojiEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ContestListener extends ListenerAdapter { + + private static final Logger log = LoggerFactory.getLogger(ContestListener.class); + private final ContestEventService eventService; + + public ContestListener(ContestEventService eventService) { + this.eventService = eventService; + } + + @Override + public void onMessageReceived(@NotNull MessageReceivedEvent event) { + if (!event.isFromGuild()) { + return; + } + if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + return; + } + if (event.getAuthor().isBot()) { + return; + } + + if (eventService.createContestEntry(event.getMessage())) { + log.debug("Created new contest entry: {}", event.getMessage()); + event.getMessage().addReaction(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild()))).queue(); + } else { + log.debug("User already has a contest entry, deleting message"); + event.getMessage().delete().queue(); + } + } + + @Override + public void onMessageDelete(@NotNull MessageDeleteEvent event) { + eventService.deleteContestEntry(event.getMessageIdLong()); + } + + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + return; + } + if (event.getUser().isBot()) { + return; + } + if (event.getUser().getIdLong() == event.getMessageAuthorIdLong()) { + log.debug("Removing self vote from contest entry"); + event.retrieveMessage().flatMap(message -> message.removeReaction(event.getEmoji(), event.getUser())).queue(); + return; + } + if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild())))) { + log.debug("Removing invalid emoji from contest entry"); + event.retrieveMessage().flatMap(message -> message.removeReaction(event.getEmoji(), event.getUser())).queue(); + return; + } + eventService.increaseVoteCount(event.getMessageIdLong(), event.getUserIdLong()); + } + + @Override + public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + return; + } + if (event.getUser().isBot()) { + return; + } + if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild())))) { + return; + } + eventService.decreaseVoteCount(event.getMessageIdLong(), event.getUserIdLong()); + } + + @Override + public void onMessageReactionRemoveEmoji(@NotNull MessageReactionRemoveEmojiEvent event) { + if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild())))) { + return; + } + log.debug("Detected removal of all vote emojis. Adding initial emoji again"); + event.getChannel().retrieveMessageById(event.getMessageId()).flatMap(message -> + message.addReaction(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild()))) + ).queue(); + + } + + @Override + public void onMessageReactionRemoveAll(@NotNull MessageReactionRemoveAllEvent event) { + log.debug("Detected removal of all vote emojis. Adding initial emoji again"); + event.getChannel().retrieveMessageById(event.getMessageId()).flatMap(message -> + message.addReaction(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild()))) + ).queue(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index e4d6020..c1766c1 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -6,7 +6,6 @@ import com.github.kaktushose.nplaybot.settings.SettingsService; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; @@ -128,7 +127,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { .injectValue("user", event.getMember().getAsMention()) .injectValue("xp", xp) .toMessageCreateData() - ).queue(it -> it.delete().queueAfter(10, TimeUnit.SECONDS)); + ).mentionRepliedUser(false).queue(it -> it.delete().queueAfter(10, TimeUnit.SECONDS)); message.clearReactions().queue(); }); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 194ee0c..770c75f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -4,10 +4,10 @@ import com.github.kaktushose.nplaybot.rank.model.RankInfo; import com.github.kaktushose.nplaybot.rank.model.UserInfo; import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; -import net.dv8tion.jda.api.entities.*; -import net.dv8tion.jda.api.entities.channel.Channel; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,7 +19,7 @@ public class RankService { - private static final Logger log = LoggerFactory.getLogger(RankListener.class); + private static final Logger log = LoggerFactory.getLogger(RankService.class); private final DataSource dataSource; public RankService(DataSource dataSource) { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java index 0c64b3b..c8c1376 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java @@ -1,14 +1,13 @@ package com.github.kaktushose.nplaybot.rank.leaderboard; import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.exceptions.ErrorHandler; +import net.dv8tion.jda.api.requests.ErrorResponse; import java.util.List; import java.util.Optional; public record LeaderboardPage(List rows) { - public record LeaderboardRow(int xp, long userId, long roleId) { - } - public String getPage(Guild guild) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < rows.size(); i++) { @@ -28,8 +27,11 @@ private String resolveName(Guild guild, long userId) { return member.get().getEffectiveName(); } // retrieve member thus it gets loaded to cache - guild.retrieveMemberById(userId).queue(); + guild.retrieveMemberById(userId).queue(null, new ErrorHandler().ignore(ErrorResponse.UNKNOWN_MEMBER)); return String.format("<@%d>", userId); } + public record LeaderboardRow(int xp, long userId, long roleId) { + } + } diff --git a/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java b/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java index 72debcf..c21c572 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java @@ -2,6 +2,8 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.sql.Connection; @@ -9,6 +11,7 @@ public class SettingsService { + private static final Logger log = LoggerFactory.getLogger(SettingsService.class); private final DataSource dataSource; public SettingsService(DataSource dataSource) { @@ -16,6 +19,7 @@ public SettingsService(DataSource dataSource) { } public String getBotToken(long guildId) { + log.debug("Querying bot token"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" SELECT bot_token @@ -34,6 +38,7 @@ public String getBotToken(long guildId) { } public TextChannel getBotChannel(Guild guild) { + log.debug("Querying bot channel"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" SELECT bot_channel_id diff --git a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql new file mode 100644 index 0000000..fdae2f8 --- /dev/null +++ b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql @@ -0,0 +1,10 @@ +CREATE TABLE event_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + contest_channel_id BIGINT NOT NULL DEFAULT 0, + contest_vote_emoji VARCHAR +); +CREATE TABLE contest_entries ( + user_id BIGINT NOT NULL PRIMARY KEY, + votes INT NOT NULL DEFAULT 0, + message_id BIGINT NOT NULL +); From 31f58a6aa5403b36c39fe7134e4187e5e2d12f55 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:42:10 +0200 Subject: [PATCH 38/66] implement rank config commands --- embeds.json | 11 ++ .../kaktushose/nplaybot/rank/RankService.java | 116 ++++++++++++++++++ .../rank/commands/RankConfigCommands.java | 73 +++++++++++ .../nplaybot/rank/model/RankConfig.java | 4 + 4 files changed, 204 insertions(+) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/model/RankConfig.java diff --git a/embeds.json b/embeds.json index c190376..6b8b5e3 100644 --- a/embeds.json +++ b/embeds.json @@ -124,5 +124,16 @@ "title": "Leaderboard", "description": "{leaderboard}", "color": "#67c94f" + }, + "rankConfig": { + "title": "Rank Configuration", + "description": "Cooldown: `{cooldown}ms`\nNachrichtenlänge: `{minLength} Buchstaben`\nXP Loot Wahrscheinlichkeit: `{lootChance}%`\n", + "color": "#67c94f" + + }, + "validChannels": { + "title": "Gewertete Textkanäle", + "description": "{channels}", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 770c75f..ae5563c 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot.rank; import com.github.kaktushose.nplaybot.rank.leaderboard.LeaderboardPage; +import com.github.kaktushose.nplaybot.rank.model.RankConfig; import com.github.kaktushose.nplaybot.rank.model.RankInfo; import com.github.kaktushose.nplaybot.rank.model.UserInfo; import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; @@ -108,6 +109,121 @@ public UserInfo getUserInfo(UserSnowflake user) { } } + public RankConfig getRankConfig(Guild guild) { + log.debug("Querying rank config for guild: {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT message_cooldown, min_message_length, xp_loot_chance + FROM rank_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + + return new RankConfig( + result.getInt("message_cooldown"), + result.getInt("min_message_length"), + result.getDouble("xp_loot_chance") + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void updateCooldown(Guild guild, int cooldown) { + log.debug("Updating cooldown for guild: {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE rank_settings + SET message_cooldown = ? + WHERE guild_id = ? + """ + ); + statement.setInt(1, cooldown); + statement.setLong(2, guild.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void updateMinMessageLength(Guild guild, int length) { + log.debug("Updating minimum message length for guild: {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE rank_settings + SET min_message_length = ? + WHERE guild_id = ? + """ + ); + statement.setInt(1, length); + statement.setLong(2, guild.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void updateXpLootChance(Guild guild, double chance) { + log.debug("Updating xp loot chance for guild: {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE rank_settings + SET xp_loot_chance = ? + WHERE guild_id = ? + """ + ); + statement.setDouble(1, chance); + statement.setLong(2, guild.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public Set getValidChannels(Guild guild) { + log.debug("Querying valid channels for guild {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT valid_channels + FROM rank_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return new HashSet<>(Arrays.asList((Long[]) result.getArray("valid_channels").getArray())); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void updateValidChannels(Guild guild, Set validChannels) { + log.debug("Querying valid channels for guild {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE rank_settings + SET valid_channels = ? + WHERE guild_id = ? + """ + ); + statement.setArray(1, connection.createArrayOf("BIGINT", validChannels.toArray())); + statement.setLong(2, guild.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public boolean isValidMessage(Message message) { log.debug("Checking message: {}", message); try (Connection connection = dataSource.getConnection()) { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java new file mode 100644 index 0000000..b59933d --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java @@ -0,0 +1,73 @@ +package com.github.kaktushose.nplaybot.rank.commands; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.constraints.Max; +import com.github.kaktushose.jda.commands.annotations.constraints.Min; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Param; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.Database; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + +@Interaction +public class RankConfigCommands { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + + @SlashCommand(value = "rank config", desc = "Zeigt die Einstellungen für das Rank System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onGetRankConfig(CommandEvent event) { + event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); + } + + @SlashCommand(value = "set cooldown", desc = "Legt den Cooldown für gewertete Nachrichten fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onSetCooldown(CommandEvent event, @Param("Die Dauer in Millisekunden") @Min(0) @Max(Integer.MAX_VALUE) int cooldown) { + database.getRankService().updateCooldown(event.getGuild(), cooldown); + event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); + } + + @SlashCommand(value = "set message length", desc = "Legt die Mindestlänge für gewertete Nachrichten fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onSetMinMessageLength(CommandEvent event, @Param("Die Mindestanzahl an Buchstaben pro Nachricht") @Min(0) @Max(Integer.MAX_VALUE) int length) { + database.getRankService().updateMinMessageLength(event.getGuild(), length); + event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); + } + + @SlashCommand(value = "set loot chance", desc = "Legt die Wahrscheinlichkeit für zufällige XP-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onSetXpLootDropChance(CommandEvent event, @Param("Die Wahrscheinlichkeit in Prozent") @Min(1) @Max(100) double chance) { + database.getRankService().updateXpLootChance(event.getGuild(), chance); + event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); + } + + @SlashCommand(value = "valid channels list", desc = "Zeigt die Textkanäle an, in denen Nachrichten gewertet werden", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onValidChannelsList(CommandEvent event) { + var channels = database.getRankService().getValidChannels(event.getGuild()); + StringBuilder result = new StringBuilder(); + channels.forEach(it -> result.append(String.format("<#%d>", it)).append("\n")); + event.reply(embedCache.getEmbed("validChannels").injectValue("channels", result)); + } + + @SlashCommand(value = "valid channels add", desc = "Fügt einen Textkanal zu der Liste der gewerteten Kanäle hinzu", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onValidChannelsAdd(CommandEvent event, @Param("Der Kanal der gewertet werden soll") TextChannel channel) { + var channels = database.getRankService().getValidChannels(event.getGuild()); + channels.add(channel.getIdLong()); + database.getRankService().updateValidChannels(event.getGuild(), channels); + StringBuilder result = new StringBuilder(); + channels.forEach(it -> result.append(String.format("<#%d>", it)).append("\n")); + event.reply(embedCache.getEmbed("validChannels").injectValue("channels", result)); + } + + @SlashCommand(value = "valid channels remove", desc = "Entfernt einen Textkanal von der Liste der gewerteten Kanäle", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onValidChannelsRemove(CommandEvent event, @Param("Der Kanal der nicht mehr gewertet werden soll") TextChannel channel) { + var channels = database.getRankService().getValidChannels(event.getGuild()); + channels.remove(channel.getIdLong()); + database.getRankService().updateValidChannels(event.getGuild(), channels); + StringBuilder result = new StringBuilder(); + channels.forEach(it -> result.append(String.format("<#%d>", it)).append("\n")); + event.reply(embedCache.getEmbed("validChannels").injectValue("channels", result)); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankConfig.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankConfig.java new file mode 100644 index 0000000..020668c --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankConfig.java @@ -0,0 +1,4 @@ +package com.github.kaktushose.nplaybot.rank.model; + +public record RankConfig(int cooldown, int minLength, double lootChance) { +} From 97ddbc8773e46143e62cad18e27a78a7f6c5c3a1 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:49:59 +0200 Subject: [PATCH 39/66] remove old files --- src/main/resources/jdac.properties | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 src/main/resources/jdac.properties diff --git a/src/main/resources/jdac.properties b/src/main/resources/jdac.properties deleted file mode 100644 index 90de9ec..0000000 --- a/src/main/resources/jdac.properties +++ /dev/null @@ -1,2 +0,0 @@ -prefix=! -helpLabels=hilfe, help From 86b34118c9b37fe5dc77a976d362d0f38e03f2ce Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:38:35 +0200 Subject: [PATCH 40/66] implement permissions system --- embeds.json | 10 +++ .../com/github/kaktushose/nplaybot/Bot.java | 2 + .../github/kaktushose/nplaybot/Database.java | 7 ++ .../events/contest/ContestCommands.java | 3 + .../internal/MaintenanceCommands.java | 3 + .../nplaybot/permissions/BotPermissions.java | 61 +++++++++++++ .../CustomPermissionsProvider.java | 27 ++++++ .../permissions/PermissionCommands.java | 86 +++++++++++++++++++ .../permissions/PermissionsService.java | 83 ++++++++++++++++++ .../rank/commands/ModifyXpCommands.java | 3 + .../rank/commands/RankConfigCommands.java | 5 +- .../rank/commands/RankInfoCommand.java | 3 + .../rank/leaderboard/LeaderboardCommand.java | 7 +- .../migration/V2.0.0__setup_event_system.sql | 1 + 14 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/permissions/CustomPermissionsProvider.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java diff --git a/embeds.json b/embeds.json index 6b8b5e3..e4d4915 100644 --- a/embeds.json +++ b/embeds.json @@ -135,5 +135,15 @@ "title": "Gewertete Textkanäle", "description": "{channels}", "color": "#67c94f" + }, + "userPermissions": { + "title": "Berechtigungen von {user}", + "description": "{permissions}", + "color": "#67c94f" + }, + "permissionsBulkEdit": { + "title": "Erfolg", + "description": "Die Berechtigungen wurden angepasst", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 9cc8730..dbacc56 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -4,6 +4,7 @@ import com.github.kaktushose.jda.commands.annotations.Produces; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.events.contest.ContestListener; +import com.github.kaktushose.nplaybot.permissions.CustomPermissionsProvider; import com.github.kaktushose.nplaybot.rank.JoinLeaveListener; import com.github.kaktushose.nplaybot.rank.RankListener; import com.github.kaktushose.nplaybot.scheduler.TaskScheduler; @@ -54,6 +55,7 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); jdaCommands.getDependencyInjector().registerProvider(this); + jdaCommands.getImplementationRegistry().setPermissionsProvider(new CustomPermissionsProvider(database)); taskScheduler = new TaskScheduler(this); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 617fdf4..422ea04 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot; import com.github.kaktushose.nplaybot.events.contest.ContestEventService; +import com.github.kaktushose.nplaybot.permissions.PermissionsService; import com.github.kaktushose.nplaybot.rank.RankService; import com.github.kaktushose.nplaybot.settings.SettingsService; import com.zaxxer.hikari.HikariConfig; @@ -12,6 +13,7 @@ public class Database { private final SettingsService settingsService; private final RankService rankService; private final ContestEventService contestEventService; + private final PermissionsService permissionsService; public Database() { var config = new HikariConfig(); @@ -26,6 +28,7 @@ public Database() { settingsService = new SettingsService(dataSource); rankService = new RankService(dataSource); contestEventService = new ContestEventService(dataSource); + permissionsService = new PermissionsService(dataSource); } public void closeDataSource() { @@ -43,4 +46,8 @@ public RankService getRankService() { public ContestEventService getContestEventService() { return contestEventService; } + + public PermissionsService getPermissionsService() { + return permissionsService; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java index bf0a53c..b9f17dd 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java @@ -3,10 +3,12 @@ import com.github.kaktushose.jda.commands.annotations.Inject; import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; import com.github.kaktushose.jda.commands.annotations.interactions.Param; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; @@ -14,6 +16,7 @@ import java.util.Optional; @Interaction +@Permissions(BotPermissions.MANAGE_EVENTS) public class ContestCommands { @Inject diff --git a/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java b/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java index 25fd2dd..f321c07 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java @@ -2,12 +2,15 @@ import com.github.kaktushose.jda.commands.annotations.Inject; import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; import net.dv8tion.jda.api.Permission; @Interaction +@Permissions(BotPermissions.BOT_ADMINISTRATOR) public class MaintenanceCommands { @Inject diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java new file mode 100644 index 0000000..825560f --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java @@ -0,0 +1,61 @@ +package com.github.kaktushose.nplaybot.permissions; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class BotPermissions { + + public static final String USER = "USER"; + public static final String MODIFY_USER_BALANCE = "MODIFY_USER_BALANCE"; + public static final String MODIFY_RANK_SETTINGS = "MODIFY_RANK_SETTINGS"; + public static final String MANAGE_EVENTS = "MANAGE_EVENTS"; + public static final String MODIFY_USER_PERMISSIONS = "MODIFY_USER_PERMISSIONS"; + public static final String BOT_ADMINISTRATOR = "BOT_ADMINISTRATOR"; + public static final String BOT_OWNER = "BOT_OWNER"; + + @SuppressWarnings("PointlessBitwiseExpression") + private static final Map permissionMapping = new LinkedHashMap<>() {{ + put(USER, 1 << 0); + put(MODIFY_USER_BALANCE, 1 << 1); + put(MODIFY_RANK_SETTINGS, 1 << 2); + put(MANAGE_EVENTS, 1 << 3); + put(MODIFY_USER_PERMISSIONS, 1 << 4); + put(BOT_ADMINISTRATOR, 1 << 5); + put(BOT_OWNER, 1 << 8); + }}; + + + public static int compute(Set permissions) { + int computedPermissions = 0; + for (String permission : permissions) { + computedPermissions = computedPermissions | permissionMapping.getOrDefault(permission, 0); + } + return computedPermissions; + } + + public static boolean hasPermissions(Set permissions, int userPermission) { + if (userPermission == permissionMapping.get(BOT_OWNER)) { + return true; + } + return (userPermission & compute(permissions)) != 0; + } + + public static int grant(int currentPermissions, String permission) { + return currentPermissions |= permissionMapping.getOrDefault(permission, 0); + } + + public static int revoke(int currentPermissions, String permission) { + return currentPermissions &= ~permissionMapping.getOrDefault(permission, 0); + } + + public static String listPermissions(int permissions) { + StringBuilder result = new StringBuilder(); + permissionMapping.forEach((name, value) -> { + if (hasPermissions(Set.of(name), permissions)) { + result.append(String.format("`%s`", name)).append("\n"); + } + }); + return result.toString(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/CustomPermissionsProvider.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/CustomPermissionsProvider.java new file mode 100644 index 0000000..8b87e13 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/CustomPermissionsProvider.java @@ -0,0 +1,27 @@ +package com.github.kaktushose.nplaybot.permissions; + +import com.github.kaktushose.jda.commands.dispatching.interactions.Context; +import com.github.kaktushose.jda.commands.permissions.PermissionsProvider; +import com.github.kaktushose.nplaybot.Database; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.User; +import org.jetbrains.annotations.NotNull; + +public class CustomPermissionsProvider implements PermissionsProvider { + + private final Database database; + + public CustomPermissionsProvider(Database database) { + this.database = database; + } + + @Override + public boolean hasPermission(@NotNull User user, @NotNull Context context) { + return database.getPermissionsService().hasPermissions(user, context.getInteractionDefinition().getPermissions()); + } + + @Override + public boolean hasPermission(@NotNull Member member, @NotNull Context context) { + return database.getPermissionsService().hasPermissions(member, context.getInteractionDefinition().getPermissions()); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java new file mode 100644 index 0000000..36402ba --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java @@ -0,0 +1,86 @@ +package com.github.kaktushose.nplaybot.permissions; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.*; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.Database; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; + +import static com.github.kaktushose.nplaybot.permissions.BotPermissions.*; + +@Interaction +public class PermissionCommands { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + + @SlashCommand(value = "permissions list", desc = "Zeigt die Berechtigungen eines Users an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @Permissions(USER) + public void onPermissionsList(CommandEvent event, @Optional Member member) { + var target = member == null ? event.getMember() : member; + + event.reply(embedCache.getEmbed("userPermissions") + .injectValue("user", target.getEffectiveName()) + .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getPermissions(target))) + ); + } + + @SlashCommand(value = "permissions grant", desc = "Fügt einem Nutzer eine Berechtigung hinzu", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @Permissions(MODIFY_USER_PERMISSIONS) + public void onPermissionsGrant(CommandEvent event, + Member member, + @Param("Die Berechtigung die hinzugefügt werden soll") + @Choices({USER, MODIFY_USER_BALANCE, MODIFY_RANK_SETTINGS, MANAGE_EVENTS, MODIFY_USER_PERMISSIONS, BOT_ADMINISTRATOR}) + String permission) { + database.getPermissionsService().grantPermissions(member, permission); + + event.reply(embedCache.getEmbed("userPermissions") + .injectValue("user", member.getEffectiveName()) + .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getPermissions(member))) + ); + } + + @SlashCommand(value = "permissions revoke", desc = "Entzieht einem Nutzer eine Berechtigung", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @Permissions(MODIFY_USER_PERMISSIONS) + public void onPermissionsRevoke(CommandEvent event, + Member member, + @Param("Die Berechtigung die entzogen werden soll") + @Choices({USER, MODIFY_USER_BALANCE, MODIFY_RANK_SETTINGS, MANAGE_EVENTS, MODIFY_USER_PERMISSIONS, BOT_ADMINISTRATOR}) + String permission) { + database.getPermissionsService().revokePermissions(member, permission); + + event.reply(embedCache.getEmbed("userPermissions") + .injectValue("user", member.getEffectiveName()) + .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getPermissions(member))) + ); + } + + @SlashCommand(value = "permissions bulk grant", desc = "Fügt allen Nutzern mit der angegebenen Rolle eine Berechtigung hinzu", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @Permissions(MODIFY_USER_PERMISSIONS) + public void onPermissionsBulkGrant(CommandEvent event, + Role role, + @Param("Die Berechtigung die hinzugefügt werden soll") + @Choices({USER, MODIFY_USER_BALANCE, MODIFY_RANK_SETTINGS, MANAGE_EVENTS, MODIFY_USER_PERMISSIONS, BOT_ADMINISTRATOR}) + String permission) { + event.getGuild().getMembersWithRoles(role).forEach(member -> database.getPermissionsService().grantPermissions(member, permission)); + + event.reply(embedCache.getEmbed("permissionsBulkEdit")); + } + + @SlashCommand(value = "permissions bulk revoke", desc = "Entzieht allen Nutzern mit der angegebenen Rolle eine Berechtigung", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @Permissions(MODIFY_USER_PERMISSIONS) + public void onPermissionsBulkRevoke(CommandEvent event, + Role role, + @Param("Die Berechtigung die entzogen werden soll") + @Choices({USER, MODIFY_USER_BALANCE, MODIFY_RANK_SETTINGS, MANAGE_EVENTS, MODIFY_USER_PERMISSIONS, BOT_ADMINISTRATOR}) + String permission) { + event.getGuild().getMembersWithRoles(role).forEach(member -> database.getPermissionsService().revokePermissions(member, permission)); + + event.reply(embedCache.getEmbed("permissionsBulkEdit")); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java new file mode 100644 index 0000000..1c855ec --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java @@ -0,0 +1,83 @@ +package com.github.kaktushose.nplaybot.permissions; + +import net.dv8tion.jda.api.entities.UserSnowflake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Set; + +public class PermissionsService { + + private static final Logger log = LoggerFactory.getLogger(PermissionsService.class); + private final DataSource dataSource; + + public PermissionsService(DataSource dataSource) { + this.dataSource = dataSource; + } + + public int getPermissions(UserSnowflake user) { + log.debug("Querying permissions for user {}", user); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT permissions + FROM users + WHERE user_id = ? + """ + ); + statement.setLong(1, user.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void grantPermissions(UserSnowflake user, String permission) { + log.warn("Granting permission {} for user {}", permission, user); + try (Connection connection = dataSource.getConnection()) { + PreparedStatement statement = connection.prepareStatement(""" + UPDATE users + SET permissions = ? + WHERE user_id = ? + """ + ); + + statement.setLong(1, BotPermissions.grant(getPermissions(user), permission)); + statement.setLong(2, user.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void revokePermissions(UserSnowflake user, String permission) { + log.warn("Revoking permission {} for user {}", permission, user); + try (Connection connection = dataSource.getConnection()) { + PreparedStatement statement = connection.prepareStatement(""" + UPDATE users + SET permissions = ? + WHERE user_id = ? + """ + ); + + statement.setLong(1, BotPermissions.revoke(getPermissions(user), permission)); + statement.setLong(2, user.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public boolean hasPermissions(UserSnowflake user, Set permissions) { + log.debug("Checking permissions for user {}", user); + return BotPermissions.hasPermissions(permissions, getPermissions(user)); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java index 70474cc..43fc817 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java @@ -4,10 +4,12 @@ import com.github.kaktushose.jda.commands.annotations.constraints.Max; import com.github.kaktushose.jda.commands.annotations.constraints.Min; import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; @@ -17,6 +19,7 @@ import org.slf4j.LoggerFactory; @Interaction(ephemeral = true) +@Permissions(BotPermissions.MODIFY_USER_BALANCE) public class ModifyXpCommands { private static final Logger log = LoggerFactory.getLogger(ModifyXpCommands.class); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java index b59933d..0ca05ca 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java @@ -5,14 +5,17 @@ import com.github.kaktushose.jda.commands.annotations.constraints.Min; import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; import com.github.kaktushose.jda.commands.annotations.interactions.Param; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; @Interaction +@Permissions(BotPermissions.MODIFY_RANK_SETTINGS) public class RankConfigCommands { @Inject @@ -20,7 +23,7 @@ public class RankConfigCommands { @Inject private EmbedCache embedCache; - @SlashCommand(value = "rank config", desc = "Zeigt die Einstellungen für das Rank System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "get rank config", desc = "Zeigt die Einstellungen für das Rank System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onGetRankConfig(CommandEvent event) { event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java index 53c6b78..449e0dd 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -3,14 +3,17 @@ import com.github.kaktushose.jda.commands.annotations.Inject; import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; import com.github.kaktushose.jda.commands.annotations.interactions.Optional; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; import com.github.kaktushose.nplaybot.rank.model.UserInfo; import net.dv8tion.jda.api.entities.Member; @Interaction +@Permissions(BotPermissions.USER) public class RankInfoCommand { @Inject diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java index 25f56d5..b320d37 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java @@ -3,6 +3,7 @@ import com.github.kaktushose.jda.commands.annotations.Inject; import com.github.kaktushose.jda.commands.annotations.interactions.Button; import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; @@ -11,6 +12,7 @@ import com.github.kaktushose.jda.commands.dispatching.reply.components.Buttons; import com.github.kaktushose.jda.commands.dispatching.reply.components.Component; import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; import net.dv8tion.jda.api.entities.Guild; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,16 +21,15 @@ @Interaction +@Permissions(BotPermissions.USER) public class LeaderboardCommand { private static final Logger log = LoggerFactory.getLogger(LeaderboardCommand.class); - + private final int minIndex = 1; @Inject private Database database; @Inject private EmbedCache embedCache; - - private final int minIndex = 1; private int index = 1; private int maxIndex; private List leaderboard; diff --git a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql index fdae2f8..8bf9287 100644 --- a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql +++ b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql @@ -8,3 +8,4 @@ CREATE TABLE contest_entries ( votes INT NOT NULL DEFAULT 0, message_id BIGINT NOT NULL ); +ALTER TABLE users ADD COLUMN permissions INTEGER DEFAULT 1; From a9b167d19a0192786e7cfd4094a7870e993e1761 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:49:07 +0200 Subject: [PATCH 41/66] add context command for rank info --- .../nplaybot/rank/commands/RankInfoCommand.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java index 449e0dd..33abb34 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -1,16 +1,15 @@ package com.github.kaktushose.nplaybot.rank.commands; import com.github.kaktushose.jda.commands.annotations.Inject; -import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; -import com.github.kaktushose.jda.commands.annotations.interactions.Optional; -import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; -import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.annotations.interactions.*; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; import com.github.kaktushose.nplaybot.Database; import com.github.kaktushose.nplaybot.permissions.BotPermissions; import com.github.kaktushose.nplaybot.rank.model.UserInfo; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.interactions.commands.Command; @Interaction @Permissions(BotPermissions.USER) @@ -22,11 +21,19 @@ public class RankInfoCommand { private EmbedCache embedCache; @SlashCommand(value = "rank", isGuildOnly = true, desc = "Zeigt die Kontoinformationen zu einem User an") - public void onCommand(CommandEvent event, @Optional Member member) { + public void onRankInfo(CommandEvent event, @Optional Member member) { var target = member == null ? event.getMember() : member; UserInfo userInfo = database.getRankService().getUserInfo(target); var embed = userInfo.nextRank().isPresent() ? "rankInfo" : "rankInfoMax"; event.reply(embedCache.getEmbed(embed).injectValues(userInfo.getEmbedValues(target))); } + + @ContextCommand(value = "Kontoinformation abrufen", type = Command.Type.USER, isGuildOnly = true, ephemeral = true) + public void onContextRankInfo(CommandEvent event, User user) { + UserInfo userInfo = database.getRankService().getUserInfo(user); + + var embed = userInfo.nextRank().isPresent() ? "rankInfo" : "rankInfoMax"; + event.reply(embedCache.getEmbed(embed).injectValues(userInfo.getEmbedValues(user))); + } } From dcb26d06394c4dbfee21c91375eb58fadce83cd6 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sun, 5 May 2024 15:20:32 +0200 Subject: [PATCH 42/66] first pass on collect events --- embeds.json | 74 ++++++++ pom.xml | 2 +- .../github/kaktushose/nplaybot/Database.java | 8 + .../events/collect/CollectEventCommands.java | 42 +++++ .../events/collect/CollectEventService.java | 133 ++++++++++++++ .../events/collect/CollectRewardCommands.java | 169 ++++++++++++++++++ .../migration/V2.0.0__setup_event_system.sql | 22 ++- 7 files changed, 447 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java diff --git a/embeds.json b/embeds.json index e4d4915..c9e7cd8 100644 --- a/embeds.json +++ b/embeds.json @@ -145,5 +145,79 @@ "title": "Erfolg", "description": "Die Berechtigungen wurden angepasst", "color": "#67c94f" + }, + "rewardCreateSelectType": { + "title": "Belohnung: {name}", + "description": "Bitte wähle die Belohnungsart aus, mit der der Nutzer belohnt werden soll", + "color": "#67c94f" + }, + "rewardCreateSelectRole": { + "title": "Belohnung: {name}", + "description": "Bitte wähle die Rolle aus, mit der der Nutzer belohnt werden soll", + "color": "#67c94f" + }, + "rewardCreateInvalidXp": { + "title": "Belohnung: {name}", + "description": "Bitte gib eine Zahl zwischen 1 und 2.147.483.647 an", + "color": "#ffc926" + }, + "rewardCreateInvalidEmbed": { + "title": "Belohnung: {name}", + "description": "Bitte gib ein gültiges Embed im JSON-Format an. [Online Embed Builder](https://glitchii.github.io/embedbuilder/)", + "color": "#ffc926" + }, + "rewardCreateSummarize": { + "title": "Belohnung: {name}", + "description": "Du hast folgende Belohnung konfiguriert", + "fields": [ + { + "name": "Typ", + "value": "{type}" + }, + { + "name": "Grenze", + "value": "{threshold}" + }, + { + "name": "{type}", + "value": "{reward}" + } + ], + "color": "#ffc926" + }, + "rewardCreateConfirm": { + "title": "Erfolg", + "description": "Die Belohnung {name} wurde erstellt", + "color": "#67c94f" + }, + "rewardCreateCancel": { + "title": "Okay", + "description": "Die Erstellung einer Belohnung wurde abgebrochen", + "color": "#67c94f" + }, + "collectEventStart": { + "title": "Erfolg", + "description": "Das Collect Event `{name}` wurde gestartet", + "color": "#67c94f" + }, + "collectEventStartError": { + "title": "Fehler", + "description": "Es ist aktuell bereits ein Collect Event aktiv. Beende dies zuerst, bevor du ein neues starten kannst!", + "color": "#ff0000" + }, + "collectEventStop": { + "title": "Erfolg", + "description": "Das aktuelle Collect Event wurde beendet", + "color": "#67c94f" + }, + "rewardDeleteSelect": { + "title": "Belohnungen löschen", + "description": "Wähle eine oder mehrere Belohnungen aus, die du löschen möchtest", + "color": "#67c94f" + }, + "rewardDelete": { + "title": "Erfolg", + "description": "Die Belohnung(en) wurden gelöscht", + "color": "#67c94f" } } diff --git a/pom.xml b/pom.xml index 83f3499..5e23208 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ com.github.Kaktushose jda-commands - 4.0.0-beta.2 + da36e41af0 com.zaxxer diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 422ea04..b029e03 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -1,11 +1,13 @@ package com.github.kaktushose.nplaybot; +import com.github.kaktushose.nplaybot.events.collect.CollectEventService; import com.github.kaktushose.nplaybot.events.contest.ContestEventService; import com.github.kaktushose.nplaybot.permissions.PermissionsService; import com.github.kaktushose.nplaybot.rank.RankService; import com.github.kaktushose.nplaybot.settings.SettingsService; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import net.dv8tion.jda.api.entities.Role; public class Database { @@ -13,6 +15,7 @@ public class Database { private final SettingsService settingsService; private final RankService rankService; private final ContestEventService contestEventService; + private final CollectEventService collectEventService; private final PermissionsService permissionsService; public Database() { @@ -28,6 +31,7 @@ public Database() { settingsService = new SettingsService(dataSource); rankService = new RankService(dataSource); contestEventService = new ContestEventService(dataSource); + collectEventService = new CollectEventService(dataSource); permissionsService = new PermissionsService(dataSource); } @@ -47,6 +51,10 @@ public ContestEventService getContestEventService() { return contestEventService; } + public CollectEventService getCollectEventService() { + return collectEventService; + } + public PermissionsService getPermissionsService() { return permissionsService; } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java new file mode 100644 index 0000000..0ce3307 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java @@ -0,0 +1,42 @@ +package com.github.kaktushose.nplaybot.events.collect; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Param; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; +import net.dv8tion.jda.api.Permission; + +@Interaction +@Permissions(BotPermissions.MANAGE_EVENTS) +public class CollectEventCommands { + + @Inject + private EmbedCache embedCache; + @Inject + private Database database; + + @SlashCommand(value = "collect event start", desc = "Startet ein Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onCollectEventStart(CommandEvent event, + @Param("Der Name des Events") String eventName, + @Param("Der Name der Währung die gesammelt werden soll, z.B. \"Schneemänner\"") String currencyName, + @Param("Die Emoji-Repräsentation der Währung, die gesammelt werden soll") String emoji) { + if (database.getCollectEventService().isActive(event.getGuild())) { + event.reply(embedCache.getEmbed("collectEventStartError")); + return; + } + database.getCollectEventService().startCollectEvent(event.getGuild(), eventName, currencyName, emoji); + event.reply(embedCache.getEmbed("collectEventStart").injectValue("name", eventName)); + } + + @SlashCommand(value = "collect event stop", desc = "Stoppt das aktuelle Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onCollectEventStop(CommandEvent event) { + database.getCollectEventService().stopCollectEvent(event.getGuild()); + event.reply(embedCache.getEmbed("collectEventStop")); + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java new file mode 100644 index 0000000..ec95f54 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java @@ -0,0 +1,133 @@ +package com.github.kaktushose.nplaybot.events.collect; + +import com.github.kaktushose.nplaybot.events.contest.ContestEventService; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Role; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class CollectEventService { + + + private static final Logger log = LoggerFactory.getLogger(ContestEventService.class); + private final DataSource dataSource; + public CollectEventService(DataSource dataSource) { + this.dataSource = dataSource; + } + + public boolean isActive(Guild guild) { + log.debug("Querying collect event active flag for guild {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT collect_event_active + FROM event_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getBoolean(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void startCollectEvent(Guild guild, String eventName, String currencyName, String emoji) { + log.debug("Starting new collect event [{}, {}, {}] for guild {}", eventName, currencyName, emoji, guild); + try (Connection connection = dataSource.getConnection()) { + connection.prepareStatement("UPDATE users SET collect_points = 0").execute(); + + var statement = connection.prepareStatement(""" + UPDATE event_settings + SET collect_event_name = ?, + collect_currency_name = ?, + collect_currency_emoji = ?, + collect_event_active = true + WHERE guild_id = ? + """ + ); + + statement.setString(1, eventName); + statement.setString(2,currencyName); + statement.setString(3, emoji); + statement.setLong(4, guild.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void stopCollectEvent(Guild guild) { + log.debug("Stopping collect event for guild {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE event_settings + SET collect_event_active = false + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void createCollectReward(String name, int threshold, int xp, Role role, String embed) { + log.debug("Creating new collect reward"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("INSERT INTO collect_rewards(name, threshold, xp, role_id, embed) VALUES (?, ?, ?, ?, CAST (? AS jsonb))"); + statement.setString(1, name); + statement.setInt(2, threshold); + statement.setInt(3, xp); + statement.setLong(4, role == null ? -1 : role.getIdLong()); + statement.setObject(5, embed); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public record CollectReward(int rewardId, String name) { + } + public List getCollectRewards() { + log.debug("Querying collect rewards"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT reward_id, name + FROM collect_rewards + """ + ); + var result = statement.executeQuery(); + List rewards = new ArrayList<>(); + while (result.next()) { + rewards.add(new CollectReward(result.getInt("reward_id"), result.getString("name"))); + } + return rewards; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void deleteCollectReward(int rewardId) { + log.debug("Deleting reward with id {}", rewardId); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("DELETE FROM collect_rewards WHERE reward_id = ?"); + statement.setInt(1, rewardId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java new file mode 100644 index 0000000..f37d462 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java @@ -0,0 +1,169 @@ +package com.github.kaktushose.nplaybot.events.collect; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.*; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.jda.commands.dispatching.interactions.components.ComponentEvent; +import com.github.kaktushose.jda.commands.dispatching.interactions.modals.ModalEvent; +import com.github.kaktushose.jda.commands.dispatching.reply.Replyable; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; +import com.google.gson.*; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Mentions; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; +import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; + +import java.util.List; +import java.util.Optional; + +@Interaction +@Permissions(BotPermissions.MANAGE_EVENTS) +public class CollectRewardCommands { + + @Inject + private EmbedCache embedCache; + @Inject + private Database database; + private static final Gson gson = new Gson(); + private static final String ROLE_REWARD = "Rolle"; + private static final String XP_REWARD = "XP"; + private String name; + private String rewardType; + private Role role; + private int xp; + private int threshold; + private String embed; + + @SlashCommand(value = "collect reward create", desc = "Erstellt eine Belohnung für das Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onRewardCreate(CommandEvent event, @Param("Der interne Name dieser Belohnung") String name, @Param("Der Wert, ab wann die Belohnung vergeben werden soll") int threshold) { + this.name = name; + this.threshold = threshold; + event.withSelectMenus("onSelectType").reply(embedCache.getEmbed("rewardCreateSelectType").injectValue("name", name)); + } + + @StringSelectMenu("Wähle eine Belohnungsart aus") + @SelectOption(label = "Rolle", value = ROLE_REWARD) + @SelectOption(label = "XP", value = XP_REWARD) + public void onSelectType(ComponentEvent event, List selection) { + rewardType = selection.get(0); + if (ROLE_REWARD.equals(rewardType)) { + event.withSelectMenus("onSelectRole").reply(embedCache.getEmbed("rewardCreateSelectRole").injectValue("name", name)); + } else if (XP_REWARD.equals(rewardType)) { + event.replyModal("onSelectXp"); + } else { + throw new IllegalArgumentException(String.format("%s ist keine gültige Auswahl!", rewardType)); + } + } + + @EntitySelectMenu(value = net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu.SelectTarget.ROLE, placeholder = "Wähle eine Rolle aus") + public void onSelectRole(ComponentEvent event, Mentions mentions) { + role = mentions.getRoles().get(0); + event.replyModal("onSelectEmbed"); + } + + @Modal("Embed angeben") + public void onSelectEmbed(ModalEvent event, @TextInput(value = "Das Embed im JSON-Format", label = "Embed") String embed) { + parseJson(embed).ifPresentOrElse(it -> { + this.embed = it; + finishSetup(event); + }, () -> event.withButtons("onRetryEmbedInput").reply(embedCache.getEmbed("rewardCreateInvalidEmbed").injectValue("name", name))); + } + + @Button("neue Eingabe") + public void onRetryEmbedInput(ComponentEvent event) { + event.replyModal("onSelectEmbed"); + } + + @Modal("XP-Belohnung") + public void onSelectXp(ModalEvent event, + @TextInput(style = TextInputStyle.SHORT, label = "XP-Menge", value = "Eine Zahl zwischen 1 und 2.147.483.647") String amount, + @TextInput(value = "Das Embed im JSON-Format", label = "Embed") String embed) { + int xp; + try { + xp = Integer.parseInt(amount); + } catch (NumberFormatException ignored) { + event.withButtons("onRetryXpInput").reply(embedCache.getEmbed("rewardCreateInvalidXp").injectValue("name", name)); + return; + } + if (xp < 1) { + event.withButtons("onRetryXpInput").reply(embedCache.getEmbed("rewardCreateInvalidXp").injectValue("name", name)); + return; + } + parseJson(embed).ifPresentOrElse(it -> { + this.embed = it; + this.xp = xp; + finishSetup(event); + }, () -> event.withButtons("onRetryXpInput").reply(embedCache.getEmbed("rewardCreateInvalidEmbed").injectValue("name", name))); + } + + @Button("neue Eingabe") + public void onRetryXpInput(ComponentEvent event) { + event.replyModal("onSelectXp"); + } + + private void finishSetup(Replyable event) { + event.withButtons("onConfirm", "onCancel").reply(embedCache.getEmbed("rewardCreateSummarize") + .injectValue("name", name) + .injectValue("type", rewardType) + .injectValue("threshold", threshold) + .injectValue("reward", role == null ? String.valueOf(xp) : role.getAsMention()) + ); + } + + @Button(value = "Erstellen", style = ButtonStyle.SUCCESS) + public void onConfirm(ComponentEvent event) { + database.getCollectEventService().createCollectReward(name, threshold, xp, role, embed); + event.reply(embedCache.getEmbed("rewardCreateConfirm").injectValue("name", name)); + event.removeComponents(); + } + + @Button(value = "Abbrechen", style = ButtonStyle.DANGER) + public void onCancel(ComponentEvent event) { + event.reply(embedCache.getEmbed("rewardCreateCancel")); + event.removeComponents(); + } + + @SlashCommand(value = "collect reward delete", desc = "Löscht eine oder mehrere Belohnung(en) für das Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onRewardDelete(CommandEvent event) { + var rewards = database.getCollectEventService().getCollectRewards(); + + var menu = ((net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu) + event.getJdaCommands().getSelectMenu("CollectRewardCommands.onRewardDeleteSelect")).createCopy(); + menu.getOptions().clear(); + menu.setMaxValues(SelectMenu.OPTIONS_MAX_AMOUNT); + rewards.forEach(it -> menu.addOption(it.name(), String.valueOf(it.rewardId()))); + event.getReplyContext().getBuilder().addActionRow(menu.build()); + event.reply(embedCache.getEmbed("rewardDeleteSelect")); + } + + @StringSelectMenu(value = "Wähle eine oder mehrere Belohnungen aus") + @SelectOption(label = "dummy option", value = "dummy option") + public void onRewardDeleteSelect(ComponentEvent event, List selection) { + for (var id : selection) { + database.getCollectEventService().deleteCollectReward(Integer.parseInt(id)); + } + event.reply(embedCache.getEmbed("rewardDelete")); + } + + private Optional parseJson(String json) { + try { + JsonObject object = gson.fromJson(json, JsonObject.class); + if (object.has("embeds")) { + if (!object.get("embeds").isJsonArray()) { + return Optional.empty(); + } + object = object.get("embeds").getAsJsonArray().get(0).getAsJsonObject(); + } + if (!(object.has("title") || object.has("description"))) { + return Optional.of(object.toString()); + } + return Optional.empty(); + } catch (JsonSyntaxException ignored) { + return Optional.empty(); + } + } +} diff --git a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql index 8bf9287..da377f4 100644 --- a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql +++ b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql @@ -1,11 +1,29 @@ CREATE TABLE event_settings ( guild_id BIGINT NOT NULL PRIMARY KEY, - contest_channel_id BIGINT NOT NULL DEFAULT 0, - contest_vote_emoji VARCHAR + contest_channel_id BIGINT NOT NULL DEFAULT -1, + contest_vote_emoji VARCHAR NOT NULL DEFAULT '', + collect_event_name VARCHAR NOT NULL DEFAULT '', + collect_currency_name VARCHAR NOT NULL DEFAULT '', + collect_currency_emoji VARCHAR NOT NULL DEFAULT '', + collect_loot_chance REAL NOT NULL, + collect_event_active BOOLEAN NOT NULL DEFAULT FALSE ); + CREATE TABLE contest_entries ( user_id BIGINT NOT NULL PRIMARY KEY, votes INT NOT NULL DEFAULT 0, message_id BIGINT NOT NULL ); + ALTER TABLE users ADD COLUMN permissions INTEGER DEFAULT 1; + +ALTER TABLE users ADD COLUMN collect_points INTEGER DEFAULT 0; + +CREATE TABLE collect_rewards ( + reward_id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL DEFAULT 0, + threshold INT NOT NULL, + xp INT, + role_id BIGINT, + embed JSONB NOT NULL +); From c0466ec2271492c7119620aa2cf4a20faa5bcb2c Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sun, 5 May 2024 15:28:33 +0200 Subject: [PATCH 43/66] add config command for collect loot chance --- embeds.json | 4 ++++ .../events/collect/CollectEventCommands.java | 8 ++++++++ .../events/collect/CollectEventService.java | 18 ++++++++++++++++++ .../rank/commands/RankConfigCommands.java | 2 +- 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/embeds.json b/embeds.json index c9e7cd8..d39885a 100644 --- a/embeds.json +++ b/embeds.json @@ -219,5 +219,9 @@ "title": "Erfolg", "description": "Die Belohnung(en) wurden gelöscht", "color": "#67c94f" + }, + "collectLootChanceUpdate": { + "title": "Erfolg", + "description": "Die Wahrscheinlichkeit für zufällige Collect-Loo-Drops wurde auf {value} gesetzt" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java index 0ce3307..f703640 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java @@ -1,6 +1,8 @@ package com.github.kaktushose.nplaybot.events.collect; import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.constraints.Max; +import com.github.kaktushose.jda.commands.annotations.constraints.Min; import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; import com.github.kaktushose.jda.commands.annotations.interactions.Param; import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; @@ -39,4 +41,10 @@ public void onCollectEventStop(CommandEvent event) { event.reply(embedCache.getEmbed("collectEventStop")); } + @SlashCommand(value = "set collect-loot chance", desc = "Legt die Wahrscheinlichkeit für zufällige Collect-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onSetXpLootDropChance(CommandEvent event, @Param("Die Wahrscheinlichkeit in Prozent") @Min(1) @Max(100) double chance) { + database.getCollectEventService().updateCollectLootChance(event.getGuild(), chance); + event.reply(embedCache.getEmbed("collectLootChanceUpdate").injectFields("value", chance)); + } + } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java index ec95f54..ee3e33c 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java @@ -98,6 +98,24 @@ public void createCollectReward(String name, int threshold, int xp, Role role, S } } + public void updateCollectLootChance(Guild guild, double chance) { + log.debug("Updating collect loot chance for guild: {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE rank_settings + SET collect_loot_chance = ? + WHERE guild_id = ? + """ + ); + statement.setDouble(1, chance); + statement.setLong(2, guild.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public record CollectReward(int rewardId, String name) { } public List getCollectRewards() { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java index 0ca05ca..7bbc1c7 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java @@ -40,7 +40,7 @@ public void onSetMinMessageLength(CommandEvent event, @Param("Die Mindestanzahl event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); } - @SlashCommand(value = "set loot chance", desc = "Legt die Wahrscheinlichkeit für zufällige XP-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "set xp-loot chance", desc = "Legt die Wahrscheinlichkeit für zufällige XP-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetXpLootDropChance(CommandEvent event, @Param("Die Wahrscheinlichkeit in Prozent") @Min(1) @Max(100) double chance) { database.getRankService().updateXpLootChance(event.getGuild(), chance); event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); From 4d241e5f5b547417da5ef12de412ae2fcfd3b2c8 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Mon, 6 May 2024 20:34:25 +0200 Subject: [PATCH 44/66] implement collect event functionality --- embeds.json | 9 +- .../com/github/kaktushose/nplaybot/Bot.java | 4 +- .../events/collect/CollectEventCommands.java | 2 +- .../events/collect/CollectEventListener.java | 142 ++++++++++++++++++ .../events/collect/CollectEventService.java | 88 +++++++++-- .../events/collect/CollectRewardCommands.java | 2 +- .../events/contest/ContestListener.java | 6 + .../nplaybot/rank/RankListener.java | 29 +--- .../kaktushose/nplaybot/rank/RankService.java | 19 +++ .../rank/commands/RankInfoCommand.java | 22 ++- .../nplaybot/rank/model/UserInfo.java | 18 --- .../migration/V2.0.0__setup_event_system.sql | 21 ++- 12 files changed, 300 insertions(+), 62 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java diff --git a/embeds.json b/embeds.json index d39885a..18aaa4f 100644 --- a/embeds.json +++ b/embeds.json @@ -187,7 +187,7 @@ }, "rewardCreateConfirm": { "title": "Erfolg", - "description": "Die Belohnung {name} wurde erstellt", + "description": "Die Belohnung `{name}` wurde erstellt", "color": "#67c94f" }, "rewardCreateCancel": { @@ -222,6 +222,11 @@ }, "collectLootChanceUpdate": { "title": "Erfolg", - "description": "Die Wahrscheinlichkeit für zufällige Collect-Loo-Drops wurde auf {value} gesetzt" + "description": "Die Wahrscheinlichkeit für zufällige Collect-Loo-Drops wurde auf {chance} gesetzt" + }, + "collectLootDropClaimed": { + "title": "Glückwunsch :tada:", + "description": "{user} hat 1 {name} gefunden", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index dbacc56..54d175b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -3,6 +3,7 @@ import com.github.kaktushose.jda.commands.JDACommands; import com.github.kaktushose.jda.commands.annotations.Produces; import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.events.collect.CollectEventListener; import com.github.kaktushose.nplaybot.events.contest.ContestListener; import com.github.kaktushose.nplaybot.permissions.CustomPermissionsProvider; import com.github.kaktushose.nplaybot.rank.JoinLeaveListener; @@ -47,7 +48,8 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { .addEventListeners( new RankListener(database, embedCache), new JoinLeaveListener(database.getRankService()), - new ContestListener(database.getContestEventService()) + new ContestListener(database.getContestEventService()), + new CollectEventListener(database, embedCache) ) .build().awaitReady(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java index f703640..9c0e32d 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java @@ -44,7 +44,7 @@ public void onCollectEventStop(CommandEvent event) { @SlashCommand(value = "set collect-loot chance", desc = "Legt die Wahrscheinlichkeit für zufällige Collect-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetXpLootDropChance(CommandEvent event, @Param("Die Wahrscheinlichkeit in Prozent") @Min(1) @Max(100) double chance) { database.getCollectEventService().updateCollectLootChance(event.getGuild(), chance); - event.reply(embedCache.getEmbed("collectLootChanceUpdate").injectFields("value", chance)); + event.reply(embedCache.getEmbed("collectLootChanceUpdate").injectValue("chance", chance)); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java new file mode 100644 index 0000000..6364f58 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java @@ -0,0 +1,142 @@ +package com.github.kaktushose.nplaybot.events.collect; + +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.rank.RankService; +import com.github.kaktushose.nplaybot.settings.SettingsService; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +public class CollectEventListener extends ListenerAdapter { + + private static final Logger log = LoggerFactory.getLogger(CollectEventListener.class); + private final RankService rankService; + private final CollectEventService eventService; + private final SettingsService settingsService; + private final EmbedCache embedCache; + private final Set collectLootDrops; + + public CollectEventListener(Database database, EmbedCache embedCache) { + this.rankService = database.getRankService(); + this.eventService = database.getCollectEventService(); + this.settingsService = database.getSettingsService(); + this.embedCache = embedCache; + collectLootDrops = new HashSet<>(); + } + + @Override + public void onMessageReceived(@NotNull MessageReceivedEvent event) { + log.debug("Received message event"); + var author = event.getAuthor(); + + if (author.isBot()) { + log.trace("Author is bot"); + return; + } + if (!event.isFromGuild()) { + log.trace("Event is not from guild"); + return; + } + + if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { + return; + } + + onCollectLootDrop(event); + onAddRegularCollectPoints(event); + } + + private void onAddRegularCollectPoints(MessageReceivedEvent event) { + var oldPoints = eventService.getCollectPoints(event.getAuthor()); + var newPoints = eventService.addCollectPoint(event.getAuthor()); + onCollectPointChange(oldPoints, newPoints, event.getMember(), event.getGuild()); + } + + private void onCollectPointChange(int oldPoints, int newPoints, Member member, Guild guild) { + var rewards = eventService.getCollectRewards(); + var optional = rewards.stream() + .filter(it -> it.threshold() > oldPoints) + .filter(it -> it.threshold() <= newPoints) + .findFirst(); + + if (optional.isEmpty()) { + return; + } + var reward = optional.get(); + + if (reward.xp() > 0) { + var xpChangeResult = rankService.addXp(member, reward.xp()); + rankService.onXpChange(xpChangeResult, member, guild, embedCache).ifPresent(it -> + settingsService.getBotChannel(guild).sendMessage(it).queue() + ); + } + + if (reward.roleId() > 0) { + guild.addRoleToMember(member, guild.getRoleById(reward.roleId())).queue(); + } + + var builder = new MessageCreateBuilder().addContent(member.getAsMention()) + .addEmbeds(EmbedBuilder.fromData(DataObject.fromJson(reward.embed())).build()) + .build(); + settingsService.getBotChannel(guild).sendMessage(builder).queue(); + } + + private void onCollectLootDrop(MessageReceivedEvent event) { + var points = eventService.getCollectLootDrop(event.getMessage()); + + if (points < 1) { + log.debug("No collect loot drop for this message"); + return; + } + + collectLootDrops.add(event.getMessageIdLong()); + event.getMessage().addReaction(Emoji.fromUnicode(eventService.getCollectCurrency(event.getGuild()).emoji())).queue(); + } + + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + if (event.getUser().isBot()) { + return; + } + + var messageId = event.getMessageIdLong(); + if (!collectLootDrops.contains(messageId)) { + return; + } + var currency = eventService.getCollectCurrency(event.getGuild()); + if (!event.getEmoji().equals(Emoji.fromUnicode(currency.emoji()))) { + return; + } + + log.debug("Collect loot drop got claimed by {}", event.getMember()); + + var oldPoints = eventService.getCollectPoints(event.getMember()); + var newPoints = eventService.addCollectPoint(event.getMember()); + onCollectPointChange(oldPoints, newPoints, event.getMember(), event.getGuild()); + collectLootDrops.remove(messageId); + + event.retrieveMessage().queue(message -> { + message.reply( + embedCache.getEmbed("collectLootDropClaimed") + .injectValue("user", event.getMember().getAsMention()) + .injectValue("name", currency.name()) + .toMessageCreateData() + ).mentionRepliedUser(false).queue(it -> it.delete().queueAfter(10, TimeUnit.SECONDS)); + message.clearReactions().queue(); + }); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java index ee3e33c..bdda76a 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java @@ -2,7 +2,9 @@ import com.github.kaktushose.nplaybot.events.contest.ContestEventService; import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.UserSnowflake; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,6 +19,7 @@ public class CollectEventService { private static final Logger log = LoggerFactory.getLogger(ContestEventService.class); private final DataSource dataSource; + public CollectEventService(DataSource dataSource) { this.dataSource = dataSource; } @@ -56,7 +59,7 @@ public void startCollectEvent(Guild guild, String eventName, String currencyName ); statement.setString(1, eventName); - statement.setString(2,currencyName); + statement.setString(2, currencyName); statement.setString(3, emoji); statement.setLong(4, guild.getIdLong()); @@ -102,7 +105,7 @@ public void updateCollectLootChance(Guild guild, double chance) { log.debug("Updating collect loot chance for guild: {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - UPDATE rank_settings + UPDATE event_settings SET collect_loot_chance = ? WHERE guild_id = ? """ @@ -116,20 +119,63 @@ public void updateCollectLootChance(Guild guild, double chance) { } } - public record CollectReward(int rewardId, String name) { + public int getCollectLootDrop(Message message) { + log.debug("Querying collect loot drop for message {}", message); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM get_collect_loot_drop(?)"); + statement.setLong(1, message.getGuildIdLong()); + var result = statement.executeQuery(); + result.next(); + var points = result.getInt(1); + log.debug("Collect loot drop: {} points", points); + return points; + } catch (SQLException e) { + throw new RuntimeException(e); + } } - public List getCollectRewards() { - log.debug("Querying collect rewards"); + + public record CollectCurrency(String name, String emoji) { + } + + public CollectCurrency getCollectCurrency(Guild guild) { + log.debug("Querying collect currency emoji"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT reward_id, name - FROM collect_rewards - """ + SELECT collect_currency_emoji, collect_currency_name + FROM event_settings + WHERE guild_id = ? + """); + statement.setLong(1, guild.getIdLong()); + var result = statement.executeQuery(); + result.next(); + return new CollectCurrency( + result.getString("collect_currency_name"), + result.getString("collect_currency_emoji") ); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public record CollectReward(int rewardId, String name, int threshold, int xp, long roleId, String embed) { + } + + public List getCollectRewards() { + log.debug("Querying collect rewards"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM collect_rewards"); var result = statement.executeQuery(); List rewards = new ArrayList<>(); while (result.next()) { - rewards.add(new CollectReward(result.getInt("reward_id"), result.getString("name"))); + rewards.add(new CollectReward( + result.getInt("reward_id"), + result.getString("name"), + result.getInt("threshold"), + result.getInt("xp"), + result.getLong("role_id"), + result.getString("embed") + ) + ); } return rewards; } catch (SQLException e) { @@ -148,4 +194,28 @@ public void deleteCollectReward(int rewardId) { } } + public int addCollectPoint(UserSnowflake user) { + log.debug("Adding one collect point to user: {}", user); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("UPDATE users SET collect_points = collect_points + 1 where user_id = ?"); + statement.setLong(1, user.getIdLong()); + statement.execute(); + return getCollectPoints(user); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getCollectPoints(UserSnowflake user) { + log.debug("Querying collect points for user {}", user); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT collect_points FROM users WHERE user_Id = ?"); + statement.setLong(1, user.getIdLong()); + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java index f37d462..3da17e8 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java @@ -158,7 +158,7 @@ private Optional parseJson(String json) { } object = object.get("embeds").getAsJsonArray().get(0).getAsJsonObject(); } - if (!(object.has("title") || object.has("description"))) { + if (object.has("title") || object.has("description")) { return Optional.of(object.toString()); } return Optional.empty(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java index 6713bc5..4302b73 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java @@ -84,6 +84,9 @@ public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { @Override public void onMessageReactionRemoveEmoji(@NotNull MessageReactionRemoveEmojiEvent event) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + return; + } if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild())))) { return; } @@ -96,6 +99,9 @@ public void onMessageReactionRemoveEmoji(@NotNull MessageReactionRemoveEmojiEven @Override public void onMessageReactionRemoveAll(@NotNull MessageReactionRemoveAllEvent event) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + return; + } log.debug("Detected removal of all vote emojis. Adding initial emoji again"); event.getChannel().retrieveMessageById(event.getMessageId()).flatMap(message -> message.addReaction(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild()))) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index c1766c1..608f3ff 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -2,15 +2,11 @@ import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.Database; -import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; import com.github.kaktushose.nplaybot.settings.SettingsService; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; -import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,24 +64,9 @@ private void onAddRegularXp(MessageReceivedEvent event) { rankService.updateValidMessage(event.getAuthor()); var result = rankService.addRandomXp(event.getAuthor()); - onXpChange(result, event.getMember(), event.getGuild()); - } - - private void onXpChange(XpChangeResult result, Member member, Guild guild) { - log.debug("Checking for rank up: {}", member); - rankService.updateRankRoles(member, guild, result); - - if (!result.rankChanged()) { - log.debug("Rank hasn't changed"); - return; - } - log.debug("Applying changes. New rank: {}", result.currentRank()); - - var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; - var messageData = new MessageCreateBuilder().addContent(member.getAsMention()) - .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).toMessageEmbed()) - .build(); - settingsService.getBotChannel(guild).sendMessage(messageData).queue(); + rankService.onXpChange(result, event.getMember(), event.getGuild(), embedCache).ifPresent(it -> + settingsService.getBotChannel(event.getGuild()).sendMessage(it).queue() + ); } private void onXpLootDrop(MessageReceivedEvent event) { @@ -118,7 +99,9 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { var xp = xpLootDrops.get(messageId); var result = rankService.addXp(event.getMember(), xp); - onXpChange(result, event.getMember(), event.getGuild()); + rankService.onXpChange(result, event.getMember(), event.getGuild(), embedCache).ifPresent(it -> + settingsService.getBotChannel(event.getGuild()).sendMessage(it).queue() + ); xpLootDrops.remove(messageId); event.retrieveMessage().queue(message -> { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index ae5563c..178d9e2 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot.rank; +import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.rank.leaderboard.LeaderboardPage; import com.github.kaktushose.nplaybot.rank.model.RankConfig; import com.github.kaktushose.nplaybot.rank.model.RankInfo; @@ -10,6 +11,8 @@ import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -338,6 +341,22 @@ public XpChangeResult setXp(UserSnowflake user, int value) { } } + public Optional onXpChange(XpChangeResult result, Member member, Guild guild, EmbedCache embedCache) { + log.debug("Checking for rank up: {}", member); + updateRankRoles(member, guild, result); + + if (!result.rankChanged()) { + log.debug("Rank hasn't changed"); + return Optional.empty(); + } + log.debug("Applying changes. New rank: {}", result.currentRank()); + + var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; + return Optional.of(new MessageCreateBuilder().addContent(member.getAsMention()) + .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).toMessageEmbed()) + .build()); + } + public void updateRankRoles(Member member, Guild guild, XpChangeResult result) { var validRole = guild.getRoleById(result.currentRank().roleId()); var invalidRoles = getRankRoleIds().stream() diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java index 33abb34..b4216ee 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -9,6 +9,7 @@ import com.github.kaktushose.nplaybot.rank.model.UserInfo; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.api.interactions.commands.Command; @Interaction @@ -24,16 +25,27 @@ public class RankInfoCommand { public void onRankInfo(CommandEvent event, @Optional Member member) { var target = member == null ? event.getMember() : member; UserInfo userInfo = database.getRankService().getUserInfo(target); - - var embed = userInfo.nextRank().isPresent() ? "rankInfo" : "rankInfoMax"; - event.reply(embedCache.getEmbed(embed).injectValues(userInfo.getEmbedValues(target))); + sendReply(userInfo, target.getUser(), event); } @ContextCommand(value = "Kontoinformation abrufen", type = Command.Type.USER, isGuildOnly = true, ephemeral = true) public void onContextRankInfo(CommandEvent event, User user) { UserInfo userInfo = database.getRankService().getUserInfo(user); + sendReply(userInfo, user, event); + } - var embed = userInfo.nextRank().isPresent() ? "rankInfo" : "rankInfoMax"; - event.reply(embedCache.getEmbed(embed).injectValues(userInfo.getEmbedValues(user))); + private void sendReply(UserInfo userInfo, User user, CommandEvent event) { + var embed = embedCache.getEmbed(userInfo.nextRank().isPresent() ? "rankInfo" : "rankInfoMax") + .injectValues(userInfo.getEmbedValues(user)) + .toEmbedBuilder(); + + var guild = event.getGuild(); + if (database.getCollectEventService().isActive(guild)) { + var currency = database.getCollectEventService().getCollectCurrency(guild); + var points = database.getCollectEventService().getCollectPoints(user); + embed.addField(currency.name(), String.format("%s %d", currency.emoji(), points), false); + } + event.reply(embed); } + } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java index af6e222..774022b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java @@ -1,6 +1,5 @@ package com.github.kaktushose.nplaybot.rank.model; -import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; import java.util.HashMap; @@ -9,23 +8,6 @@ public record UserInfo(int currentXp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain) { - public Map getEmbedValues(Member member) { - var result = new HashMap() {{ - put("user", String.format("<@%d>", member.getIdLong())); - put("color", currentRank.color()); - put("avatarUrl", member.getEffectiveAvatarUrl()); - put("currentRank", String.format("<@&%d>", currentRank.roleId())); - put("currentXp", currentXp); - put("xpGain", xpGain); - put("messageCount", messageCount); - }}; - nextRank.ifPresent(rank -> { - result.put("nextRank", String.format("<@&%d>", rank.roleId())); - result.put("missingXp", rank.xpBound() - currentXp); - }); - return result; - } - public Map getEmbedValues(User user) { var result = new HashMap() {{ put("user", String.format("<@%d>", user.getIdLong())); diff --git a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql index da377f4..7582163 100644 --- a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql +++ b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql @@ -23,7 +23,24 @@ CREATE TABLE collect_rewards ( reward_id SERIAL PRIMARY KEY, name VARCHAR NOT NULL DEFAULT 0, threshold INT NOT NULL, - xp INT, - role_id BIGINT, + xp INT NOT NULL DEFAULT 0, + role_id BIGINT NOT NULL DEFAULT 0, embed JSONB NOT NULL ); + +CREATE FUNCTION get_collect_loot_drop(id BIGINT) +RETURNS TABLE (points INT) AS +$$ +DECLARE + chance INT; + drop_chance REAL; +BEGIN + SELECT event_settings.collect_loot_chance INTO drop_chance FROM event_settings WHERE guild_id = id; + chance := floor(random() * 100) + 1; + IF ROUND(drop_chance) >= chance THEN + RETURN QUERY SELECT 1; + ELSE + RETURN QUERY SELECT 0; + END IF; +END; +$$ LANGUAGE plpgsql; From eff6bedb87ae75374b6690672e9d41e3c55f5d4e Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Fri, 10 May 2024 11:35:27 +0200 Subject: [PATCH 45/66] fix typos --- embeds.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embeds.json b/embeds.json index 18aaa4f..45e1a5a 100644 --- a/embeds.json +++ b/embeds.json @@ -222,7 +222,7 @@ }, "collectLootChanceUpdate": { "title": "Erfolg", - "description": "Die Wahrscheinlichkeit für zufällige Collect-Loo-Drops wurde auf {chance} gesetzt" + "description": "Die Wahrscheinlichkeit für zufällige Collect-Loot-Drops wurde auf {chance}% gesetzt" }, "collectLootDropClaimed": { "title": "Glückwunsch :tada:", From e5865e200b783d0ab5d232b411255ae04300cfc2 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Fri, 10 May 2024 11:35:47 +0200 Subject: [PATCH 46/66] add missing permissions annotation for SwitchDailyMessageCommand --- .../nplaybot/rank/daily/SwitchDailyMessageCommand.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/daily/SwitchDailyMessageCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/daily/SwitchDailyMessageCommand.java index 8301a57..d1cd90f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/daily/SwitchDailyMessageCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/daily/SwitchDailyMessageCommand.java @@ -3,12 +3,15 @@ import com.github.kaktushose.jda.commands.annotations.Inject; import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; import com.github.kaktushose.jda.commands.annotations.interactions.Param; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; @Interaction +@Permissions(BotPermissions.USER) public class SwitchDailyMessageCommand { @Inject From cb5544485c49e3526a5742c9cf0e4130910140e2 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Fri, 10 May 2024 11:41:26 +0200 Subject: [PATCH 47/66] fix Leaderboard index numbers --- .../nplaybot/rank/leaderboard/LeaderboardCommand.java | 2 +- .../kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java index b320d37..c142e0f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardCommand.java @@ -78,7 +78,7 @@ public void onEnd(ComponentEvent event) { private void reply(Replyable event) { log.debug("Sending new leaderboard with index {}/{}", index, maxIndex); event.with(getButtons()).reply( - embedCache.getEmbed("leaderboard").injectValue("leaderboard", leaderboard.get(index - 1).getPage(guild)) + embedCache.getEmbed("leaderboard").injectValue("leaderboard", leaderboard.get(index - 1).getPage(guild, index - 1)) .toEmbedBuilder() .setFooter(String.format("Seite (%d/%d)", index, maxIndex)) ); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java index c8c1376..bc4b910 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/leaderboard/LeaderboardPage.java @@ -8,11 +8,11 @@ import java.util.Optional; public record LeaderboardPage(List rows) { - public String getPage(Guild guild) { + public String getPage(Guild guild, int index) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < rows.size(); i++) { var row = rows.get(i); - appendRow(builder, i + 1, resolveName(guild, row.userId), row.xp, String.format("<@&%d>", row.roleId)); + appendRow(builder, (i + 1) + index * 10, resolveName(guild, row.userId), row.xp, String.format("<@&%d>", row.roleId)); } return builder.toString(); } From f1b6cee45472e9faca802c72af54fc0313c89434 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Fri, 10 May 2024 12:10:34 +0200 Subject: [PATCH 48/66] adjust embed builder link --- embeds.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embeds.json b/embeds.json index 45e1a5a..9a209c4 100644 --- a/embeds.json +++ b/embeds.json @@ -163,7 +163,7 @@ }, "rewardCreateInvalidEmbed": { "title": "Belohnung: {name}", - "description": "Bitte gib ein gültiges Embed im JSON-Format an. [Online Embed Builder](https://glitchii.github.io/embedbuilder/)", + "description": "Bitte gib ein gültiges Embed im JSON-Format an. [Online Embed Builder](https://glitchii.github.io/embedbuilder/?single=true&data=e30=)", "color": "#ffc926" }, "rewardCreateSummarize": { From 12e432d16146ac5df454da4128fdf64a438916ee Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Fri, 10 May 2024 14:32:00 +0200 Subject: [PATCH 49/66] refactor permissions commands --- embeds.json | 12 +- pom.xml | 2 +- .../nplaybot/permissions/BotPermissions.java | 34 +++-- .../permissions/PermissionCommands.java | 116 +++++++++++------- .../permissions/PermissionsService.java | 70 ++++++++--- .../V2.1.0__add_role_permissions.sql | 23 ++++ 6 files changed, 182 insertions(+), 75 deletions(-) create mode 100644 src/main/resources/db/migration/V2.1.0__add_role_permissions.sql diff --git a/embeds.json b/embeds.json index 9a209c4..f3c4bc9 100644 --- a/embeds.json +++ b/embeds.json @@ -136,14 +136,14 @@ "description": "{channels}", "color": "#67c94f" }, - "userPermissions": { - "title": "Berechtigungen von {user}", - "description": "{permissions}", + "permissionsEdit": { + "title": "Berechtigungen von {target}", + "description": "Bearbeite die Berechtigungen, indem du sie im Select Menü an- oder abwählst", "color": "#67c94f" }, - "permissionsBulkEdit": { - "title": "Erfolg", - "description": "Die Berechtigungen wurden angepasst", + "permissionsList": { + "title": "Berechtigungen von {target}", + "description": "{permissions}", "color": "#67c94f" }, "rewardCreateSelectType": { diff --git a/pom.xml b/pom.xml index 5e23208..9797aa4 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ com.github.Kaktushose jda-commands - da36e41af0 + 649440b3e8 com.zaxxer diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java index 825560f..e3319a6 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java @@ -1,8 +1,6 @@ package com.github.kaktushose.nplaybot.permissions; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; public class BotPermissions { @@ -29,24 +27,42 @@ public class BotPermissions { public static int compute(Set permissions) { int computedPermissions = 0; for (String permission : permissions) { - computedPermissions = computedPermissions | permissionMapping.getOrDefault(permission, 0); + computedPermissions = computedPermissions | getPermissionValue(permission); } return computedPermissions; } public static boolean hasPermissions(Set permissions, int userPermission) { - if (userPermission == permissionMapping.get(BOT_OWNER)) { + if (userPermission == getPermissionValue(BOT_OWNER)) { return true; } return (userPermission & compute(permissions)) != 0; } - public static int grant(int currentPermissions, String permission) { - return currentPermissions |= permissionMapping.getOrDefault(permission, 0); + public static int combine(Collection permissions) { + int result = 0; + for (int permission : permissions) { + result |= permission; + } + return result; + } + + public static Map permissionsMapping() { + return new LinkedHashMap<>(permissionMapping); + } + + public static int getPermissionValue(String permission) { + return permissionMapping.getOrDefault(permission, 0); } - public static int revoke(int currentPermissions, String permission) { - return currentPermissions &= ~permissionMapping.getOrDefault(permission, 0); + public static Set getRawPermissionsValues(int permissions) { + Set result = new HashSet<>(); + permissionMapping.forEach((name, value) -> { + if (hasPermissions(Set.of(name), permissions)) { + result.add(value); + } + }); + return result; } public static String listPermissions(int permissions) { diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java index 36402ba..48f4b75 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java @@ -4,83 +4,111 @@ import com.github.kaktushose.jda.commands.annotations.interactions.*; import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.jda.commands.dispatching.interactions.components.ComponentEvent; import com.github.kaktushose.nplaybot.Database; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; + +import java.util.List; import static com.github.kaktushose.nplaybot.permissions.BotPermissions.*; @Interaction public class PermissionCommands { + private static final String NONE = "NONE"; @Inject private Database database; @Inject private EmbedCache embedCache; + private Member targetMember; + private Role targetRole; @SlashCommand(value = "permissions list", desc = "Zeigt die Berechtigungen eines Users an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) @Permissions(USER) public void onPermissionsList(CommandEvent event, @Optional Member member) { var target = member == null ? event.getMember() : member; - event.reply(embedCache.getEmbed("userPermissions") - .injectValue("user", target.getEffectiveName()) - .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getPermissions(target))) + event.reply(embedCache.getEmbed("permissionsList") + .injectValue("target", target.getEffectiveName()) + .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getMemberPermissions(target))) ); } - @SlashCommand(value = "permissions grant", desc = "Fügt einem Nutzer eine Berechtigung hinzu", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "permissions user edit", desc = "Bearbeitet die Berechtigungen von einem Nutzer. Hat keinen Einfluss auf die Rollen-Berechtigungen", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) @Permissions(MODIFY_USER_PERMISSIONS) - public void onPermissionsGrant(CommandEvent event, - Member member, - @Param("Die Berechtigung die hinzugefügt werden soll") - @Choices({USER, MODIFY_USER_BALANCE, MODIFY_RANK_SETTINGS, MANAGE_EVENTS, MODIFY_USER_PERMISSIONS, BOT_ADMINISTRATOR}) - String permission) { - database.getPermissionsService().grantPermissions(member, permission); - - event.reply(embedCache.getEmbed("userPermissions") - .injectValue("user", member.getEffectiveName()) - .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getPermissions(member))) - ); + public void onPermissionsUserEdit(CommandEvent event, Member member) { + targetMember = member; + + var permissionsMap = BotPermissions.permissionsMapping(); + permissionsMap.put(NONE, 0); + permissionsMap.remove(BOT_OWNER); + + var menu = ((net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu) + event.getJdaCommands().getSelectMenu( + "PermissionCommands.onPermissionsUserSelect", + event.getContext().getRuntime().getRuntimeId()) + ).createCopy(); + menu.getOptions().clear(); + menu.setMaxValues(SelectMenu.OPTIONS_MAX_AMOUNT); + + permissionsMap.forEach((label, value) -> menu.addOption(label, String.valueOf(value))); + var permissions = database.getPermissionsService().getUserPermissions(member.getUser()); + menu.setDefaultValues(BotPermissions.getRawPermissionsValues(permissions).stream().map(String::valueOf).toList()); + + event.getReplyContext().getBuilder().addActionRow(menu.build()); + event.reply(embedCache.getEmbed("permissionsEdit").injectValue("target", targetMember.getEffectiveName())); } - @SlashCommand(value = "permissions revoke", desc = "Entzieht einem Nutzer eine Berechtigung", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @StringSelectMenu(value = "Wähle eine oder mehrere Berechtigungen aus") + @SelectOption(label = "dummy option", value = "dummy option") @Permissions(MODIFY_USER_PERMISSIONS) - public void onPermissionsRevoke(CommandEvent event, - Member member, - @Param("Die Berechtigung die entzogen werden soll") - @Choices({USER, MODIFY_USER_BALANCE, MODIFY_RANK_SETTINGS, MANAGE_EVENTS, MODIFY_USER_PERMISSIONS, BOT_ADMINISTRATOR}) - String permission) { - database.getPermissionsService().revokePermissions(member, permission); - - event.reply(embedCache.getEmbed("userPermissions") - .injectValue("user", member.getEffectiveName()) - .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getPermissions(member))) + public void onPermissionsUserSelect(ComponentEvent event, List selection) { + database.getPermissionsService().setUserPermissions(targetMember, BotPermissions.combine(selection.stream().map(Integer::valueOf).toList())); + + event.keepComponents(false).reply(embedCache.getEmbed("permissionsList") + .injectValue("target", targetMember.getEffectiveName()) + .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getUserPermissions(targetMember.getUser()))) ); } - @SlashCommand(value = "permissions bulk grant", desc = "Fügt allen Nutzern mit der angegebenen Rolle eine Berechtigung hinzu", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "permissions role edit", desc = "Bearbeitet die Berechtigungen von einer Rolle", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) @Permissions(MODIFY_USER_PERMISSIONS) - public void onPermissionsBulkGrant(CommandEvent event, - Role role, - @Param("Die Berechtigung die hinzugefügt werden soll") - @Choices({USER, MODIFY_USER_BALANCE, MODIFY_RANK_SETTINGS, MANAGE_EVENTS, MODIFY_USER_PERMISSIONS, BOT_ADMINISTRATOR}) - String permission) { - event.getGuild().getMembersWithRoles(role).forEach(member -> database.getPermissionsService().grantPermissions(member, permission)); - - event.reply(embedCache.getEmbed("permissionsBulkEdit")); + public void onPermissionsRoleEdit(CommandEvent event, Role role) { + targetRole = role; + + var permissionsMap = BotPermissions.permissionsMapping(); + permissionsMap.put(NONE, 0); + permissionsMap.remove(USER); + permissionsMap.remove(BOT_OWNER); + + var menu = ((net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu) + event.getJdaCommands().getSelectMenu( + "PermissionCommands.onPermissionsRoleSelect", + event.getContext().getRuntime().getRuntimeId()) + ).createCopy(); + menu.getOptions().clear(); + menu.setMaxValues(SelectMenu.OPTIONS_MAX_AMOUNT); + + permissionsMap.forEach((label, value) -> menu.addOption(label, String.valueOf(value))); + var permissions = database.getPermissionsService().getRolePermissions(List.of(targetRole)); + menu.setDefaultValues(BotPermissions.getRawPermissionsValues(permissions).stream().map(String::valueOf).toList()); + + event.getReplyContext().getBuilder().addActionRow(menu.build()); + event.reply(embedCache.getEmbed("permissionsEdit").injectValue("target", targetRole.getName())); } - @SlashCommand(value = "permissions bulk revoke", desc = "Entzieht allen Nutzern mit der angegebenen Rolle eine Berechtigung", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @StringSelectMenu(value = "Wähle eine oder mehrere Berechtigungen aus") + @SelectOption(label = "dummy option", value = "dummy option") @Permissions(MODIFY_USER_PERMISSIONS) - public void onPermissionsBulkRevoke(CommandEvent event, - Role role, - @Param("Die Berechtigung die entzogen werden soll") - @Choices({USER, MODIFY_USER_BALANCE, MODIFY_RANK_SETTINGS, MANAGE_EVENTS, MODIFY_USER_PERMISSIONS, BOT_ADMINISTRATOR}) - String permission) { - event.getGuild().getMembersWithRoles(role).forEach(member -> database.getPermissionsService().revokePermissions(member, permission)); - - event.reply(embedCache.getEmbed("permissionsBulkEdit")); + public void onPermissionsRoleSelect(ComponentEvent event, List selection) { + database.getPermissionsService().setRolePermissions(targetRole, BotPermissions.combine(selection.stream().map(Integer::valueOf).toList())); + + event.keepComponents(false).reply(embedCache.getEmbed("permissionsList") + .injectValue("target", targetRole.getName()) + .injectValue("permissions", BotPermissions.listPermissions(database.getPermissionsService().getRolePermissions(List.of(targetRole)))) + ); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java index 1c855ec..4b74f3d 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java @@ -1,6 +1,6 @@ package com.github.kaktushose.nplaybot.permissions; -import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.entities.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -8,6 +8,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.List; import java.util.Set; public class PermissionsService { @@ -19,7 +20,7 @@ public PermissionsService(DataSource dataSource) { this.dataSource = dataSource; } - public int getPermissions(UserSnowflake user) { + public int getUserPermissions(User user) { log.debug("Querying permissions for user {}", user); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -38,8 +39,42 @@ public int getPermissions(UserSnowflake user) { } } - public void grantPermissions(UserSnowflake user, String permission) { - log.warn("Granting permission {} for user {}", permission, user); + public int getRolePermissions(List roles) { + log.debug("Querying permissions for member roles"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM get_role_permissions(?)"); + statement.setArray(1, connection.createArrayOf("BIGINT", roles.stream().map(ISnowflake::getIdLong).toArray())); + var result = statement.executeQuery(); + result.next(); + var permissions = result.getInt(1); + if (permissions >= BotPermissions.getPermissionValue(BotPermissions.BOT_OWNER)) { + permissions = BotPermissions.getPermissionValue(BotPermissions.BOT_OWNER); + } + return permissions; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns the combined permissions from {@link #getUserPermissions(User)} and {@link #getRolePermissions(List)} for + * the given member + * + * @param member the {@link Member} to return the combined permissions for + * @return the combined permissions + */ + public int getMemberPermissions(Member member) { + log.debug("Querying permissions for member {}", member); + int permissions = getUserPermissions(member.getUser()) | getRolePermissions(member.getRoles()); + + if (permissions >= BotPermissions.getPermissionValue(BotPermissions.BOT_OWNER)) { + permissions = BotPermissions.getPermissionValue(BotPermissions.BOT_OWNER); + } + return permissions; + } + + public void setUserPermissions(UserSnowflake user, int permissions) { + log.info("Granting permission {} for user {}", permissions, user); try (Connection connection = dataSource.getConnection()) { PreparedStatement statement = connection.prepareStatement(""" UPDATE users @@ -48,7 +83,7 @@ public void grantPermissions(UserSnowflake user, String permission) { """ ); - statement.setLong(1, BotPermissions.grant(getPermissions(user), permission)); + statement.setLong(1, permissions); statement.setLong(2, user.getIdLong()); statement.execute(); @@ -57,27 +92,32 @@ public void grantPermissions(UserSnowflake user, String permission) { } } - public void revokePermissions(UserSnowflake user, String permission) { - log.warn("Revoking permission {} for user {}", permission, user); + public void setRolePermissions(Role role, int permissions) { + log.info("Granting permission {} for role {}", permissions, role); try (Connection connection = dataSource.getConnection()) { PreparedStatement statement = connection.prepareStatement(""" - UPDATE users - SET permissions = ? - WHERE user_id = ? + INSERT INTO role_permissions + VALUES(?, ?) + ON CONFLICT (role_id) DO UPDATE SET permissions = EXCLUDED.permissions; """ ); - statement.setLong(1, BotPermissions.revoke(getPermissions(user), permission)); - statement.setLong(2, user.getIdLong()); - + statement.setLong(1, role.getIdLong()); + statement.setLong(2, permissions); statement.execute(); } catch (SQLException e) { throw new RuntimeException(e); } } - public boolean hasPermissions(UserSnowflake user, Set permissions) { + public boolean hasPermissions(Member member, Set permissions) { + log.debug("Checking permissions for member {}", member); + return BotPermissions.hasPermissions(permissions, getMemberPermissions(member)); + } + + public boolean hasPermissions(User user, Set permissions) { log.debug("Checking permissions for user {}", user); - return BotPermissions.hasPermissions(permissions, getPermissions(user)); + return BotPermissions.hasPermissions(permissions, getUserPermissions(user)); } + } diff --git a/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql b/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql new file mode 100644 index 0000000..9418340 --- /dev/null +++ b/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql @@ -0,0 +1,23 @@ +CREATE TABLE role_permissions ( + role_id BIGINT PRIMARY KEY NOT NULL, + permissions INTEGER NOT NULL DEFAULT 1; +); + +CREATE FUNCTION get_role_permissions(ids BIGINT[]) +RETURNS TABLE (permissions INT) AS +$$ +DECLARE + combined_permissions INTEGER := 0; + role_permission INTEGER; + id BIGINT; +BEGIN + FOREACH id IN ARRAY ids LOOP + SELECT role_permissions.permissions INTO role_permission FROM role_permissions WHERE role_permissions.role_id = id; + IF NOT FOUND THEN + CONTINUE; + END IF; + combined_permissions := combined_permissions | role_permission; + END LOOP; + RETURN QUERY SELECT combined_permissions; +END; +$$ LANGUAGE plpgsql; From 13f88ba18c0d3f7631a4886da39a4b0bb7f371f2 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Fri, 10 May 2024 14:32:20 +0200 Subject: [PATCH 50/66] reformat code --- .../com/github/kaktushose/nplaybot/Database.java | 1 - .../nplaybot/events/collect/CollectEventService.java | 12 ++++++------ .../events/collect/CollectRewardCommands.java | 10 ++++++---- .../nplaybot/events/contest/ContestEventService.java | 1 + .../nplaybot/rank/commands/RankInfoCommand.java | 1 - 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index b029e03..3183701 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -7,7 +7,6 @@ import com.github.kaktushose.nplaybot.settings.SettingsService; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import net.dv8tion.jda.api.entities.Role; public class Database { diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java index bdda76a..53f3491 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java @@ -134,9 +134,6 @@ public int getCollectLootDrop(Message message) { } } - public record CollectCurrency(String name, String emoji) { - } - public CollectCurrency getCollectCurrency(Guild guild) { log.debug("Querying collect currency emoji"); try (Connection connection = dataSource.getConnection()) { @@ -157,9 +154,6 @@ public CollectCurrency getCollectCurrency(Guild guild) { } } - public record CollectReward(int rewardId, String name, int threshold, int xp, long roleId, String embed) { - } - public List getCollectRewards() { log.debug("Querying collect rewards"); try (Connection connection = dataSource.getConnection()) { @@ -218,4 +212,10 @@ public int getCollectPoints(UserSnowflake user) { throw new RuntimeException(e); } } + + public record CollectCurrency(String name, String emoji) { + } + + public record CollectReward(int rewardId, String name, int threshold, int xp, long roleId, String embed) { + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java index 3da17e8..c05b879 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java @@ -9,7 +9,9 @@ import com.github.kaktushose.jda.commands.dispatching.reply.Replyable; import com.github.kaktushose.nplaybot.Database; import com.github.kaktushose.nplaybot.permissions.BotPermissions; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Mentions; import net.dv8tion.jda.api.entities.Role; @@ -24,13 +26,13 @@ @Permissions(BotPermissions.MANAGE_EVENTS) public class CollectRewardCommands { + private static final Gson gson = new Gson(); + private static final String ROLE_REWARD = "Rolle"; + private static final String XP_REWARD = "XP"; @Inject private EmbedCache embedCache; @Inject private Database database; - private static final Gson gson = new Gson(); - private static final String ROLE_REWARD = "Rolle"; - private static final String XP_REWARD = "XP"; private String name; private String rewardType; private Role role; diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java index 433ddd1..31253ff 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java @@ -16,6 +16,7 @@ public class ContestEventService { private static final Logger log = LoggerFactory.getLogger(ContestEventService.class); private final DataSource dataSource; + public ContestEventService(DataSource dataSource) { this.dataSource = dataSource; } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java index b4216ee..7a670c1 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -9,7 +9,6 @@ import com.github.kaktushose.nplaybot.rank.model.UserInfo; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.api.interactions.commands.Command; @Interaction From e8471d3a49db72280f0242a2841070f3f729d8bf Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Fri, 10 May 2024 14:50:05 +0200 Subject: [PATCH 51/66] fix sql migration file --- .../resources/db/migration/V2.1.0__add_role_permissions.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql b/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql index 9418340..7b5091b 100644 --- a/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql +++ b/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql @@ -1,6 +1,6 @@ CREATE TABLE role_permissions ( role_id BIGINT PRIMARY KEY NOT NULL, - permissions INTEGER NOT NULL DEFAULT 1; + permissions INTEGER NOT NULL DEFAULT 1 ); CREATE FUNCTION get_role_permissions(ids BIGINT[]) From 6a1d13ffd5ecc8bc2295b9bc95253633372b73c8 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sat, 11 May 2024 14:14:02 +0200 Subject: [PATCH 52/66] first pass on karma system --- embeds.json | 32 ++- .../com/github/kaktushose/nplaybot/Bot.java | 4 +- .../github/kaktushose/nplaybot/Database.java | 7 + .../events/collect/CollectEventService.java | 2 +- .../nplaybot/karma/KarmaConfigCommands.java | 64 +++++ .../nplaybot/karma/KarmaListener.java | 133 +++++++++ .../nplaybot/karma/KarmaRewardCommands.java | 172 ++++++++++++ .../nplaybot/karma/KarmaService.java | 252 ++++++++++++++++++ .../nplaybot/karma/KarmaTokenTask.java | 15 ++ .../nplaybot/permissions/BotPermissions.java | 10 +- .../permissions/PermissionCommands.java | 8 +- .../kaktushose/nplaybot/rank/RankService.java | 10 +- .../rank/commands/RankConfigCommands.java | 2 +- .../nplaybot/rank/model/UserInfo.java | 3 +- .../nplaybot/scheduler/TaskScheduler.java | 2 +- .../migration/V3.0.0__setup_karma_system.sql | 18 ++ 16 files changed, 715 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/karma/KarmaTokenTask.java create mode 100644 src/main/resources/db/migration/V3.0.0__setup_karma_system.sql diff --git a/embeds.json b/embeds.json index f3c4bc9..920ac65 100644 --- a/embeds.json +++ b/embeds.json @@ -49,6 +49,10 @@ "name": "Aktuelle XP", "value": ":star2: {currentXp}" }, + { + "name": "Aktuelles Karma", + "value": ":thumbs_up: {karma}" + }, { "name": "XP-Zuwachs (24h):", "value": ":chart_with_upwards_trend: {xpGain}" @@ -75,6 +79,10 @@ "name": "Aktuelle XP", "value": ":star2: {currentXp}" }, + { + "name": "Aktuelles Karma", + "value": ":thumbs_up: {karma}" + }, { "name": "XP-Zuwachs (24h):", "value": ":chart_with_upwards_trend: {xpGain}" @@ -126,7 +134,7 @@ "color": "#67c94f" }, "rankConfig": { - "title": "Rank Configuration", + "title": "Rank Konfiguration", "description": "Cooldown: `{cooldown}ms`\nNachrichtenlänge: `{minLength} Buchstaben`\nXP Loot Wahrscheinlichkeit: `{lootChance}%`\n", "color": "#67c94f" @@ -163,7 +171,7 @@ }, "rewardCreateInvalidEmbed": { "title": "Belohnung: {name}", - "description": "Bitte gib ein gültiges Embed im JSON-Format an. [Online Embed Builder](https://glitchii.github.io/embedbuilder/?single=true&data=e30=)", + "description": "Bitte gib ein gültiges Embed im JSON-Format an. [Online Embed Builder](https://glitchii.github.io/embedbuilder/?data=e30=)", "color": "#ffc926" }, "rewardCreateSummarize": { @@ -228,5 +236,25 @@ "title": "Glückwunsch :tada:", "description": "{user} hat 1 {name} gefunden", "color": "#67c94f" + }, + "setKarmaResult": { + "title": "Erfolg", + "description": "Die Karma Punkte von {user} wurden auf {karma} Karma gesetzt", + "color": "#67c94f" + }, + "addKarmaResult": { + "title": "Erfolg", + "description": "{user} wurden {karma} Karma hinzugefügt", + "color": "#67c94f" + }, + "karmaConfig": { + "title": "Karma Konfiguration", + "description": "Erlaubte Emojis: {emojis}\nKarma Tokens pro Nutzer: `{tokens}`", + "color": "#67c94f" + }, + "karmaRewardRemove": { + "title": "Achtung", + "description": "Lieber {user},\ndu hast in letzter Zeit zu viel Karma verloren und somit wurden dir deine Belohnungen wieder abgezogen", + "color": "#ffc926" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 54d175b..1885404 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -5,6 +5,7 @@ import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.events.collect.CollectEventListener; import com.github.kaktushose.nplaybot.events.contest.ContestListener; +import com.github.kaktushose.nplaybot.karma.KarmaListener; import com.github.kaktushose.nplaybot.permissions.CustomPermissionsProvider; import com.github.kaktushose.nplaybot.rank.JoinLeaveListener; import com.github.kaktushose.nplaybot.rank.RankListener; @@ -49,7 +50,8 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { new RankListener(database, embedCache), new JoinLeaveListener(database.getRankService()), new ContestListener(database.getContestEventService()), - new CollectEventListener(database, embedCache) + new CollectEventListener(database, embedCache), + new KarmaListener(database, embedCache) ) .build().awaitReady(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 3183701..702a7d7 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -2,6 +2,7 @@ import com.github.kaktushose.nplaybot.events.collect.CollectEventService; import com.github.kaktushose.nplaybot.events.contest.ContestEventService; +import com.github.kaktushose.nplaybot.karma.KarmaService; import com.github.kaktushose.nplaybot.permissions.PermissionsService; import com.github.kaktushose.nplaybot.rank.RankService; import com.github.kaktushose.nplaybot.settings.SettingsService; @@ -16,6 +17,7 @@ public class Database { private final ContestEventService contestEventService; private final CollectEventService collectEventService; private final PermissionsService permissionsService; + private final KarmaService karmaService; public Database() { var config = new HikariConfig(); @@ -32,6 +34,7 @@ public Database() { contestEventService = new ContestEventService(dataSource); collectEventService = new CollectEventService(dataSource); permissionsService = new PermissionsService(dataSource); + karmaService = new KarmaService(dataSource); } public void closeDataSource() { @@ -57,4 +60,8 @@ public CollectEventService getCollectEventService() { public PermissionsService getPermissionsService() { return permissionsService; } + + public KarmaService getKarmaService() { + return karmaService; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java index 53f3491..abdf815 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java @@ -178,7 +178,7 @@ public List getCollectRewards() { } public void deleteCollectReward(int rewardId) { - log.debug("Deleting reward with id {}", rewardId); + log.debug("Deleting collect reward with id {}", rewardId); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("DELETE FROM collect_rewards WHERE reward_id = ?"); statement.setInt(1, rewardId); diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java new file mode 100644 index 0000000..cb1f071 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java @@ -0,0 +1,64 @@ +package com.github.kaktushose.nplaybot.karma; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.constraints.Max; +import com.github.kaktushose.jda.commands.annotations.constraints.Min; +import com.github.kaktushose.jda.commands.annotations.interactions.Interaction; +import com.github.kaktushose.jda.commands.annotations.interactions.Permissions; +import com.github.kaktushose.jda.commands.annotations.interactions.SlashCommand; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; + +@Interaction +public class KarmaConfigCommands { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + + @SlashCommand(value = "add karma", desc = "Fügt einem User Karma hinzu", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @Permissions(BotPermissions.MODIFY_USER_BALANCE) + public void onAddKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE) @Max(Integer.MAX_VALUE) int amount) { + database.getKarmaService().addKarma(target, amount); + + event.reply(embedCache.getEmbed("addKarmaResult") + .injectValue("user", target.getAsMention()) + .injectValue("karma", amount) + ); + } + + @SlashCommand(value = "set karma", desc = "Setzt die Karma Punkte von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @Permissions(BotPermissions.MODIFY_USER_BALANCE) + public void onSetKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE) @Max(Integer.MAX_VALUE) int value) { + database.getKarmaService().setKarma(target, value); + + event.reply(embedCache.getEmbed("setKarmaResult") + .injectValue("user", target.getAsMention()) + .injectValue("karma", value) + ); + } + + @SlashCommand(value = "set default karma-tokens", desc = "Legt die tägliche Anzahl an Karma-Tokens für jeden Nutzer fest", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @Permissions(BotPermissions.MANAGE_KARMA_SETTINGS) + public void onSetKarmaTokens(CommandEvent event, @Min(1) @Max(Integer.MAX_VALUE) int value) { + database.getKarmaService().setDefaultTokens(event.getGuild(), value); + onGetKarmaConfig(event); + } + + @SlashCommand(value = "get karma config", desc = "Zeigt die Einstellungen für das Karma System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @Permissions(BotPermissions.MANAGE_KARMA_SETTINGS) + public void onGetKarmaConfig(CommandEvent event) { + var emojis = database.getKarmaService().getValidEmojis(event.getGuild()); + var builder = new StringBuilder(); + emojis.forEach(it -> builder.append(it.getFormatted()).append(" ")); + event.reply(embedCache.getEmbed("karmaConfig") + .injectValue("emojis", builder) + .injectValue("tokens", database.getKarmaService().getDefaultTokens(event.getGuild())) + ); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java new file mode 100644 index 0000000..d04e1ad --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java @@ -0,0 +1,133 @@ +package com.github.kaktushose.nplaybot.karma; + +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.rank.RankService; +import com.github.kaktushose.nplaybot.settings.SettingsService; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import org.jetbrains.annotations.NotNull; + +public class KarmaListener extends ListenerAdapter { + + private final KarmaService karmaService; + private final RankService rankService; + private final SettingsService settingsService; + private final EmbedCache embedCache; + + public KarmaListener(Database database, EmbedCache embedCache) { + karmaService = database.getKarmaService(); + rankService = database.getRankService(); + settingsService = database.getSettingsService(); + this.embedCache = embedCache; + } + + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { + return; + } + if (event.getUser().isBot()) { + return; + } + if (event.getUser().getIdLong() == event.getMessageAuthorIdLong()) { + return; + } + if (!karmaService.getValidEmojis(event.getGuild()).contains(event.getEmoji())) { + return; + } + int oldKarma = rankService.getUserInfo(UserSnowflake.fromId(event.getMessageAuthorIdLong())).karma(); + int newKarma = karmaService.onKarmaVoteAdd(event.getUser(), UserSnowflake.fromId(event.getMessageAuthorIdLong())); + event.retrieveMessage().queue(message -> onKarmaIncrease(oldKarma, newKarma, message.getMember(), message.getGuild())); + } + + @Override + public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { + if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { + return; + } + if (event.getUser().isBot()) { + return; + } + if (!karmaService.getValidEmojis(event.getGuild()).contains(event.getEmoji())) { + return; + } + event.retrieveMessage().queue(message -> { + if (event.getUser().getIdLong() == message.getAuthor().getIdLong()) { + return; + } + int oldKarma = rankService.getUserInfo(message.getAuthor()).karma(); + int newKarma = karmaService.onKarmaVoteRemove(event.getUser(), message.getAuthor()); + onKarmaDecrease(oldKarma, newKarma, message.getMember(), message.getGuild()); + }); + } + + private void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild guild) { + var rewards = karmaService.getKarmaRewards(); + var optional = rewards.stream() + .filter(it -> it.threshold() > oldKarma) + .filter(it -> it.threshold() <= newKarma) + .findFirst(); + + if (optional.isEmpty()) { + return; + } + var reward = optional.get(); + + if (reward.xp() > 0) { + var xpChangeResult = rankService.addXp(member, reward.xp()); + rankService.onXpChange(xpChangeResult, member, guild, embedCache).ifPresent(it -> + settingsService.getBotChannel(guild).sendMessage(it).queue() + ); + } + + if (reward.roleId() > 0) { + guild.addRoleToMember(member, guild.getRoleById(reward.roleId())).queue(); + } + + var builder = new MessageCreateBuilder().addContent(member.getAsMention()) + .addEmbeds(EmbedBuilder.fromData(DataObject.fromJson(reward.embed())).build()) + .build(); + settingsService.getBotChannel(guild).sendMessage(builder).queue(); + } + + private void onKarmaDecrease(int oldKarma, int newKarma, Member member, Guild guild) { + var rewards = karmaService.getKarmaRewards(); + var optional = rewards.stream() + .filter(it -> it.threshold() < oldKarma) + .filter(it -> it.threshold() >= newKarma) + .findFirst(); + + if (optional.isEmpty()) { + return; + } + var reward = optional.get(); + + if (reward.xp() > 0) { + var xpChangeResult = rankService.addXp(member, -reward.xp()); + rankService.onXpChange(xpChangeResult, member, guild, embedCache).ifPresent(it -> + settingsService.getBotChannel(guild).sendMessage(it).queue() + ); + } + + if (reward.roleId() > 0) { + guild.removeRoleFromMember(member, guild.getRoleById(reward.roleId())).queue(); + } + + var builder = new MessageCreateBuilder() + .addContent(member.getAsMention()) + .addEmbeds(embedCache.getEmbed("karmaRewardRemove") + .injectValue("user", member.getAsMention()) + .toEmbedBuilder() + .build() + ).build(); + settingsService.getBotChannel(guild).sendMessage(builder).queue(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java new file mode 100644 index 0000000..c418781 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java @@ -0,0 +1,172 @@ +package com.github.kaktushose.nplaybot.karma; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.*; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.jda.commands.dispatching.interactions.components.ComponentEvent; +import com.github.kaktushose.jda.commands.dispatching.interactions.modals.ModalEvent; +import com.github.kaktushose.jda.commands.dispatching.reply.Replyable; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Mentions; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; +import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; + +import java.util.List; +import java.util.Optional; + +@Interaction +@Permissions(BotPermissions.MANAGE_KARMA_SETTINGS) +public class KarmaRewardCommands { + + private static final Gson gson = new Gson(); + private static final String ROLE_REWARD = "Rolle"; + private static final String XP_REWARD = "XP"; + @Inject + private EmbedCache embedCache; + @Inject + private Database database; + private String name; + private String rewardType; + private Role role; + private int xp; + private int threshold; + private String embed; + + @SlashCommand(value = "karma reward create", desc = "Erstellt eine Belohnung für das Karma System", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onRewardCreate(CommandEvent event, @Param("Der interne Name dieser Belohnung") String name, @Param("Der Wert, ab wann die Belohnung vergeben werden soll") int threshold) { + this.name = name; + this.threshold = threshold; + event.withSelectMenus("onSelectType").reply(embedCache.getEmbed("rewardCreateSelectType").injectValue("name", name)); + } + + @StringSelectMenu("Wähle eine Belohnungsart aus") + @SelectOption(label = "Rolle", value = ROLE_REWARD) + @SelectOption(label = "XP", value = XP_REWARD) + public void onSelectType(ComponentEvent event, List selection) { + rewardType = selection.get(0); + if (ROLE_REWARD.equals(rewardType)) { + event.withSelectMenus("onSelectRole").reply(embedCache.getEmbed("rewardCreateSelectRole").injectValue("name", name)); + } else if (XP_REWARD.equals(rewardType)) { + event.replyModal("onSelectXp"); + } else { + throw new IllegalArgumentException(String.format("%s ist keine gültige Auswahl!", rewardType)); + } + } + + @EntitySelectMenu(value = net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu.SelectTarget.ROLE, placeholder = "Wähle eine Rolle aus") + public void onSelectRole(ComponentEvent event, Mentions mentions) { + role = mentions.getRoles().get(0); + event.replyModal("onSelectEmbed"); + } + + @Modal("Embed angeben") + public void onSelectEmbed(ModalEvent event, @TextInput(value = "Das Embed im JSON-Format", label = "Embed") String embed) { + parseJson(embed).ifPresentOrElse(it -> { + this.embed = it; + finishSetup(event); + }, () -> event.withButtons("onRetryEmbedInput").reply(embedCache.getEmbed("rewardCreateInvalidEmbed").injectValue("name", name))); + } + + @Button("neue Eingabe") + public void onRetryEmbedInput(ComponentEvent event) { + event.replyModal("onSelectEmbed"); + } + + @Modal("XP-Belohnung") + public void onSelectXp(ModalEvent event, + @TextInput(style = TextInputStyle.SHORT, label = "XP-Menge", value = "Eine Zahl zwischen 1 und 2.147.483.647") String amount, + @TextInput(value = "Das Embed im JSON-Format", label = "Embed") String embed) { + int xp; + try { + xp = Integer.parseInt(amount); + } catch (NumberFormatException ignored) { + event.withButtons("onRetryXpInput").reply(embedCache.getEmbed("rewardCreateInvalidXp").injectValue("name", name)); + return; + } + if (xp < 1) { + event.withButtons("onRetryXpInput").reply(embedCache.getEmbed("rewardCreateInvalidXp").injectValue("name", name)); + return; + } + parseJson(embed).ifPresentOrElse(it -> { + this.embed = it; + this.xp = xp; + finishSetup(event); + }, () -> event.withButtons("onRetryXpInput").reply(embedCache.getEmbed("rewardCreateInvalidEmbed").injectValue("name", name))); + } + + @Button("neue Eingabe") + public void onRetryXpInput(ComponentEvent event) { + event.replyModal("onSelectXp"); + } + + private void finishSetup(Replyable event) { + event.withButtons("onConfirm", "onCancel").reply(embedCache.getEmbed("rewardCreateSummarize") + .injectValue("name", name) + .injectValue("type", rewardType) + .injectValue("threshold", threshold) + .injectValue("reward", role == null ? String.valueOf(xp) : role.getAsMention()) + ); + } + + @Button(value = "Erstellen", style = ButtonStyle.SUCCESS) + public void onConfirm(ComponentEvent event) { + database.getKarmaService().createKarmaReward(name, threshold, xp, role, embed); + event.reply(embedCache.getEmbed("rewardCreateConfirm").injectValue("name", name)); + event.removeComponents(); + } + + @Button(value = "Abbrechen", style = ButtonStyle.DANGER) + public void onCancel(ComponentEvent event) { + event.reply(embedCache.getEmbed("rewardCreateCancel")); + event.removeComponents(); + } + + @SlashCommand(value = "karma reward delete", desc = "Löscht eine oder mehrere Belohnung(en) für das Karma System", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + public void onRewardDelete(CommandEvent event) { + var rewards = database.getKarmaService().getKarmaRewards(); + + var menu = ((net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu) + event.getJdaCommands().getSelectMenu("KarmaRewardCommands.onRewardDeleteSelect")).createCopy(); + menu.getOptions().clear(); + menu.setMaxValues(SelectMenu.OPTIONS_MAX_AMOUNT); + rewards.forEach(it -> menu.addOption(it.name(), String.valueOf(it.rewardId()))); + event.getReplyContext().getBuilder().addActionRow(menu.build()); + event.reply(embedCache.getEmbed("rewardDeleteSelect")); + } + + @StringSelectMenu(value = "Wähle eine oder mehrere Belohnungen aus") + @SelectOption(label = "dummy option", value = "dummy option") + public void onRewardDeleteSelect(ComponentEvent event, List selection) { + for (var id : selection) { + database.getKarmaService().deleteKarmaReward(Integer.parseInt(id)); + } + event.reply(embedCache.getEmbed("rewardDelete")); + } + + private java.util.Optional parseJson(String json) { + try { + JsonObject object = gson.fromJson(json, JsonObject.class); + if (object.has("embeds")) { + if (!object.get("embeds").isJsonArray()) { + return java.util.Optional.empty(); + } + object = object.get("embeds").getAsJsonArray().get(0).getAsJsonObject(); + } + if (object.has("title") || object.has("description")) { + return java.util.Optional.of(object.toString()); + } + return java.util.Optional.empty(); + } catch (JsonSyntaxException ignored) { + return Optional.empty(); + } + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java new file mode 100644 index 0000000..85aaec6 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java @@ -0,0 +1,252 @@ +package com.github.kaktushose.nplaybot.karma; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class KarmaService { + + private static final Logger log = LoggerFactory.getLogger(KarmaService.class); + private final DataSource dataSource; + + public KarmaService(DataSource dataSource) { + this.dataSource = dataSource; + } + + public void setKarma(UserSnowflake user, int karma) { + log.debug("Setting karma of {} to {}", user, karma); + try (var connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE users + SET karma_points = ? + WHERE user_id = ? + """); + statement.setInt(1, karma); + statement.setLong(2, user.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void addKarma(UserSnowflake user, int karma) { + log.debug("Increasing karma points of {} by {}", user, karma); + try (var connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE users + SET karma_points = karma_points + ? + WHERE user_id = ? + """); + statement.setInt(1, karma); + statement.setLong(2, user.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void resetTokens() { + log.debug("Resetting all karma tokens to default value"); + try (var connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("UPDATE users SET karma_tokens = karma_settings.default_tokens"); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void decreaseTokens(UserSnowflake user, int decrease) { + log.debug("Decreasing karma tokens of {} by {}", user, decrease); + try (var connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE users + SET karma_tokens = karma_tokens - ? + WHERE user_id = ? + """); + statement.setInt(1, decrease); + statement.setLong(2, user.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int onKarmaVoteAdd(UserSnowflake author, UserSnowflake target) { + log.debug("Performing karma vote of {} for {}", author, target); + try (var connection = dataSource.getConnection()) { + if (getUserTokens(author) > 0) { + addKarma(target, 1); + decreaseTokens(author, 1); + } + + var statement = connection.prepareStatement("SELECT karma_points FROM users WHERE user_id = ?"); + statement.setLong(1, target.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int onKarmaVoteRemove(User author, User target) { + log.debug("Removing karma vote of {} for {}", author, target); + try (var connection = dataSource.getConnection()) { + addKarma(target, -1); + + var statement = connection.prepareStatement("SELECT karma_points FROM users WHERE user_id = ?"); + statement.setLong(1, target.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getUserTokens(UserSnowflake user) { + log.debug("Querying karma tokens for user {}", user); + try (var connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT karma_tokens + FROM users + WHERE user_id = ? + """); + + statement.setLong(1, user.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getDefaultTokens(Guild guild) { + log.debug("Querying default karma tokens for guild {}", guild); + try (var connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT default_tokens + FROM karma_settings + WHERE guild_id = ? + """); + + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void setDefaultTokens(Guild guild, int value) { + log.debug("Setting default karma tokens for guild {}", guild); + try (var connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE karma_settings + SET default_tokens = ? + WHERE guild_id = ? + """); + + statement.setInt(1, value); + statement.setLong(2, guild.getIdLong()); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public List getValidEmojis(Guild guild) { + log.debug("Querying valid karma emojis for guild {}", guild); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT valid_emojis + FROM karma_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + var emojis = Arrays.asList((String[]) result.getArray(1).getArray()); + return emojis.stream().map(Emoji::fromUnicode).toList(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void createKarmaReward(String name, int threshold, int xp, Role role, String embed) { + log.debug("Creating new karma reward"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("INSERT INTO karma_rewards(name, threshold, xp, role_id, embed) VALUES (?, ?, ?, ?, CAST (? AS jsonb))"); + statement.setString(1, name); + statement.setInt(2, threshold); + statement.setInt(3, xp); + statement.setLong(4, role == null ? -1 : role.getIdLong()); + statement.setObject(5, embed); + + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public List getKarmaRewards() { + log.debug("Querying karma rewards"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM karma_rewards"); + var result = statement.executeQuery(); + List rewards = new ArrayList<>(); + while (result.next()) { + rewards.add(new KarmaReward( + result.getInt("reward_id"), + result.getString("name"), + result.getInt("threshold"), + result.getInt("xp"), + result.getLong("role_id"), + result.getString("embed") + ) + ); + } + return rewards; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void deleteKarmaReward(int rewardId) { + log.debug("Deleting karma reward with id {}", rewardId); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("DELETE FROM karma_rewards WHERE reward_id = ?"); + statement.setInt(1, rewardId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public record KarmaReward(int rewardId, String name, int threshold, int xp, long roleId, String embed) { + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaTokenTask.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaTokenTask.java new file mode 100644 index 0000000..b849307 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaTokenTask.java @@ -0,0 +1,15 @@ +package com.github.kaktushose.nplaybot.karma; + +import com.github.kaktushose.nplaybot.Bot; +import com.github.kaktushose.nplaybot.scheduler.ScheduledTask; + +import java.util.concurrent.TimeUnit; + +public class KarmaTokenTask { + + @ScheduledTask(startAtMidnight = true, period = 24, unit = TimeUnit.HOURS) + public void onResetKarmaTokens(Bot bot) { + bot.getDatabase().getKarmaService().resetTokens(); + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java index e3319a6..bfc43d3 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/BotPermissions.java @@ -6,9 +6,10 @@ public class BotPermissions { public static final String USER = "USER"; public static final String MODIFY_USER_BALANCE = "MODIFY_USER_BALANCE"; - public static final String MODIFY_RANK_SETTINGS = "MODIFY_RANK_SETTINGS"; + public static final String MANAGE_RANK_SETTINGS = "MANAGE_RANK_SETTINGS"; + public static final String MANAGE_KARMA_SETTINGS = "MANAGE_KARMA_SETTINGS"; public static final String MANAGE_EVENTS = "MANAGE_EVENTS"; - public static final String MODIFY_USER_PERMISSIONS = "MODIFY_USER_PERMISSIONS"; + public static final String MANAGE_USER_PERMISSIONS = "MANAGE_USER_PERMISSIONS"; public static final String BOT_ADMINISTRATOR = "BOT_ADMINISTRATOR"; public static final String BOT_OWNER = "BOT_OWNER"; @@ -16,9 +17,10 @@ public class BotPermissions { private static final Map permissionMapping = new LinkedHashMap<>() {{ put(USER, 1 << 0); put(MODIFY_USER_BALANCE, 1 << 1); - put(MODIFY_RANK_SETTINGS, 1 << 2); + put(MANAGE_RANK_SETTINGS, 1 << 2); + put(MANAGE_KARMA_SETTINGS, 1 << 6); put(MANAGE_EVENTS, 1 << 3); - put(MODIFY_USER_PERMISSIONS, 1 << 4); + put(MANAGE_USER_PERMISSIONS, 1 << 4); put(BOT_ADMINISTRATOR, 1 << 5); put(BOT_OWNER, 1 << 8); }}; diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java index 48f4b75..3ca2456 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java @@ -38,7 +38,7 @@ public void onPermissionsList(CommandEvent event, @Optional Member member) { } @SlashCommand(value = "permissions user edit", desc = "Bearbeitet die Berechtigungen von einem Nutzer. Hat keinen Einfluss auf die Rollen-Berechtigungen", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) - @Permissions(MODIFY_USER_PERMISSIONS) + @Permissions(MANAGE_USER_PERMISSIONS) public void onPermissionsUserEdit(CommandEvent event, Member member) { targetMember = member; @@ -64,7 +64,7 @@ public void onPermissionsUserEdit(CommandEvent event, Member member) { @StringSelectMenu(value = "Wähle eine oder mehrere Berechtigungen aus") @SelectOption(label = "dummy option", value = "dummy option") - @Permissions(MODIFY_USER_PERMISSIONS) + @Permissions(MANAGE_USER_PERMISSIONS) public void onPermissionsUserSelect(ComponentEvent event, List selection) { database.getPermissionsService().setUserPermissions(targetMember, BotPermissions.combine(selection.stream().map(Integer::valueOf).toList())); @@ -75,7 +75,7 @@ public void onPermissionsUserSelect(ComponentEvent event, List selection } @SlashCommand(value = "permissions role edit", desc = "Bearbeitet die Berechtigungen von einer Rolle", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) - @Permissions(MODIFY_USER_PERMISSIONS) + @Permissions(MANAGE_USER_PERMISSIONS) public void onPermissionsRoleEdit(CommandEvent event, Role role) { targetRole = role; @@ -102,7 +102,7 @@ public void onPermissionsRoleEdit(CommandEvent event, Role role) { @StringSelectMenu(value = "Wähle eine oder mehrere Berechtigungen aus") @SelectOption(label = "dummy option", value = "dummy option") - @Permissions(MODIFY_USER_PERMISSIONS) + @Permissions(MANAGE_USER_PERMISSIONS) public void onPermissionsRoleSelect(ComponentEvent event, List selection) { database.getPermissionsService().setRolePermissions(targetRole, BotPermissions.combine(selection.stream().map(Integer::valueOf).toList())); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 178d9e2..2585e37 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -87,7 +87,7 @@ public UserInfo getUserInfo(UserSnowflake user) { log.debug("Querying user info for user: {}", user); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT xp, rank_id, message_count, start_xp + SELECT xp, rank_id, message_count, start_xp, karma_points FROM users WHERE user_id = ? """ @@ -105,7 +105,8 @@ public UserInfo getUserInfo(UserSnowflake user) { currentRank, nextRank, result.getInt("message_count"), - result.getInt("xp") - result.getInt("start_xp") + result.getInt("xp") - result.getInt("start_xp"), + result.getInt("karma_points") ); } catch (SQLException e) { throw new RuntimeException(e); @@ -450,7 +451,7 @@ public Map getDailyRankInfos() { log.debug("Querying daily rank infos"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT user_id, xp, rank_id, message_count, start_xp + SELECT user_id, xp, rank_id, message_count, start_xp, karma_points FROM users WHERE daily_message = true """ @@ -466,7 +467,8 @@ public Map getDailyRankInfos() { currentRank, nextRank, result.getInt("message_count"), - result.getInt("xp") - result.getInt("start_xp") + result.getInt("xp") - result.getInt("start_xp"), + result.getInt("karma_points") )); } return users; diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java index 7bbc1c7..8e981f9 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java @@ -15,7 +15,7 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; @Interaction -@Permissions(BotPermissions.MODIFY_RANK_SETTINGS) +@Permissions(BotPermissions.MANAGE_RANK_SETTINGS) public class RankConfigCommands { @Inject diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java index 774022b..f0fc05f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java @@ -6,7 +6,7 @@ import java.util.Map; import java.util.Optional; -public record UserInfo(int currentXp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain) { +public record UserInfo(int currentXp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain, int karma) { public Map getEmbedValues(User user) { var result = new HashMap() {{ @@ -15,6 +15,7 @@ public Map getEmbedValues(User user) { put("avatarUrl", user.getEffectiveAvatarUrl()); put("currentRank", currentRank.name()); put("currentXp", currentXp); + put("karma", karma); put("xpGain", xpGain); put("messageCount", messageCount); }}; diff --git a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java index 70f2ddd..38883ac 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java +++ b/src/main/java/com/github/kaktushose/nplaybot/scheduler/TaskScheduler.java @@ -74,7 +74,7 @@ private void indexTasks() { private Runnable execute(Method method) { return () -> { try { - log.debug("Invoking task: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName()); + log.info("Invoking task: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName()); method.invoke(method.getDeclaringClass().getConstructors()[0].newInstance(), bot); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { log.error("Exception in scheduled task!", e); diff --git a/src/main/resources/db/migration/V3.0.0__setup_karma_system.sql b/src/main/resources/db/migration/V3.0.0__setup_karma_system.sql new file mode 100644 index 0000000..6e90ad0 --- /dev/null +++ b/src/main/resources/db/migration/V3.0.0__setup_karma_system.sql @@ -0,0 +1,18 @@ +ALTER TABLE users +ADD COLUMN karma_points INT NOT NULL DEFAULT 0, +ADD COLUMN karma_tokens INT NOT NULL DEFAULT 5; + +CREATE TABLE karma_settings( + guild_id BIGINT NOT NULL PRIMARY KEY, + VALID_EMOJIS VARCHAR[] NOT NULL, + default_tokens INT NOT NULL DEFAULT 5 +); + +CREATE TABLE karma_rewards ( + reward_id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL DEFAULT 0, + threshold INT NOT NULL, + xp INT NOT NULL DEFAULT 0, + role_id BIGINT NOT NULL DEFAULT 0, + embed JSONB NOT NULL +); From 933b50966d7d08c6ff6fe0019debd2d21b462d87 Mon Sep 17 00:00:00 2001 From: Kaktushose <42280757+Kaktushose@users.noreply.github.com> Date: Sat, 11 May 2024 14:34:57 +0200 Subject: [PATCH 53/66] refactor command names --- .../events/collect/CollectEventCommands.java | 6 +++--- .../events/collect/CollectRewardCommands.java | 4 ++-- .../nplaybot/events/contest/ContestCommands.java | 4 ++-- .../nplaybot/internal/MaintenanceCommands.java | 2 +- .../nplaybot/karma/KarmaConfigCommands.java | 10 +++++----- .../nplaybot/karma/KarmaRewardCommands.java | 4 ++-- .../nplaybot/rank/commands/ModifyXpCommands.java | 4 ++-- .../nplaybot/rank/commands/RankConfigCommands.java | 14 +++++++------- .../kaktushose/nplaybot/rank/model/UserInfo.java | 3 ++- 9 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java index 9c0e32d..109f7f9 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java @@ -22,7 +22,7 @@ public class CollectEventCommands { @Inject private Database database; - @SlashCommand(value = "collect event start", desc = "Startet ein Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "events collect-event start", desc = "Startet ein Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onCollectEventStart(CommandEvent event, @Param("Der Name des Events") String eventName, @Param("Der Name der Währung die gesammelt werden soll, z.B. \"Schneemänner\"") String currencyName, @@ -35,13 +35,13 @@ public void onCollectEventStart(CommandEvent event, event.reply(embedCache.getEmbed("collectEventStart").injectValue("name", eventName)); } - @SlashCommand(value = "collect event stop", desc = "Stoppt das aktuelle Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "events collect-event stop", desc = "Stoppt das aktuelle Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onCollectEventStop(CommandEvent event) { database.getCollectEventService().stopCollectEvent(event.getGuild()); event.reply(embedCache.getEmbed("collectEventStop")); } - @SlashCommand(value = "set collect-loot chance", desc = "Legt die Wahrscheinlichkeit für zufällige Collect-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "events set collect-loot-chance", desc = "Legt die Wahrscheinlichkeit für zufällige Collect-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetXpLootDropChance(CommandEvent event, @Param("Die Wahrscheinlichkeit in Prozent") @Min(1) @Max(100) double chance) { database.getCollectEventService().updateCollectLootChance(event.getGuild(), chance); event.reply(embedCache.getEmbed("collectLootChanceUpdate").injectValue("chance", chance)); diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java index c05b879..c04b0c8 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java @@ -40,7 +40,7 @@ public class CollectRewardCommands { private int threshold; private String embed; - @SlashCommand(value = "collect reward create", desc = "Erstellt eine Belohnung für das Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "events collect-reward create", desc = "Erstellt eine Belohnung für das Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onRewardCreate(CommandEvent event, @Param("Der interne Name dieser Belohnung") String name, @Param("Der Wert, ab wann die Belohnung vergeben werden soll") int threshold) { this.name = name; this.threshold = threshold; @@ -129,7 +129,7 @@ public void onCancel(ComponentEvent event) { event.removeComponents(); } - @SlashCommand(value = "collect reward delete", desc = "Löscht eine oder mehrere Belohnung(en) für das Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "events collect-reward delete", desc = "Löscht eine oder mehrere Belohnung(en) für das Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onRewardDelete(CommandEvent event) { var rewards = database.getCollectEventService().getCollectRewards(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java index b9f17dd..cdb9c6e 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java @@ -24,7 +24,7 @@ public class ContestCommands { @Inject private EmbedCache embedCache; - @SlashCommand(value = "contest event start", desc = "Startet ein Contest-Event", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @SlashCommand(value = "events contest-event start", desc = "Startet ein Contest-Event", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) public void onContestEventStart(CommandEvent event, @Param("Der Textkanal, in dem das Contest-Event stattfinden soll") TextChannel channel, @Param("Der Emoji, mit dem abgestimmt werden soll") String emoji) { @@ -32,7 +32,7 @@ public void onContestEventStart(CommandEvent event, event.reply(embedCache.getEmbed("contestEventStart").injectValue("channel", channel.getAsMention())); } - @SlashCommand(value = "contest event stop", desc = "Stoppt das aktuelle Contest-Event und zeigt die Gewinner an", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @SlashCommand(value = "events contest-event stop", desc = "Stoppt das aktuelle Contest-Event und zeigt die Gewinner an", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) public void onContestEventStop(CommandEvent event) { var result = database.getContestEventService().stopContestEvent(event.getGuild()); StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java b/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java index f321c07..bcaa9c9 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/internal/MaintenanceCommands.java @@ -16,7 +16,7 @@ public class MaintenanceCommands { @Inject private EmbedCache embedCache; - @SlashCommand(value = "reload embeds", desc = "Aktualisiert den EmbedCache", enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "maintenance reload embeds", desc = "Aktualisiert den EmbedCache", enabledFor = Permission.BAN_MEMBERS) public void onReload(CommandEvent event) { embedCache.loadEmbeds(); event.reply(embedCache.getEmbed("embedCacheReload")); diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java index cb1f071..f987487 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java @@ -21,7 +21,7 @@ public class KarmaConfigCommands { @Inject private EmbedCache embedCache; - @SlashCommand(value = "add karma", desc = "Fügt einem User Karma hinzu", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @SlashCommand(value = "balance add karma", desc = "Fügt einem User Karma hinzu", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) @Permissions(BotPermissions.MODIFY_USER_BALANCE) public void onAddKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE) @Max(Integer.MAX_VALUE) int amount) { database.getKarmaService().addKarma(target, amount); @@ -32,7 +32,7 @@ public void onAddKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE ); } - @SlashCommand(value = "set karma", desc = "Setzt die Karma Punkte von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @SlashCommand(value = "balance set karma", desc = "Setzt die Karma Punkte von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) @Permissions(BotPermissions.MODIFY_USER_BALANCE) public void onSetKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE) @Max(Integer.MAX_VALUE) int value) { database.getKarmaService().setKarma(target, value); @@ -43,21 +43,21 @@ public void onSetKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE ); } - @SlashCommand(value = "set default karma-tokens", desc = "Legt die tägliche Anzahl an Karma-Tokens für jeden Nutzer fest", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @SlashCommand(value = "karma-config set default-karma-tokens", desc = "Legt die tägliche Anzahl an Karma-Tokens für jeden Nutzer fest", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) @Permissions(BotPermissions.MANAGE_KARMA_SETTINGS) public void onSetKarmaTokens(CommandEvent event, @Min(1) @Max(Integer.MAX_VALUE) int value) { database.getKarmaService().setDefaultTokens(event.getGuild(), value); onGetKarmaConfig(event); } - @SlashCommand(value = "get karma config", desc = "Zeigt die Einstellungen für das Karma System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "karma-config display", desc = "Zeigt die Einstellungen für das Karma System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) @Permissions(BotPermissions.MANAGE_KARMA_SETTINGS) public void onGetKarmaConfig(CommandEvent event) { var emojis = database.getKarmaService().getValidEmojis(event.getGuild()); var builder = new StringBuilder(); emojis.forEach(it -> builder.append(it.getFormatted()).append(" ")); event.reply(embedCache.getEmbed("karmaConfig") - .injectValue("emojis", builder) + .injectValue("emojis", builder) .injectValue("tokens", database.getKarmaService().getDefaultTokens(event.getGuild())) ); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java index c418781..e9c068a 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java @@ -40,7 +40,7 @@ public class KarmaRewardCommands { private int threshold; private String embed; - @SlashCommand(value = "karma reward create", desc = "Erstellt eine Belohnung für das Karma System", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "karma-config reward create", desc = "Erstellt eine Belohnung für das Karma System", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onRewardCreate(CommandEvent event, @Param("Der interne Name dieser Belohnung") String name, @Param("Der Wert, ab wann die Belohnung vergeben werden soll") int threshold) { this.name = name; this.threshold = threshold; @@ -129,7 +129,7 @@ public void onCancel(ComponentEvent event) { event.removeComponents(); } - @SlashCommand(value = "karma reward delete", desc = "Löscht eine oder mehrere Belohnung(en) für das Karma System", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "karma-config reward delete", desc = "Löscht eine oder mehrere Belohnung(en) für das Karma System", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onRewardDelete(CommandEvent event) { var rewards = database.getKarmaService().getKarmaRewards(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java index 43fc817..aab1b2b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java @@ -29,7 +29,7 @@ public class ModifyXpCommands { @Inject private EmbedCache embedCache; - @SlashCommand(value = "add xp", desc = "Fügt einem User XP hinzu", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @SlashCommand(value = "balance add xp", desc = "Fügt einem User XP hinzu", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) public void onAddXp(CommandEvent event, Member target, @Min(1) @Max(Integer.MAX_VALUE) int amount) { var result = database.getRankService().addXp(target, amount); @@ -41,7 +41,7 @@ public void onAddXp(CommandEvent event, Member target, @Min(1) @Max(Integer.MAX_ checkRankUpdate(result, target, event.getGuild()); } - @SlashCommand(value = "set xp", desc = "Setzt die XP von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + @SlashCommand(value = "balance set xp", desc = "Setzt die XP von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) public void onSetXp(CommandEvent event, Member target, @Min(0) @Max(Integer.MAX_VALUE) int value) { var result = database.getRankService().setXp(target, value); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java index 8e981f9..3195cdb 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java @@ -23,30 +23,30 @@ public class RankConfigCommands { @Inject private EmbedCache embedCache; - @SlashCommand(value = "get rank config", desc = "Zeigt die Einstellungen für das Rank System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "rank-config show", desc = "Zeigt die Einstellungen für das Rank System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onGetRankConfig(CommandEvent event) { event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); } - @SlashCommand(value = "set cooldown", desc = "Legt den Cooldown für gewertete Nachrichten fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "rank-config set cooldown", desc = "Legt den Cooldown für gewertete Nachrichten fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetCooldown(CommandEvent event, @Param("Die Dauer in Millisekunden") @Min(0) @Max(Integer.MAX_VALUE) int cooldown) { database.getRankService().updateCooldown(event.getGuild(), cooldown); event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); } - @SlashCommand(value = "set message length", desc = "Legt die Mindestlänge für gewertete Nachrichten fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "rank-config set message-length", desc = "Legt die Mindestlänge für gewertete Nachrichten fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetMinMessageLength(CommandEvent event, @Param("Die Mindestanzahl an Buchstaben pro Nachricht") @Min(0) @Max(Integer.MAX_VALUE) int length) { database.getRankService().updateMinMessageLength(event.getGuild(), length); event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); } - @SlashCommand(value = "set xp-loot chance", desc = "Legt die Wahrscheinlichkeit für zufällige XP-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "rank-config set xp-loot-chance", desc = "Legt die Wahrscheinlichkeit für zufällige XP-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetXpLootDropChance(CommandEvent event, @Param("Die Wahrscheinlichkeit in Prozent") @Min(1) @Max(100) double chance) { database.getRankService().updateXpLootChance(event.getGuild(), chance); event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); } - @SlashCommand(value = "valid channels list", desc = "Zeigt die Textkanäle an, in denen Nachrichten gewertet werden", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "rank-config valid-channels list", desc = "Zeigt die Textkanäle an, in denen Nachrichten gewertet werden", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onValidChannelsList(CommandEvent event) { var channels = database.getRankService().getValidChannels(event.getGuild()); StringBuilder result = new StringBuilder(); @@ -54,7 +54,7 @@ public void onValidChannelsList(CommandEvent event) { event.reply(embedCache.getEmbed("validChannels").injectValue("channels", result)); } - @SlashCommand(value = "valid channels add", desc = "Fügt einen Textkanal zu der Liste der gewerteten Kanäle hinzu", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "rank-config valid-channels add", desc = "Fügt einen Textkanal zu der Liste der gewerteten Kanäle hinzu", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onValidChannelsAdd(CommandEvent event, @Param("Der Kanal der gewertet werden soll") TextChannel channel) { var channels = database.getRankService().getValidChannels(event.getGuild()); channels.add(channel.getIdLong()); @@ -64,7 +64,7 @@ public void onValidChannelsAdd(CommandEvent event, @Param("Der Kanal der gewerte event.reply(embedCache.getEmbed("validChannels").injectValue("channels", result)); } - @SlashCommand(value = "valid channels remove", desc = "Entfernt einen Textkanal von der Liste der gewerteten Kanäle", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "rank-config valid-channels remove", desc = "Entfernt einen Textkanal von der Liste der gewerteten Kanäle", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onValidChannelsRemove(CommandEvent event, @Param("Der Kanal der nicht mehr gewertet werden soll") TextChannel channel) { var channels = database.getRankService().getValidChannels(event.getGuild()); channels.remove(channel.getIdLong()); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java index f0fc05f..b60ba3f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java @@ -6,7 +6,8 @@ import java.util.Map; import java.util.Optional; -public record UserInfo(int currentXp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain, int karma) { +public record UserInfo(int currentXp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain, + int karma) { public Map getEmbedValues(User user) { var result = new HashMap() {{ From c979034cfee5f0f9ca90ca5870f5669b3d692906 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Mon, 27 May 2024 17:37:22 +0200 Subject: [PATCH 54/66] implement starboard feature --- .../com/github/kaktushose/nplaybot/Bot.java | 4 +- .../github/kaktushose/nplaybot/Database.java | 7 + .../nplaybot/karma/KarmaListener.java | 2 +- .../nplaybot/starboard/StarboardListener.java | 189 ++++++++++++++++++ .../nplaybot/starboard/StarboardService.java | 170 ++++++++++++++++ .../db/migration/V3.1.0__add_starboard.sql | 12 ++ 6 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardService.java create mode 100644 src/main/resources/db/migration/V3.1.0__add_starboard.sql diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 1885404..2b23008 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -10,6 +10,7 @@ import com.github.kaktushose.nplaybot.rank.JoinLeaveListener; import com.github.kaktushose.nplaybot.rank.RankListener; import com.github.kaktushose.nplaybot.scheduler.TaskScheduler; +import com.github.kaktushose.nplaybot.starboard.StarboardListener; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.OnlineStatus; @@ -51,7 +52,8 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { new JoinLeaveListener(database.getRankService()), new ContestListener(database.getContestEventService()), new CollectEventListener(database, embedCache), - new KarmaListener(database, embedCache) + new KarmaListener(database, embedCache), + new StarboardListener(database, embedCache) ) .build().awaitReady(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 702a7d7..7f82d04 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -6,6 +6,7 @@ import com.github.kaktushose.nplaybot.permissions.PermissionsService; import com.github.kaktushose.nplaybot.rank.RankService; import com.github.kaktushose.nplaybot.settings.SettingsService; +import com.github.kaktushose.nplaybot.starboard.StarboardService; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -18,6 +19,7 @@ public class Database { private final CollectEventService collectEventService; private final PermissionsService permissionsService; private final KarmaService karmaService; + private final StarboardService starboardService; public Database() { var config = new HikariConfig(); @@ -35,6 +37,7 @@ public Database() { collectEventService = new CollectEventService(dataSource); permissionsService = new PermissionsService(dataSource); karmaService = new KarmaService(dataSource); + starboardService = new StarboardService(dataSource); } public void closeDataSource() { @@ -64,4 +67,8 @@ public PermissionsService getPermissionsService() { public KarmaService getKarmaService() { return karmaService; } + + public StarboardService getStarboardService() { + return starboardService; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java index d04e1ad..956b249 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java @@ -69,7 +69,7 @@ public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { }); } - private void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild guild) { + public void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild guild) { var rewards = karmaService.getKarmaRewards(); var optional = rewards.stream() .filter(it -> it.threshold() > oldKarma) diff --git a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java new file mode 100644 index 0000000..3287299 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java @@ -0,0 +1,189 @@ +package com.github.kaktushose.nplaybot.starboard; + +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.karma.KarmaListener; +import com.github.kaktushose.nplaybot.karma.KarmaService; +import com.github.kaktushose.nplaybot.rank.RankService; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.events.message.GenericMessageEvent; +import net.dv8tion.jda.api.events.message.MessageDeleteEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveAllEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEmojiEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import net.dv8tion.jda.api.utils.messages.MessageEditData; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StarboardListener extends ListenerAdapter { + + private final StarboardService starboardService; + private final KarmaService karmaService; + private final RankService rankService; + private final KarmaListener karmaListener; + + public StarboardListener(Database database, EmbedCache embedCache) { + this.starboardService = database.getStarboardService(); + this.karmaService = database.getKarmaService(); + this.rankService = database.getRankService(); + this.karmaListener = new KarmaListener(database, embedCache); + } + + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + if (event.getUser().isBot()) { + return; + } + + if (!event.getReaction().getEmoji().equals(Emoji.fromUnicode("⭐"))) { + return; + } + + AtomicInteger count = new AtomicInteger(0); + event.getChannel().retrieveMessageById(event.getMessageIdLong()).queue(message -> { + Optional.ofNullable(message.getReaction(Emoji.fromFormatted("⭐"))).ifPresent( + it -> count.set(it.getCount()) + ); + + long messageId = event.getMessageIdLong(); + if (!starboardService.entryExists(messageId)) { + starboardService.createEntry(messageId); + } + if (count.get() < starboardService.getThreshold(event.getGuild())) { + return; + } + if (!starboardService.isRewarded(messageId)) { + starboardService.setRewarded(messageId); + + var oldKarma = rankService.getUserInfo(event.getUser()).karma(); + karmaService.addKarma(UserSnowflake.fromId(event.getMessageAuthorIdLong()), starboardService.getKarmaReward(event.getGuild())); + var newKarma = rankService.getUserInfo(event.getUser()).karma(); + karmaListener.onKarmaIncrease(oldKarma, newKarma, event.getMember(), event.getGuild()); + } + + var starboardChannel = event.getGuild().getTextChannelById(starboardService.getStarboardChannelId(event.getGuild())); + if (starboardService.isPosted(messageId)) { + starboardChannel.retrieveMessageById(starboardService.getPostId(messageId)) + .flatMap(msg -> msg.editMessage(MessageEditData.fromCreateData(buildMessage(message, count.get())))) + .queue(); + return; + } + + starboardChannel.sendMessage(buildMessage(message, count.get())).queue(msg -> starboardService.setPostId(messageId, msg.getIdLong())); + }); + } + + // single reaction removed + @Override + public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { + if (event.getUser().isBot()) { + return; + } + + if (!event.getReaction().getEmoji().equals(Emoji.fromUnicode("⭐"))) { + return; + } + + AtomicInteger count = new AtomicInteger(0); + var starboardChannel = event.getGuild().getTextChannelById(starboardService.getStarboardChannelId(event.getGuild())); + event.getChannel().retrieveMessageById(event.getMessageIdLong()).queue(message -> { + Optional.ofNullable(message.getReaction(Emoji.fromFormatted("⭐"))).ifPresent( + it -> count.set(it.getCount()) + ); + + long messageId = event.getMessageIdLong(); + + if (count.get() == 0) { + if (!starboardService.entryExists(messageId)) { + return; + } + if (!starboardService.isPosted(messageId)) { + return; + } + starboardChannel.retrieveMessageById(starboardService.getPostId(messageId)).flatMap(Message::delete).queue(); + return; + } + + if (!starboardService.entryExists(messageId)) { + starboardService.createEntry(messageId); + } + + if (count.get() < starboardService.getThreshold(event.getGuild()) && starboardService.isPosted(messageId)) { + starboardChannel.retrieveMessageById(starboardService.getPostId(messageId)).flatMap(Message::delete).queue(); + starboardService.setPostId(messageId, -1); + return; + } + + starboardChannel.sendMessage(buildMessage(message, count.get())).queue(msg -> starboardService.setPostId(messageId, msg.getIdLong())); + }); + } + + @Override + public void onMessageReactionRemoveEmoji(@NotNull MessageReactionRemoveEmojiEvent event) { + if (!event.getReaction().getEmoji().equals(Emoji.fromUnicode("⭐"))) { + return; + } + removeEntry(event); + } + + @Override + public void onMessageDelete(@NotNull MessageDeleteEvent event) { + if (starboardService.entryExists(event.getMessageIdLong())) { + removeEntry(event); + } + } + + @Override + public void onMessageReactionRemoveAll(@NotNull MessageReactionRemoveAllEvent event) { + removeEntry(event); + } + + private MessageCreateData buildMessage(Message message, int count) { + EmbedBuilder embed = new EmbedBuilder() + .setAuthor(message.getAuthor().getEffectiveName(), null, message.getAuthor().getEffectiveAvatarUrl()) + .setTimestamp(message.getTimeCreated()); + + String content = message.getContentRaw(); + + if (!message.getAttachments().isEmpty()) { + embed.setImage(message.getAttachments().get(0).getUrl()); + } else { + String regex = "(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + embed.setImage(matcher.group(0)); + if (!content.isEmpty()) { + content = content.replace(matcher.group(0), ""); + } + } + } + + embed.setDescription(content) + .setColor(16766720) + .addField("**Source**", String.format("[Jump!](%s)", message.getJumpUrl()), false); + + return new MessageCreateBuilder() + .setContent(String.format(":star2: **%d** %s", count, message.getChannel().getAsMention())) + .setEmbeds(embed.build()).build(); + } + + private void removeEntry(GenericMessageEvent event) { + starboardService.setPostId(event.getMessageIdLong(), -1); + event.getGuild().getTextChannelById(starboardService.getStarboardChannelId(event.getGuild())) + .retrieveMessageById(starboardService.getPostId(event.getMessageIdLong())) + .flatMap(Message::delete) + .queue(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardService.java b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardService.java new file mode 100644 index 0000000..aa1bb8e --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardService.java @@ -0,0 +1,170 @@ +package com.github.kaktushose.nplaybot.starboard; + +import net.dv8tion.jda.api.entities.Guild; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public class StarboardService { + + private static final Logger log = LoggerFactory.getLogger(StarboardService.class); + private final DataSource dataSource; + + public StarboardService(DataSource dataSource) { + this.dataSource = dataSource; + } + + public boolean entryExists(long messageId) { + log.debug("Checking if starboard entry exists"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT EXISTS(SELECT 1 FROM starboard_entries WHERE message_id = ?)"); + statement.setLong(1, messageId); + var result = statement.executeQuery(); + result.next(); + return result.getBoolean(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void createEntry(long messageId) { + log.debug("Creating starboard entry"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("INSERT INTO starboard_entries VALUES(?)"); + statement.setLong(1, messageId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public boolean isRewarded(long messageId) { + log.debug("Querying starboard votes"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT is_rewarded + FROM starboard_entries + WHERE message_id = ? + """ + ); + statement.setLong(1, messageId); + var result = statement.executeQuery(); + result.next(); + return result.getBoolean(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void setRewarded(long messageId) { + log.debug("Querying starboard votes"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE starboard_entries + SET is_rewarded = TRUE + WHERE message_id = ? + """ + ); + statement.setLong(1, messageId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public long getPostId(long messageId) { + log.debug("Querying starboard post id"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT post_id + FROM starboard_entries + WHERE message_id = ? + """ + ); + statement.setLong(1, messageId); + var result = statement.executeQuery(); + result.next(); + return result.getLong(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void setPostId(long messageId, long postId) { + log.debug("Setting starboard post id"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + UPDATE starboard_entries + SET post_id = ? + WHERE message_id = ? + """ + ); + statement.setLong(1, postId); + statement.setLong(2, messageId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getThreshold(Guild guild) { + log.debug("Querying starboard threshold"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT threshold + FROM starboard_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getKarmaReward(Guild guild) { + log.debug("Querying starboard karma reward"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT karma_reward + FROM starboard_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public long getStarboardChannelId(Guild guild) { + log.debug("Querying starboard channel id"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT channel_id + FROM starboard_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + var result = statement.executeQuery(); + result.next(); + return result.getLong(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public boolean isPosted(long messageId) { + return getPostId(messageId) > 0; + } +} diff --git a/src/main/resources/db/migration/V3.1.0__add_starboard.sql b/src/main/resources/db/migration/V3.1.0__add_starboard.sql new file mode 100644 index 0000000..abda1b1 --- /dev/null +++ b/src/main/resources/db/migration/V3.1.0__add_starboard.sql @@ -0,0 +1,12 @@ +CREATE TABLE starboard_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + channel_id BIGINT NOT NULL DEFAULT 0, + threshold INT NOT NULL DEFAULT 5, + karma_reward INT NOT NULL DEFAULT 5 +); + +CREATE TABLE starboard_entries ( + message_id BIGINT PRIMARY KEY NOT NULL, + post_id BIGINT NOT NULL DEFAULT -1, + is_rewarded BOOLEAN NOT NULL DEFAULT false +); From ed87a6b9cad7e256c1cd998012a2e9e2d2ee6cce Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Sun, 2 Jun 2024 14:53:19 +0200 Subject: [PATCH 55/66] bump jda-commands version --- pom.xml | 2 +- .../nplaybot/permissions/PermissionCommands.java | 14 ++++++-------- .../nplaybot/rank/commands/RankConfigCommands.java | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 9797aa4..22cf8a9 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ com.github.Kaktushose jda-commands - 649440b3e8 + 18635417f1 com.zaxxer diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java index 3ca2456..580bb5b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionCommands.java @@ -46,10 +46,9 @@ public void onPermissionsUserEdit(CommandEvent event, Member member) { permissionsMap.put(NONE, 0); permissionsMap.remove(BOT_OWNER); - var menu = ((net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu) - event.getJdaCommands().getSelectMenu( - "PermissionCommands.onPermissionsUserSelect", - event.getContext().getRuntime().getRuntimeId()) + var menu = event.getSelectMenu( + "PermissionCommands.onPermissionsUserSelect", + net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu.class ).createCopy(); menu.getOptions().clear(); menu.setMaxValues(SelectMenu.OPTIONS_MAX_AMOUNT); @@ -84,10 +83,9 @@ public void onPermissionsRoleEdit(CommandEvent event, Role role) { permissionsMap.remove(USER); permissionsMap.remove(BOT_OWNER); - var menu = ((net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu) - event.getJdaCommands().getSelectMenu( - "PermissionCommands.onPermissionsRoleSelect", - event.getContext().getRuntime().getRuntimeId()) + var menu = event.getSelectMenu( + "PermissionCommands.onPermissionsRoleSelect", + net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu.class ).createCopy(); menu.getOptions().clear(); menu.setMaxValues(SelectMenu.OPTIONS_MAX_AMOUNT); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java index 3195cdb..f22e6ab 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java @@ -23,7 +23,7 @@ public class RankConfigCommands { @Inject private EmbedCache embedCache; - @SlashCommand(value = "rank-config show", desc = "Zeigt die Einstellungen für das Rank System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) + @SlashCommand(value = "rank-config display", desc = "Zeigt die Einstellungen für das Rank System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onGetRankConfig(CommandEvent event) { event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); } From b36c5810daf6a1e46b91361f72afcbf8594df318 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Sun, 2 Jun 2024 17:43:05 +0200 Subject: [PATCH 56/66] first pass on items --- embeds.json | 26 ++- .../github/kaktushose/nplaybot/Database.java | 7 + .../events/collect/CollectRewardCommands.java | 5 + .../nplaybot/items/AddItemCommand.java | 59 +++++++ .../nplaybot/items/ItemService.java | 157 ++++++++++++++++++ .../nplaybot/items/RemoveItemCommand.java | 57 +++++++ .../nplaybot/karma/KarmaRewardCommands.java | 5 + .../rank/commands/RankInfoCommand.java | 13 ++ .../nplaybot/starboard/StarboardListener.java | 8 +- .../db/migration/V3.2.0__setup_items.sql | 54 ++++++ 10 files changed, 388 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/items/AddItemCommand.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java create mode 100644 src/main/java/com/github/kaktushose/nplaybot/items/RemoveItemCommand.java create mode 100644 src/main/resources/db/migration/V3.2.0__setup_items.sql diff --git a/embeds.json b/embeds.json index 920ac65..dcbe04a 100644 --- a/embeds.json +++ b/embeds.json @@ -137,7 +137,6 @@ "title": "Rank Konfiguration", "description": "Cooldown: `{cooldown}ms`\nNachrichtenlänge: `{minLength} Buchstaben`\nXP Loot Wahrscheinlichkeit: `{lootChance}%`\n", "color": "#67c94f" - }, "validChannels": { "title": "Gewertete Textkanäle", @@ -256,5 +255,30 @@ "title": "Achtung", "description": "Lieber {user},\ndu hast in letzter Zeit zu viel Karma verloren und somit wurden dir deine Belohnungen wieder abgezogen", "color": "#ffc926" + }, + "itemRemoveSelect": { + "title": "Items entfernen", + "description": "Wähle eine oder mehrere Items aus, die du dem Nutzer entfernen möchtest", + "color": "#67c94f" + }, + "itemDelete": { + "title": "Erfolg", + "description": "Die Items wurden dem Nutzer entfernt", + "color": "#67c94f" + }, + "noOptions": { + "title": "Fehler", + "description": "Es existieren keine {type} die ausgewählt werden könnten", + "color": "#ffc926" + }, + "itemAddSelect": { + "title": "Items hinzufügen", + "description": "Wähle eine oder mehrere Items aus, die du dem Nutzer hinzufügen möchtest", + "color": "#67c94f" + }, + "itemAdd": { + "title": "Erfolg", + "description": "Die Items wurden dem Nutzer hinzugefügt", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 7f82d04..ea30c7b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -2,6 +2,7 @@ import com.github.kaktushose.nplaybot.events.collect.CollectEventService; import com.github.kaktushose.nplaybot.events.contest.ContestEventService; +import com.github.kaktushose.nplaybot.items.ItemService; import com.github.kaktushose.nplaybot.karma.KarmaService; import com.github.kaktushose.nplaybot.permissions.PermissionsService; import com.github.kaktushose.nplaybot.rank.RankService; @@ -20,6 +21,7 @@ public class Database { private final PermissionsService permissionsService; private final KarmaService karmaService; private final StarboardService starboardService; + private final ItemService itemService; public Database() { var config = new HikariConfig(); @@ -38,6 +40,7 @@ public Database() { permissionsService = new PermissionsService(dataSource); karmaService = new KarmaService(dataSource); starboardService = new StarboardService(dataSource); + itemService = new ItemService(dataSource); } public void closeDataSource() { @@ -71,4 +74,8 @@ public KarmaService getKarmaService() { public StarboardService getStarboardService() { return starboardService; } + + public ItemService getItemService() { + return itemService; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java index c04b0c8..a4ea983 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectRewardCommands.java @@ -133,6 +133,11 @@ public void onCancel(ComponentEvent event) { public void onRewardDelete(CommandEvent event) { var rewards = database.getCollectEventService().getCollectRewards(); + if (rewards.isEmpty()) { + event.reply(embedCache.getEmbed("noOptions").injectValue("type", "Belohnungen")); + return; + } + var menu = ((net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu) event.getJdaCommands().getSelectMenu("CollectRewardCommands.onRewardDeleteSelect")).createCopy(); menu.getOptions().clear(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/AddItemCommand.java b/src/main/java/com/github/kaktushose/nplaybot/items/AddItemCommand.java new file mode 100644 index 0000000..79dbd78 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/items/AddItemCommand.java @@ -0,0 +1,59 @@ +package com.github.kaktushose.nplaybot.items; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.*; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.jda.commands.dispatching.interactions.components.ComponentEvent; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; + +import java.util.List; + +@Interaction +@Permissions(BotPermissions.MODIFY_USER_BALANCE) +public class AddItemCommand { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + private Member target; + + @SlashCommand(value = "balance item add", desc = "Fügt einem Nutzer ein Item hinzu", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + public void onItemAdd(CommandEvent event, Member target) { + var items = database.getItemService().getAllItems(); + + if (items.isEmpty()) { + event.reply(embedCache.getEmbed("noOptions").injectValue("type", "Items")); + return; + } + + var transactions = database.getItemService().getTransactions(target).stream().map(ItemService.Transaction::typeId).toList(); + items = items.stream().filter(it -> !transactions.contains(it.typeId())).toList(); + + this.target = target; + + var menu = event.getSelectMenu( + "AddItemCommand.onItemAddSelect", + net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu.class + ).createCopy(); + + menu.getOptions().clear(); + menu.setMaxValues(1); + + items.forEach(it -> menu.addOption(it.name(), String.valueOf(it.itemId()))); + event.getReplyContext().getBuilder().addActionRow(menu.build()); + event.reply(embedCache.getEmbed("itemAddSelect")); + } + + @StringSelectMenu("Wähle ein Item aus") + @SelectOption(label = "dummy option", value = "dummy option") + public void onItemAddSelect(ComponentEvent event, List selection) { + selection.forEach(id -> database.getItemService().createTransaction(target, Integer.parseInt(id), event.getGuild())); + event.reply(embedCache.getEmbed("itemAdd")); + event.removeComponents(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java new file mode 100644 index 0000000..d517e01 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java @@ -0,0 +1,157 @@ +package com.github.kaktushose.nplaybot.items; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.UserSnowflake; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class ItemService { + + private final DataSource dataSource; + + public ItemService(DataSource dataSource) { + this.dataSource = dataSource; + } + + public List getAllItems() { + try (Connection connection = dataSource.getConnection()) { + var items = new ArrayList(); + + var statement = connection.prepareStatement(""" + SELECT item_id, type_id, items.name, duration, role_id + FROM items + JOIN item_types ON item_types.base_type_id = items.type_id + """ + ); + var result = statement.executeQuery(); + while (result.next()) { + items.add(new Item( + result.getInt("item_id"), + result.getInt("type_id"), + result.getString("name"), + result.getLong("duration"), + result.getLong("role_id") + )); + } + + return items; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public Item getItem(int itemId) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT item_id, type_id, items.name, duration, role_id + FROM items + JOIN item_types ON item_types.base_type_id = items.type_id + WHERE item_id = ? + """ + ); + statement.setLong(1, itemId); + var result = statement.executeQuery(); + result.next(); + return new Item( + result.getInt("item_id"), + result.getInt("type_id"), + result.getString("name"), + result.getLong("duration"), + result.getLong("role_id") + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public List getTransactions(UserSnowflake user) { + try (Connection connection = dataSource.getConnection()) { + var transactions = new ArrayList(); + + var statement = connection.prepareStatement(""" + SELECT transaction_id, transactions.item_id, type_id, items.name, duration, expires_at, role_id + FROM transactions + JOIN items ON transactions.item_id = items.item_id + JOIN item_types ON item_types.base_type_id = items.type_id + WHERE user_id = ? + """ + ); + statement.setLong(1, user.getIdLong()); + var result = statement.executeQuery(); + while (result.next()) { + transactions.add(new Transaction( + result.getInt("transaction_id"), + result.getInt("item_id"), + result.getInt("type_id"), + result.getString("name"), + result.getLong("duration"), + result.getLong("expires_at"), + result.getLong("role_id") + )); + } + + return transactions; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void createTransaction(UserSnowflake user, int itemId, Guild guild) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("INSERT INTO transactions (\"user_id\", \"item_id\", \"expires_at\") VALUES(?, ?, ?)"); + statement.setLong(1, user.getIdLong()); + statement.setInt(2, itemId); + var item = getItem(itemId); + statement.setLong(3, System.currentTimeMillis() + item.duration); + if (item.roleId > 0) { + guild.addRoleToMember(user, guild.getRoleById(item.roleId())).queue(); + } + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void deleteTransaction(UserSnowflake user, int transactionId, Guild guild) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT item_id FROM transactions WHERE transaction_id = ?"); + statement.setInt(1, transactionId); + var result = statement.executeQuery(); + result.next(); + var item = getItem(result.getInt(1)); + if (item.roleId > 0) { + guild.removeRoleFromMember(user, guild.getRoleById(item.roleId())).queue(); + } + + statement = connection.prepareStatement("DELETE FROM transactions WHERE transaction_id = ?"); + statement.setLong(1, transactionId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public String getTypeEmoji(int typeId) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT emoji FROM item_types where base_type_id = ?"); + statement.setInt(1, typeId); + var result = statement.executeQuery(); + result.next(); + return result.getString(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public record Item(int itemId, int typeId, String name, long duration, long roleId) { + } + + public record Transaction(int transactionId, int itemId, int typeId, String name, long duration, long expiresAt, + long roleId) { + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/RemoveItemCommand.java b/src/main/java/com/github/kaktushose/nplaybot/items/RemoveItemCommand.java new file mode 100644 index 0000000..8b4b67e --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/items/RemoveItemCommand.java @@ -0,0 +1,57 @@ +package com.github.kaktushose.nplaybot.items; + +import com.github.kaktushose.jda.commands.annotations.Inject; +import com.github.kaktushose.jda.commands.annotations.interactions.*; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; +import com.github.kaktushose.jda.commands.dispatching.interactions.components.ComponentEvent; +import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.permissions.BotPermissions; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; + +import java.util.List; + +@Interaction +@Permissions(BotPermissions.MODIFY_USER_BALANCE) +public class RemoveItemCommand { + + @Inject + private Database database; + @Inject + private EmbedCache embedCache; + private Member target; + + @SlashCommand(value = "balance item remove", desc = "Entfernt einem Nutzer ein oder mehrere Items", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) + public void onItemRemove(CommandEvent event, Member target) { + var transactions = database.getItemService().getTransactions(target); + + if (transactions.isEmpty()) { + event.reply(embedCache.getEmbed("noOptions").injectValue("type", "Items")); + return; + } + + this.target = target; + + var menu = event.getSelectMenu( + "RemoveItemCommand.onItemRemoveSelect", + net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu.class + ).createCopy(); + + menu.getOptions().clear(); + menu.setMaxValues(SelectMenu.OPTIONS_MAX_AMOUNT); + + transactions.forEach(it -> menu.addOption(it.name(), String.valueOf(it.transactionId()))); + event.getReplyContext().getBuilder().addActionRow(menu.build()); + event.reply(embedCache.getEmbed("itemRemoveSelect")); + } + + @StringSelectMenu("Wähle ein oder mehrere Items aus") + @SelectOption(label = "dummy option", value = "dummy option") + public void onItemRemoveSelect(ComponentEvent event, List selection) { + selection.forEach(id -> database.getItemService().deleteTransaction(target, Integer.parseInt(id), event.getGuild())); + event.reply(embedCache.getEmbed("itemDelete")); + event.removeComponents(); + } +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java index e9c068a..3f97c48 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaRewardCommands.java @@ -133,6 +133,11 @@ public void onCancel(ComponentEvent event) { public void onRewardDelete(CommandEvent event) { var rewards = database.getKarmaService().getKarmaRewards(); + if (rewards.isEmpty()) { + event.reply(embedCache.getEmbed("noOptions").injectValue("type", "Belohnungen")); + return; + } + var menu = ((net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu) event.getJdaCommands().getSelectMenu("KarmaRewardCommands.onRewardDeleteSelect")).createCopy(); menu.getOptions().clear(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java index 7a670c1..3a7b119 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -44,6 +44,19 @@ private void sendReply(UserInfo userInfo, User user, CommandEvent event) { var points = database.getCollectEventService().getCollectPoints(user); embed.addField(currency.name(), String.format("%s %d", currency.emoji(), points), false); } + + var transactions = database.getItemService().getTransactions(user); + if (!transactions.isEmpty()) { + var items = new StringBuilder(); + transactions.forEach(it -> + items.append(database.getItemService().getTypeEmoji(it.typeId())) + .append(" ") + .append(it.name()) + .append("\n") + ); + embed.addField("Items", items.toString(), false); + } + event.reply(embed); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java index 3287299..0ace81b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java @@ -134,7 +134,9 @@ public void onMessageReactionRemoveEmoji(@NotNull MessageReactionRemoveEmojiEven if (!event.getReaction().getEmoji().equals(Emoji.fromUnicode("⭐"))) { return; } - removeEntry(event); + if (starboardService.entryExists(event.getMessageIdLong())) { + removeEntry(event); + } } @Override @@ -146,7 +148,9 @@ public void onMessageDelete(@NotNull MessageDeleteEvent event) { @Override public void onMessageReactionRemoveAll(@NotNull MessageReactionRemoveAllEvent event) { - removeEntry(event); + if (starboardService.entryExists(event.getMessageIdLong())) { + removeEntry(event); + } } private MessageCreateData buildMessage(Message message, int count) { diff --git a/src/main/resources/db/migration/V3.2.0__setup_items.sql b/src/main/resources/db/migration/V3.2.0__setup_items.sql new file mode 100644 index 0000000..6fd5e78 --- /dev/null +++ b/src/main/resources/db/migration/V3.2.0__setup_items.sql @@ -0,0 +1,54 @@ +CREATE TABLE item_types( + base_type_id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + emoji VARCHAR NOT NULL, + role_id BIGINT NOT NULL +); + +CREATE TABLE items( + item_id SERIAL PRIMARY KEY, + type_id SERIAL NOT NULL REFERENCES item_types(base_type_id), + name VARCHAR NOT NULL, + duration BIGINT +); + +CREATE TABLE transactions( + transaction_id SERIAL PRIMARY KEY, + user_id BIGINT NOT NULL, + item_id SERIAL NOT NULL REFERENCES items(item_id), + expires_at BIGINT NOT NULL +); + +ALTER TABLE users ADD COLUMN last_karma INT NOT NULL DEFAULT 0; + +INSERT INTO item_types VALUES (1, 'XP-Booster', ':moneybag:', -1); + +INSERT INTO item_types VALUES (2, 'Premium', ':star:', -1); + +CREATE OR REPLACE FUNCTION add_random_xp(id BIGINT) +RETURNS TABLE (rank_changed BOOLEAN, current_rank int, next_rank INT, current_xp INT) AS +$$ +DECLARE + xp INT; + bonus_xp INT; + karma INT; +BEGIN + SELECT get_random_xp INTO xp FROM get_random_xp(); + SELECT users.karma_points INTO karma FROM users WHERE users.user_id = id; + bonus_xp := 0; + IF karma < 0 THEN + bonus_xp := -1; + ELSIF karma >= 300 THEN + bonus_xp := 3; + ELSIF karma >= 200 THEN + bonus_xp := 2; + ELSIF karma >= 100 THEN + bonus_xp := 1; + END IF; + IF (SELECT EXISTS(SELECT 1 FROM transactions JOIN items ON transactions.item_id = items.item_id WHERE transactions.user_id=id AND type_id = 1)) THEN + bonus_xp := bonus_xp + 2; + END IF; + SELECT INTO rank_changed, current_rank, next_rank, current_xp * FROM add_xp(id, xp + bonus_xp); + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; From d8b1296f4247197c4539c8835b09d8b5f31f9b08 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Sun, 30 Jun 2024 16:58:48 +0200 Subject: [PATCH 57/66] implement play activity role --- embeds.json | 17 ++- .../com/github/kaktushose/nplaybot/Bot.java | 8 ++ .../github/kaktushose/nplaybot/Database.java | 6 +- .../events/collect/CollectEventListener.java | 4 +- .../nplaybot/items/ItemExpirationTask.java | 68 +++++++++++ .../nplaybot/items/ItemService.java | 81 ++++++++++++- .../nplaybot/karma/KarmaConfigCommands.java | 16 +++ .../nplaybot/karma/KarmaListener.java | 71 +---------- .../nplaybot/karma/KarmaService.java | 114 +++++++++++++++++- .../nplaybot/rank/RankListener.java | 8 +- .../kaktushose/nplaybot/rank/RankService.java | 57 +++++++-- .../rank/commands/ModifyXpCommands.java | 24 +--- .../rank/commands/RankInfoCommand.java | 6 +- .../nplaybot/rank/model/RankInfo.java | 2 +- .../nplaybot/rank/model/UserInfo.java | 2 +- .../nplaybot/starboard/StarboardListener.java | 6 +- .../db/migration/V3.2.0__setup_items.sql | 7 +- 17 files changed, 368 insertions(+), 129 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/items/ItemExpirationTask.java diff --git a/embeds.json b/embeds.json index dcbe04a..b663e17 100644 --- a/embeds.json +++ b/embeds.json @@ -278,7 +278,22 @@ }, "itemAdd": { "title": "Erfolg", - "description": "Die Items wurden dem Nutzer hinzugefügt", + "description": "Das Item wurde dem Nutzer hinzugefügt", + "color": "#67c94f" + }, + "itemExpired": { + "title": "Item abgelaufen!", + "description": "Dein {item} ist abgelaufen!", + "color": "#f4cd53" + }, + "playActivityAdd": { + "title": "Glückwunsch", + "description": "Du hast in den letzten 30 Tagen genug Karma gesammelt um die :star: Premium 30 Tage Rolle zu erhalten", + "color": "#67c94f" + }, + "playActivityRenew": { + "title": "Glückwunsch", + "description": "Du hast in den letzten 30 Tagen genug Karma gesammelt um die :star: Premium 30 Tage Rolle weiterhin zu behalten", "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 2b23008..5b4e4d0 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -15,6 +15,7 @@ import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; +import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.utils.MemberCachePolicy; @@ -27,6 +28,7 @@ public class Bot { private final Database database; private final EmbedCache embedCache; private final TaskScheduler taskScheduler; + private final Guild guild; @SuppressWarnings("DataFlowIssue") private Bot(long guildId) throws InterruptedException, RuntimeException { @@ -65,6 +67,8 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { taskScheduler = new TaskScheduler(this); + guild = jda.getGuildById(guildId); + jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); } @@ -97,4 +101,8 @@ public Database getDatabase() { public EmbedCache getEmbedCache() { return embedCache; } + + public Guild getGuild() { + return guild; + } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index ea30c7b..567c955 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -34,13 +34,13 @@ public Database() { dataSource = new HikariDataSource(config); settingsService = new SettingsService(dataSource); - rankService = new RankService(dataSource); + itemService = new ItemService(dataSource); + rankService = new RankService(dataSource, itemService); contestEventService = new ContestEventService(dataSource); collectEventService = new CollectEventService(dataSource); permissionsService = new PermissionsService(dataSource); - karmaService = new KarmaService(dataSource); + karmaService = new KarmaService(dataSource, rankService, itemService); starboardService = new StarboardService(dataSource); - itemService = new ItemService(dataSource); } public void closeDataSource() { diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java index 6364f58..87dea88 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java @@ -80,9 +80,7 @@ private void onCollectPointChange(int oldPoints, int newPoints, Member member, G if (reward.xp() > 0) { var xpChangeResult = rankService.addXp(member, reward.xp()); - rankService.onXpChange(xpChangeResult, member, guild, embedCache).ifPresent(it -> - settingsService.getBotChannel(guild).sendMessage(it).queue() - ); + rankService.onXpChange(xpChangeResult, member, guild, embedCache); } if (reward.roleId() > 0) { diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/ItemExpirationTask.java b/src/main/java/com/github/kaktushose/nplaybot/items/ItemExpirationTask.java new file mode 100644 index 0000000..f066154 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/items/ItemExpirationTask.java @@ -0,0 +1,68 @@ +package com.github.kaktushose.nplaybot.items; + +import com.github.kaktushose.nplaybot.Bot; +import com.github.kaktushose.nplaybot.scheduler.ScheduledTask; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.exceptions.ErrorHandler; +import net.dv8tion.jda.api.requests.ErrorResponse; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class ItemExpirationTask { + + public static final int PLAY_ACTIVITY_KARMA_THRESHOLD = 30; + private final ScheduledExecutorService executor; + + public ItemExpirationTask() { + executor = Executors.newScheduledThreadPool(4, runnable -> new Thread(runnable, "ItemExpiration")); + } + + @ScheduledTask(period = 24, unit = TimeUnit.HOURS) + public void onCheckItems(Bot bot) { + var transactions = bot.getDatabase().getItemService().getExpiringTransactions(); + + for (var transaction : transactions) { + executor.schedule(() -> { + bot.getDatabase().getItemService().deleteTransaction( + UserSnowflake.fromId(transaction.userId()), + transaction.transactionId(), + bot.getGuild() + ); + + if (transaction.isPlayActivity()) { + var rankInfo = bot.getDatabase().getRankService().getUserInfo(UserSnowflake.fromId(transaction.userId())); + if (rankInfo.karma() - rankInfo.lastKarma() >= PLAY_ACTIVITY_KARMA_THRESHOLD) { + bot.getDatabase().getItemService().addPlayActivity(UserSnowflake.fromId(transaction.userId()), bot.getGuild()); + bot.getDatabase().getItemService().updateLastKarma(UserSnowflake.fromId(transaction.userId())); + + messageUser(transaction, bot.getEmbedCache().getEmbed("playActivityRenew").toEmbedBuilder(), bot); + return; + } + } + + var item = bot.getDatabase().getItemService().getItem(transaction.itemId()); + var emoji = bot.getDatabase().getItemService().getTypeEmoji(item.typeId()); + EmbedBuilder embed = bot.getEmbedCache().getEmbed("itemExpired") + .injectValue("item", String.format("%s %s", emoji, item.name())) + .toEmbedBuilder(); + + messageUser(transaction, embed, bot); + + }, transaction.expiresAt() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + } + + private void messageUser(ItemService.Transaction transaction, EmbedBuilder embed, Bot bot) { + var user = bot.getJda().getUserById(transaction.userId()); + user.openPrivateChannel().flatMap(privateChannel -> privateChannel.sendMessageEmbeds(embed.build())) + .queue(null, new ErrorHandler().handle(ErrorResponse.CANNOT_SEND_TO_USER, exception -> { + TextChannel channel = bot.getDatabase().getSettingsService().getBotChannel(bot.getGuild()); + channel.sendMessage(user.getAsMention()).and(channel.sendMessageEmbeds(embed.build())).queue(); + })); + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java index d517e01..3a961ca 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java @@ -8,10 +8,12 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; public class ItemService { private final DataSource dataSource; + private static final int PLAY_ACTIVITY_ITEM_ID = 7; public ItemService(DataSource dataSource) { this.dataSource = dataSource; @@ -22,7 +24,7 @@ public List getAllItems() { var items = new ArrayList(); var statement = connection.prepareStatement(""" - SELECT item_id, type_id, items.name, duration, role_id + SELECT * FROM items JOIN item_types ON item_types.base_type_id = items.type_id """ @@ -47,7 +49,7 @@ public List getAllItems() { public Item getItem(int itemId) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT item_id, type_id, items.name, duration, role_id + SELECT * FROM items JOIN item_types ON item_types.base_type_id = items.type_id WHERE item_id = ? @@ -73,7 +75,7 @@ public List getTransactions(UserSnowflake user) { var transactions = new ArrayList(); var statement = connection.prepareStatement(""" - SELECT transaction_id, transactions.item_id, type_id, items.name, duration, expires_at, role_id + SELECT * FROM transactions JOIN items ON transactions.item_id = items.item_id JOIN item_types ON item_types.base_type_id = items.type_id @@ -85,12 +87,14 @@ public List getTransactions(UserSnowflake user) { while (result.next()) { transactions.add(new Transaction( result.getInt("transaction_id"), + result.getLong("user_id"), result.getInt("item_id"), result.getInt("type_id"), result.getString("name"), result.getLong("duration"), result.getLong("expires_at"), - result.getLong("role_id") + result.getLong("role_id"), + result.getBoolean("is_play_activity") )); } @@ -147,11 +151,76 @@ public String getTypeEmoji(int typeId) { } } + public void updateLastKarma(UserSnowflake user) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("UPDATE users SET last_karma = karma_points WHERE user_id = ?"); + statement.setLong(1, user.getIdLong()); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + + public void addPlayActivity(UserSnowflake user, Guild guild) { + try (Connection connection = dataSource.getConnection()) { + var item = getItem(PLAY_ACTIVITY_ITEM_ID); + var statement = connection.prepareStatement("INSERT INTO transactions (\"user_id\", \"item_id\", \"expires_at\", \"is_play_activity\") VALUES(?, ?, ?, true)"); + statement.setLong(1, user.getIdLong()); + statement.setInt(2, PLAY_ACTIVITY_ITEM_ID); + statement.setLong(3, System.currentTimeMillis() + item.duration); + statement.execute(); + + if (item.roleId > 0) { + guild.addRoleToMember(user, guild.getRoleById(item.roleId())).queue(); + } + + updateLastKarma(user); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public List getExpiringTransactions() { + try (Connection connection = dataSource.getConnection()) { + var transactions = new ArrayList(); + + var statement = connection.prepareStatement(""" + SELECT * + FROM transactions + JOIN items ON transactions.item_id = items.item_id + JOIN item_types ON item_types.base_type_id = items.type_id + WHERE expires_at < ? + """ + ); + statement.setLong(1, System.currentTimeMillis() + TimeUnit.HOURS.toMillis(24)); + var result = statement.executeQuery(); + while (result.next()) { + transactions.add(new Transaction( + result.getInt("transaction_id"), + result.getLong("user_id"), + result.getInt("item_id"), + result.getInt("type_id"), + result.getString("name"), + result.getLong("duration"), + result.getLong("expires_at"), + result.getLong("role_id"), + result.getBoolean("is_play_activity") + )); + } + + return transactions; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public record Item(int itemId, int typeId, String name, long duration, long roleId) { } - public record Transaction(int transactionId, int itemId, int typeId, String name, long duration, long expiresAt, - long roleId) { + public record Transaction(int transactionId, long userId, int itemId, int typeId, String name, long duration, + long expiresAt, + long roleId, boolean isPlayActivity) { } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java index f987487..f64f70a 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java @@ -24,8 +24,17 @@ public class KarmaConfigCommands { @SlashCommand(value = "balance add karma", desc = "Fügt einem User Karma hinzu", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) @Permissions(BotPermissions.MODIFY_USER_BALANCE) public void onAddKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE) @Max(Integer.MAX_VALUE) int amount) { + var oldKarma = database.getRankService().getUserInfo(target).karma(); + var newKarma = oldKarma + amount; + database.getKarmaService().addKarma(target, amount); + if (newKarma > oldKarma) { + database.getKarmaService().onKarmaIncrease(oldKarma, newKarma, event.getMember(), event.getGuild(), embedCache); + } else if (newKarma < oldKarma) { + database.getKarmaService().onKarmaDecrease(oldKarma, newKarma, event.getMember(), event.getGuild(), embedCache); + } + event.reply(embedCache.getEmbed("addKarmaResult") .injectValue("user", target.getAsMention()) .injectValue("karma", amount) @@ -35,8 +44,15 @@ public void onAddKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE @SlashCommand(value = "balance set karma", desc = "Setzt die Karma Punkte von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) @Permissions(BotPermissions.MODIFY_USER_BALANCE) public void onSetKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE) @Max(Integer.MAX_VALUE) int value) { + var oldKarma = database.getRankService().getUserInfo(target).karma(); database.getKarmaService().setKarma(target, value); + if (value > oldKarma) { + database.getKarmaService().onKarmaIncrease(oldKarma, value, event.getMember(), event.getGuild(), embedCache); + } else if (value < oldKarma) { + database.getKarmaService().onKarmaDecrease(oldKarma, value, event.getMember(), event.getGuild(), embedCache); + } + event.reply(embedCache.getEmbed("setKarmaResult") .injectValue("user", target.getAsMention()) .injectValue("karma", value) diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java index 956b249..bdd1775 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java @@ -2,6 +2,7 @@ import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.Database; +import com.github.kaktushose.nplaybot.items.ItemService; import com.github.kaktushose.nplaybot.rank.RankService; import com.github.kaktushose.nplaybot.settings.SettingsService; import net.dv8tion.jda.api.EmbedBuilder; @@ -15,17 +16,17 @@ import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.jetbrains.annotations.NotNull; +import static com.github.kaktushose.nplaybot.items.ItemExpirationTask.PLAY_ACTIVITY_KARMA_THRESHOLD; + public class KarmaListener extends ListenerAdapter { private final KarmaService karmaService; private final RankService rankService; - private final SettingsService settingsService; private final EmbedCache embedCache; public KarmaListener(Database database, EmbedCache embedCache) { karmaService = database.getKarmaService(); rankService = database.getRankService(); - settingsService = database.getSettingsService(); this.embedCache = embedCache; } @@ -45,7 +46,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { } int oldKarma = rankService.getUserInfo(UserSnowflake.fromId(event.getMessageAuthorIdLong())).karma(); int newKarma = karmaService.onKarmaVoteAdd(event.getUser(), UserSnowflake.fromId(event.getMessageAuthorIdLong())); - event.retrieveMessage().queue(message -> onKarmaIncrease(oldKarma, newKarma, message.getMember(), message.getGuild())); + event.retrieveMessage().queue(message -> karmaService.onKarmaIncrease(oldKarma, newKarma, message.getMember(), message.getGuild(), embedCache)); } @Override @@ -65,69 +66,7 @@ public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { } int oldKarma = rankService.getUserInfo(message.getAuthor()).karma(); int newKarma = karmaService.onKarmaVoteRemove(event.getUser(), message.getAuthor()); - onKarmaDecrease(oldKarma, newKarma, message.getMember(), message.getGuild()); + karmaService.onKarmaDecrease(oldKarma, newKarma, message.getMember(), message.getGuild(), embedCache); }); } - - public void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild guild) { - var rewards = karmaService.getKarmaRewards(); - var optional = rewards.stream() - .filter(it -> it.threshold() > oldKarma) - .filter(it -> it.threshold() <= newKarma) - .findFirst(); - - if (optional.isEmpty()) { - return; - } - var reward = optional.get(); - - if (reward.xp() > 0) { - var xpChangeResult = rankService.addXp(member, reward.xp()); - rankService.onXpChange(xpChangeResult, member, guild, embedCache).ifPresent(it -> - settingsService.getBotChannel(guild).sendMessage(it).queue() - ); - } - - if (reward.roleId() > 0) { - guild.addRoleToMember(member, guild.getRoleById(reward.roleId())).queue(); - } - - var builder = new MessageCreateBuilder().addContent(member.getAsMention()) - .addEmbeds(EmbedBuilder.fromData(DataObject.fromJson(reward.embed())).build()) - .build(); - settingsService.getBotChannel(guild).sendMessage(builder).queue(); - } - - private void onKarmaDecrease(int oldKarma, int newKarma, Member member, Guild guild) { - var rewards = karmaService.getKarmaRewards(); - var optional = rewards.stream() - .filter(it -> it.threshold() < oldKarma) - .filter(it -> it.threshold() >= newKarma) - .findFirst(); - - if (optional.isEmpty()) { - return; - } - var reward = optional.get(); - - if (reward.xp() > 0) { - var xpChangeResult = rankService.addXp(member, -reward.xp()); - rankService.onXpChange(xpChangeResult, member, guild, embedCache).ifPresent(it -> - settingsService.getBotChannel(guild).sendMessage(it).queue() - ); - } - - if (reward.roleId() > 0) { - guild.removeRoleFromMember(member, guild.getRoleById(reward.roleId())).queue(); - } - - var builder = new MessageCreateBuilder() - .addContent(member.getAsMention()) - .addEmbeds(embedCache.getEmbed("karmaRewardRemove") - .injectValue("user", member.getAsMention()) - .toEmbedBuilder() - .build() - ).build(); - settingsService.getBotChannel(guild).sendMessage(builder).queue(); - } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java index 85aaec6..df98f34 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java @@ -1,11 +1,15 @@ package com.github.kaktushose.nplaybot.karma; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Role; -import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.entities.UserSnowflake; +import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.items.ItemService; +import com.github.kaktushose.nplaybot.rank.RankService; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,13 +20,19 @@ import java.util.Arrays; import java.util.List; +import static com.github.kaktushose.nplaybot.items.ItemExpirationTask.PLAY_ACTIVITY_KARMA_THRESHOLD; + public class KarmaService { private static final Logger log = LoggerFactory.getLogger(KarmaService.class); private final DataSource dataSource; + private final RankService rankService; + private final ItemService itemService; - public KarmaService(DataSource dataSource) { + public KarmaService(DataSource dataSource, RankService rankService, ItemService itemService) { this.dataSource = dataSource; + this.rankService = rankService; + this.itemService = itemService; } public void setKarma(UserSnowflake user, int karma) { @@ -59,6 +69,100 @@ public void addKarma(UserSnowflake user, int karma) { } } + public void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild guild, EmbedCache embedCache) { + // play activity role + var rankInfo = rankService.getUserInfo(member); + if (newKarma - rankInfo.lastKarma() >= PLAY_ACTIVITY_KARMA_THRESHOLD) { + if (itemService.getTransactions(member).stream().anyMatch(ItemService.Transaction::isPlayActivity)) { + return; + } + + itemService.addPlayActivity(member, guild); + itemService.updateLastKarma(member); + + var builder = new MessageCreateBuilder().addContent(member.getAsMention()) + .addEmbeds(embedCache.getEmbed("playActivityAdd").toMessageEmbed()) + .build(); + getBotChannel(guild).sendMessage(builder).queue(); + } + + // karma rewards + var rewards = getKarmaRewards(); + var optional = rewards.stream() + .filter(it -> it.threshold() > oldKarma) + .filter(it -> it.threshold() <= newKarma) + .findFirst(); + + if (optional.isEmpty()) { + return; + } + var reward = optional.get(); + + if (reward.xp() > 0) { + var xpChangeResult = rankService.addXp(member, reward.xp()); + rankService.onXpChange(xpChangeResult, member, guild, embedCache); + } + + if (reward.roleId() > 0) { + guild.addRoleToMember(member, guild.getRoleById(reward.roleId())).queue(); + } + + var builder = new MessageCreateBuilder().addContent(member.getAsMention()) + .addEmbeds(EmbedBuilder.fromData(DataObject.fromJson(reward.embed())).build()) + .build(); + getBotChannel(guild).sendMessage(builder).queue(); + } + + public void onKarmaDecrease(int oldKarma, int newKarma, Member member, Guild guild, EmbedCache embedCache) { + var rewards = getKarmaRewards(); + var optional = rewards.stream() + .filter(it -> it.threshold() < oldKarma) + .filter(it -> it.threshold() >= newKarma) + .findFirst(); + + if (optional.isEmpty()) { + return; + } + var reward = optional.get(); + + if (reward.xp() > 0) { + var xpChangeResult = rankService.addXp(member, -reward.xp()); + rankService.onXpChange(xpChangeResult, member, guild, embedCache); + } + + if (reward.roleId() > 0) { + guild.removeRoleFromMember(member, guild.getRoleById(reward.roleId())).queue(); + } + + var builder = new MessageCreateBuilder() + .addContent(member.getAsMention()) + .addEmbeds(embedCache.getEmbed("karmaRewardRemove") + .injectValue("user", member.getAsMention()) + .toEmbedBuilder() + .build() + ).build(); + getBotChannel(guild).sendMessage(builder).queue(); + } + + private TextChannel getBotChannel(Guild guild) { + log.debug("Querying bot channel"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT bot_channel_id + FROM guild_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return guild.getTextChannelById(result.getLong(1)); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public void resetTokens() { log.debug("Resetting all karma tokens to default value"); try (var connection = dataSource.getConnection()) { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index 608f3ff..912dd8b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -64,9 +64,7 @@ private void onAddRegularXp(MessageReceivedEvent event) { rankService.updateValidMessage(event.getAuthor()); var result = rankService.addRandomXp(event.getAuthor()); - rankService.onXpChange(result, event.getMember(), event.getGuild(), embedCache).ifPresent(it -> - settingsService.getBotChannel(event.getGuild()).sendMessage(it).queue() - ); + rankService.onXpChange(result, event.getMember(), event.getGuild(), embedCache); } private void onXpLootDrop(MessageReceivedEvent event) { @@ -99,9 +97,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { var xp = xpLootDrops.get(messageId); var result = rankService.addXp(event.getMember(), xp); - rankService.onXpChange(result, event.getMember(), event.getGuild(), embedCache).ifPresent(it -> - settingsService.getBotChannel(event.getGuild()).sendMessage(it).queue() - ); + rankService.onXpChange(result, event.getMember(), event.getGuild(), embedCache); xpLootDrops.remove(messageId); event.retrieveMessage().queue(message -> { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 2585e37..2c5edba 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot.rank; import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.items.ItemService; import com.github.kaktushose.nplaybot.rank.leaderboard.LeaderboardPage; import com.github.kaktushose.nplaybot.rank.model.RankConfig; import com.github.kaktushose.nplaybot.rank.model.RankInfo; @@ -10,6 +11,7 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -25,9 +27,11 @@ public class RankService { private static final Logger log = LoggerFactory.getLogger(RankService.class); private final DataSource dataSource; + private final ItemService itemService; - public RankService(DataSource dataSource) { + public RankService(DataSource dataSource, ItemService itemService) { this.dataSource = dataSource; + this.itemService = itemService; } public void createUser(UserSnowflake user) { @@ -60,7 +64,7 @@ private Optional getRankInfo(int rankId) { log.debug("Querying rank info for rank: {}", rankId); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT role_id, name, color, bound + SELECT * FROM ranks WHERE rank_id = ? """ @@ -74,7 +78,9 @@ private Optional getRankInfo(int rankId) { result.getLong("role_id"), result.getString("name"), result.getString("color"), - result.getInt("bound") + result.getInt("bound"), + result.getBoolean("lootbox_reward"), + result.getInt("item_reward_id") )); } return Optional.empty(); @@ -87,7 +93,7 @@ public UserInfo getUserInfo(UserSnowflake user) { log.debug("Querying user info for user: {}", user); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" - SELECT xp, rank_id, message_count, start_xp, karma_points + SELECT * FROM users WHERE user_id = ? """ @@ -106,7 +112,8 @@ public UserInfo getUserInfo(UserSnowflake user) { nextRank, result.getInt("message_count"), result.getInt("xp") - result.getInt("start_xp"), - result.getInt("karma_points") + result.getInt("karma_points"), + result.getInt("last_karma") ); } catch (SQLException e) { throw new RuntimeException(e); @@ -342,20 +349,49 @@ public XpChangeResult setXp(UserSnowflake user, int value) { } } - public Optional onXpChange(XpChangeResult result, Member member, Guild guild, EmbedCache embedCache) { + public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedCache embedCache) { log.debug("Checking for rank up: {}", member); updateRankRoles(member, guild, result); if (!result.rankChanged()) { log.debug("Rank hasn't changed"); - return Optional.empty(); + return; } log.debug("Applying changes. New rank: {}", result.currentRank()); + if (result.currentRank().lootboxReward()) { + // TODO trigger lootbox + } + + if (result.currentRank().itemRewardId() > 0) { + itemService.createTransaction(member, result.currentRank().itemRewardId(), guild); + } + var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; - return Optional.of(new MessageCreateBuilder().addContent(member.getAsMention()) + var message = new MessageCreateBuilder().addContent(member.getAsMention()) .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).toMessageEmbed()) - .build()); + .build(); + + getBotChannel(guild).sendMessage(message).queue(); + } + + private TextChannel getBotChannel(Guild guild) { + log.debug("Querying bot channel"); + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT bot_channel_id + FROM guild_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return guild.getTextChannelById(result.getLong(1)); + } catch (SQLException e) { + throw new RuntimeException(e); + } } public void updateRankRoles(Member member, Guild guild, XpChangeResult result) { @@ -468,7 +504,8 @@ public Map getDailyRankInfos() { nextRank, result.getInt("message_count"), result.getInt("xp") - result.getInt("start_xp"), - result.getInt("karma_points") + result.getInt("karma_points"), + result.getInt("last_karma") )); } return users; diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java index aab1b2b..3e08a25 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java @@ -10,11 +10,8 @@ import com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent; import com.github.kaktushose.nplaybot.Database; import com.github.kaktushose.nplaybot.permissions.BotPermissions; -import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +35,7 @@ public void onAddXp(CommandEvent event, Member target, @Min(1) @Max(Integer.MAX_ .injectValue("xp", amount) ); - checkRankUpdate(result, target, event.getGuild()); + database.getRankService().onXpChange(result, event.getMember(), event.getGuild(), embedCache); } @SlashCommand(value = "balance set xp", desc = "Setzt die XP von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) @@ -50,23 +47,6 @@ public void onSetXp(CommandEvent event, Member target, @Min(0) @Max(Integer.MAX_ .injectValue("xp", value) ); - checkRankUpdate(result, target, event.getGuild()); - } - - private void checkRankUpdate(XpChangeResult result, Member member, Guild guild) { - log.debug("Checking for rank up: {}", member); - database.getRankService().updateRankRoles(member, guild, result); - - if (!result.rankChanged()) { - log.debug("Rank hasn't changed"); - return; - } - log.debug("Applying changes. New rank: {}", result.currentRank()); - - var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; - var messageData = new MessageCreateBuilder().addContent(member.getAsMention()) - .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member.getUser())).toMessageEmbed()) - .build(); - database.getSettingsService().getBotChannel(guild).sendMessage(messageData).queue(); + database.getRankService().onXpChange(result, event.getMember(), event.getGuild(), embedCache); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java index 3a7b119..6cf3796 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -11,6 +11,8 @@ import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.interactions.commands.Command; +import java.util.concurrent.TimeUnit; + @Interaction @Permissions(BotPermissions.USER) public class RankInfoCommand { @@ -52,7 +54,9 @@ private void sendReply(UserInfo userInfo, User user, CommandEvent event) { items.append(database.getItemService().getTypeEmoji(it.typeId())) .append(" ") .append(it.name()) - .append("\n") + .append(" (läuft ab ") + .append(String.format("", TimeUnit.MILLISECONDS.toSeconds(it.expiresAt()))) + .append(")\n") ); embed.addField("Items", items.toString(), false); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java index d0ed97d..f02de01 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java @@ -1,4 +1,4 @@ package com.github.kaktushose.nplaybot.rank.model; -public record RankInfo(long roleId, String name, String color, int xpBound) { +public record RankInfo(long roleId, String name, String color, int xpBound, boolean lootboxReward, int itemRewardId) { } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java index b60ba3f..8fe10bd 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/UserInfo.java @@ -7,7 +7,7 @@ import java.util.Optional; public record UserInfo(int currentXp, RankInfo currentRank, Optional nextRank, int messageCount, int xpGain, - int karma) { + int karma, int lastKarma) { public Map getEmbedValues(User user) { var result = new HashMap() {{ diff --git a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java index 0ace81b..23f4540 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java @@ -31,13 +31,13 @@ public class StarboardListener extends ListenerAdapter { private final StarboardService starboardService; private final KarmaService karmaService; private final RankService rankService; - private final KarmaListener karmaListener; + private final EmbedCache embedCache; public StarboardListener(Database database, EmbedCache embedCache) { this.starboardService = database.getStarboardService(); this.karmaService = database.getKarmaService(); this.rankService = database.getRankService(); - this.karmaListener = new KarmaListener(database, embedCache); + this.embedCache = embedCache; } @Override @@ -69,7 +69,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { var oldKarma = rankService.getUserInfo(event.getUser()).karma(); karmaService.addKarma(UserSnowflake.fromId(event.getMessageAuthorIdLong()), starboardService.getKarmaReward(event.getGuild())); var newKarma = rankService.getUserInfo(event.getUser()).karma(); - karmaListener.onKarmaIncrease(oldKarma, newKarma, event.getMember(), event.getGuild()); + karmaService.onKarmaIncrease(oldKarma, newKarma, event.getMember(), event.getGuild(), embedCache); } var starboardChannel = event.getGuild().getTextChannelById(starboardService.getStarboardChannelId(event.getGuild())); diff --git a/src/main/resources/db/migration/V3.2.0__setup_items.sql b/src/main/resources/db/migration/V3.2.0__setup_items.sql index 6fd5e78..2196375 100644 --- a/src/main/resources/db/migration/V3.2.0__setup_items.sql +++ b/src/main/resources/db/migration/V3.2.0__setup_items.sql @@ -16,7 +16,8 @@ CREATE TABLE transactions( transaction_id SERIAL PRIMARY KEY, user_id BIGINT NOT NULL, item_id SERIAL NOT NULL REFERENCES items(item_id), - expires_at BIGINT NOT NULL + expires_at BIGINT NOT NULL, + is_play_activity BOOLEAN NOT NULL DEFAULT FALSE ); ALTER TABLE users ADD COLUMN last_karma INT NOT NULL DEFAULT 0; @@ -52,3 +53,7 @@ BEGIN RETURN NEXT; END; $$ LANGUAGE plpgsql; + +ALTER TABLE ranks ADD COLUMN lootbox_reward BOOLEAN NOT NULL DEFAULT FALSE; + +ALTER TABLE ranks ADD COLUMN item_reward_id INT NOT NULL DEFAULT -1; From 4ed85c41babfb4aac99325d707922a39a1b0ff45 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Sun, 30 Jun 2024 21:10:04 +0200 Subject: [PATCH 58/66] implement lootboxes --- embeds.json | 23 +++++ .../com/github/kaktushose/nplaybot/Bot.java | 4 +- .../github/kaktushose/nplaybot/Database.java | 5 +- .../nplaybot/rank/LootboxListener.java | 86 +++++++++++++++++++ .../nplaybot/rank/RankListener.java | 21 ++++- .../kaktushose/nplaybot/rank/RankService.java | 40 +++++++-- .../db/migration/V3.2.0__setup_items.sql | 28 ++++++ 7 files changed, 197 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java diff --git a/embeds.json b/embeds.json index b663e17..cf9fe38 100644 --- a/embeds.json +++ b/embeds.json @@ -11,6 +11,10 @@ { "name": "Nächste Stufe:", "value": ":dart: {nextRank} (noch {xp} XP)" + }, + { + "name": "Belohnung", + "value": "{reward}" } ] }, @@ -26,6 +30,10 @@ { "name": "Herzlichen Glückwunsch", "value": ":confetti_ball: Du hast hiermit die maximale Stufe erreicht" + }, + { + "name": "Belohnung", + "value": "{reward}" } ] }, @@ -295,5 +303,20 @@ "title": "Glückwunsch", "description": "Du hast in den letzten 30 Tagen genug Karma gesammelt um die :star: Premium 30 Tage Rolle weiterhin zu behalten", "color": "#67c94f" + }, + "onLootboxXp": { + "title": "Glückwunsch", + "description": "Du hast eine Lootbox gefunden und {xp} XP erhalten", + "color": "#67c94f" + }, + "onLootboxKarma": { + "title": "Glückwunsch", + "description": "Du hast eine Lootbox gefunden und {karma} Karma erhalten", + "color": "#67c94f" + }, + "onLootboxItem": { + "title": "Glückwunsch", + "description": "Du hast eine Lootbox gefunden und {item} erhalten", + "color": "#67c94f" } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 5b4e4d0..4851497 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -33,7 +33,7 @@ public class Bot { @SuppressWarnings("DataFlowIssue") private Bot(long guildId) throws InterruptedException, RuntimeException { try { - database = new Database(); + database = new Database(this); } catch (Exception e) { throw new RuntimeException("Unable to connect to database!", e); } @@ -50,7 +50,7 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) .addEventListeners( - new RankListener(database, embedCache), + new RankListener(database, embedCache, this), new JoinLeaveListener(database.getRankService()), new ContestListener(database.getContestEventService()), new CollectEventListener(database, embedCache), diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 567c955..72e3d0a 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -10,6 +10,7 @@ import com.github.kaktushose.nplaybot.starboard.StarboardService; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import net.dv8tion.jda.api.JDA; public class Database { @@ -23,7 +24,7 @@ public class Database { private final StarboardService starboardService; private final ItemService itemService; - public Database() { + public Database(Bot bot) { var config = new HikariConfig(); config.setJdbcUrl(System.getenv("POSTGRES_URL")); @@ -35,7 +36,7 @@ public Database() { settingsService = new SettingsService(dataSource); itemService = new ItemService(dataSource); - rankService = new RankService(dataSource, itemService); + rankService = new RankService(dataSource, itemService, bot); contestEventService = new ContestEventService(dataSource); collectEventService = new CollectEventService(dataSource); permissionsService = new PermissionsService(dataSource); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java new file mode 100644 index 0000000..a463729 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java @@ -0,0 +1,86 @@ +package com.github.kaktushose.nplaybot.rank; + +import com.github.kaktushose.nplaybot.Bot; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class LootboxListener extends ListenerAdapter { + + private static final String LOOTBOX_EMOJI = "\uD83C\uDF81"; + private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + private final Bot bot; + private final RankService.Lootbox lootbox; + private final UserSnowflake user; + private final long messageId; + + private LootboxListener(Bot bot, RankService.Lootbox lootbox, UserSnowflake user, Message message) { + this.bot = bot; + this.lootbox = lootbox; + this.user = user; + this.messageId = message.getIdLong(); + bot.getJda().addEventListener(this); + message.addReaction(Emoji.fromFormatted(LOOTBOX_EMOJI)).queue(); + executor.schedule(() -> terminate(message), 30, TimeUnit.MINUTES); + } + + public static void newListener(Bot bot, RankService.Lootbox lootbox, UserSnowflake user, Message message) { + new LootboxListener(bot, lootbox, user, message); + } + + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + if (event.getMessageIdLong() != messageId) { + return; + } + if (event.getUserIdLong() != user.getIdLong()) { + return; + } + if (!event.getReaction().getEmoji().equals(Emoji.fromFormatted(LOOTBOX_EMOJI))) { + return; + } + + event.getReaction().clearReactions().queue(); + + if (lootbox.xpReward() > 0) { + var result = bot.getDatabase().getRankService().addXp(user, lootbox.xpReward()); + bot.getDatabase().getRankService().onXpChange(result, event.getMember(), bot.getGuild(), bot.getEmbedCache()); + event.getChannel().sendMessage(user.getAsMention()) + .and(event.getChannel().sendMessage(bot.getEmbedCache().getEmbed("onLootboxXp").injectValue("xp", lootbox.xpReward()).toMessageCreateData())) + .queue(); + return; + } + + if (lootbox.karmaReward() > 0) { + var oldKarma = bot.getDatabase().getRankService().getUserInfo(user).karma(); + var newKarma = oldKarma + lootbox.karmaReward(); + bot.getDatabase().getKarmaService().addKarma(user, lootbox.karmaReward()); + bot.getDatabase().getKarmaService().onKarmaIncrease(oldKarma, newKarma, event.getMember(), bot.getGuild(), bot.getEmbedCache()); + event.getChannel().sendMessage(user.getAsMention()) + .and(event.getChannel().sendMessage(bot.getEmbedCache().getEmbed("onLootboxKarma").injectValue("karma", lootbox.karmaReward()).toMessageCreateData())) + .queue(); + return; + } + + if (lootbox.itemId() >= 0) { + bot.getDatabase().getItemService().createTransaction(user, lootbox.itemId(), bot.getGuild()); + var item = bot.getDatabase().getItemService().getItem(lootbox.itemId()); + event.getChannel().sendMessage(user.getAsMention()) + .and(event.getChannel().sendMessage(bot.getEmbedCache().getEmbed("onLootboxItem").injectValue("item", item.name()).toMessageCreateData())) + .queue(); + } + } + + private void terminate(Message message) { + message.clearReactions().queue(); + bot.getJda().removeEventListener(this); + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index 912dd8b..b5e85ec 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot.rank; import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.Bot; import com.github.kaktushose.nplaybot.Database; import com.github.kaktushose.nplaybot.settings.SettingsService; import net.dv8tion.jda.api.entities.emoji.Emoji; @@ -13,20 +14,25 @@ import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; public class RankListener extends ListenerAdapter { + private static final int LOOTBOX_CHANCE = 70; + private static final int LOOTBOX_RETRIEVE_LIMIT = 30; private static final Logger log = LoggerFactory.getLogger(RankListener.class); private final RankService rankService; private final SettingsService settingsService; private final EmbedCache embedCache; + private final Bot bot; private final Map xpLootDrops; - public RankListener(Database database, EmbedCache embedCache) { + public RankListener(Database database, EmbedCache embedCache, Bot bot) { this.rankService = database.getRankService(); this.settingsService = database.getSettingsService(); this.embedCache = embedCache; + this.bot = bot; xpLootDrops = new HashMap<>(); } @@ -52,6 +58,8 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { onXpLootDrop(event); + onCheckForLootbox(event); + if (!rankService.isValidMessage(event.getMessage())) { log.debug("Message doesn't meet rank criteria"); return; @@ -60,6 +68,17 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { onAddRegularXp(event); } + private void onCheckForLootbox(MessageReceivedEvent event) { + if (ThreadLocalRandom.current().nextInt(100) >= LOOTBOX_CHANCE) { + return; + } + event.getChannel().getHistory().retrievePast(LOOTBOX_RETRIEVE_LIMIT).queue(messages -> { + var message = messages.get(ThreadLocalRandom.current().nextInt(LOOTBOX_RETRIEVE_LIMIT)); + var lootbox = rankService.getRandomLootbox(); + LootboxListener.newListener(bot, lootbox, event.getMember(), message); + }); + } + private void onAddRegularXp(MessageReceivedEvent event) { rankService.updateValidMessage(event.getAuthor()); var result = rankService.addRandomXp(event.getAuthor()); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 2c5edba..cdccc1b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot.rank; import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.Bot; import com.github.kaktushose.nplaybot.items.ItemService; import com.github.kaktushose.nplaybot.rank.leaderboard.LeaderboardPage; import com.github.kaktushose.nplaybot.rank.model.RankConfig; @@ -14,7 +15,6 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,10 +28,12 @@ public class RankService { private static final Logger log = LoggerFactory.getLogger(RankService.class); private final DataSource dataSource; private final ItemService itemService; + private final Bot bot; - public RankService(DataSource dataSource, ItemService itemService) { + public RankService(DataSource dataSource, ItemService itemService, Bot bot) { this.dataSource = dataSource; this.itemService = itemService; + this.bot = bot; } public void createUser(UserSnowflake user) { @@ -359,22 +361,50 @@ public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedC } log.debug("Applying changes. New rank: {}", result.currentRank()); + Optional lootbox; + String reward = ""; if (result.currentRank().lootboxReward()) { - // TODO trigger lootbox + lootbox = Optional.of(getRandomLootbox()); + reward = "\uD83C\uDF81 eine Lootbox"; + } else { + lootbox = Optional.empty(); } if (result.currentRank().itemRewardId() > 0) { itemService.createTransaction(member, result.currentRank().itemRewardId(), guild); + reward = itemService.getItem(result.currentRank().itemRewardId()).name(); } var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; var message = new MessageCreateBuilder().addContent(member.getAsMention()) - .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).toMessageEmbed()) + .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).injectValue("reward", reward).toMessageEmbed()) .build(); - getBotChannel(guild).sendMessage(message).queue(); + getBotChannel(guild).sendMessage(message).queue(msg -> lootbox.ifPresent(it -> LootboxListener.newListener(bot, it, member, msg))); } + public Lootbox getRandomLootbox() { + try (Connection connection = dataSource.getConnection()) { + var result = connection.prepareStatement("SELECT * FROM get_random_lootbox()").executeQuery(); + result.next(); + + var statement = connection.prepareStatement("SELECT * FROM lootbox_rewards WHERE reward_id = ?"); + statement.setInt(1, result.getInt(1)); + result = statement.executeQuery(); + result.next(); + return new Lootbox( + result.getInt("reward_id"), + result.getInt("xp_reward"), + result.getInt("karma_reward"), + result.getObject("item_reward") == null ? -1 : result.getInt("item_reward") + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public record Lootbox(int id, int xpReward, int karmaReward, int itemId){} + private TextChannel getBotChannel(Guild guild) { log.debug("Querying bot channel"); try (Connection connection = dataSource.getConnection()) { diff --git a/src/main/resources/db/migration/V3.2.0__setup_items.sql b/src/main/resources/db/migration/V3.2.0__setup_items.sql index 2196375..1ffbe85 100644 --- a/src/main/resources/db/migration/V3.2.0__setup_items.sql +++ b/src/main/resources/db/migration/V3.2.0__setup_items.sql @@ -57,3 +57,31 @@ $$ LANGUAGE plpgsql; ALTER TABLE ranks ADD COLUMN lootbox_reward BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE ranks ADD COLUMN item_reward_id INT NOT NULL DEFAULT -1; + +CREATE TABLE lootbox_rewards ( + reward_id SERIAL NOT NULL PRIMARY KEY, + xp_reward INT NOT NULL DEFAULT -1, + karma_reward INT NOT NULL DEFAULT -1, + item_reward INT REFERENCES items(item_id), + chance INT NOT NULL DEFAULT 0 +); + +CREATE FUNCTION get_random_lootbox() +RETURNS INT AS +$$ +DECLARE + result_row RECORD; + box_chance INT; + chance_sum INT; +BEGIN + box_chance := floor(random() * 100) + 1; + chance_sum := 0; + FOR result_row IN SELECT * FROM lootbox_rewards + LOOP + chance_sum := chance_sum + result_row.chance; + IF chance_sum >= box_chance THEN + RETURN result_row.reward_id; + END IF; + END LOOP; +END; +$$ LANGUAGE plpgsql; From d43bd2c1bac2b48ea5930fb91459fbc14ac9cff8 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Mon, 1 Jul 2024 17:55:39 +0200 Subject: [PATCH 59/66] implement lootboxes --- embeds.json | 31 ++++++++++++ .../com/github/kaktushose/nplaybot/Bot.java | 4 +- .../nplaybot/items/ItemService.java | 27 +++++++++- .../nplaybot/rank/RankDecayTask.java | 46 +++++++++++++++++ .../kaktushose/nplaybot/rank/RankService.java | 31 +++++++++++- .../nplaybot/rank/model/RankInfo.java | 2 +- .../nplaybot/rank/model/XpChangeResult.java | 2 +- .../db/migration/V3.2.0__setup_items.sql | 50 +++++++++++++++++++ 8 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java diff --git a/embeds.json b/embeds.json index cf9fe38..203d278 100644 --- a/embeds.json +++ b/embeds.json @@ -318,5 +318,36 @@ "title": "Glückwunsch", "description": "Du hast eine Lootbox gefunden und {item} erhalten", "color": "#67c94f" + }, + + "rankDecrease": { + "title": ":arrow_down: Stufenabstieg!", + "description": "{user}", + "color": "{color}", + "fields": [ + { + "name": "Neue Stufe:", + "value": ":level_slider: {currentRank}" + }, + { + "name": "Nächste Stufe:", + "value": ":dart: {nextRank} (noch {xp} XP)" + } + ] + }, + "rankDecreaseMax": { + "title": ":arrow_down: Stufenabstieg!", + "description": "{user}", + "color": "{color}", + "fields": [ + { + "name": "Neue Stufe:", + "value": ":level_slider: {currentRank}" + }, + { + "name": "Ups", + "value": ":frowning: Du hast die unterste Stufe erreicht.\nAber Kopf hoch, schlimmer wirds nicht :wink:" + } + ] } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 4851497..e43cf45 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -65,11 +65,11 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { jdaCommands.getDependencyInjector().registerProvider(this); jdaCommands.getImplementationRegistry().setPermissionsProvider(new CustomPermissionsProvider(database)); - taskScheduler = new TaskScheduler(this); - guild = jda.getGuildById(guildId); jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); + + taskScheduler = new TaskScheduler(this); } public static Bot start(long guildId) throws InterruptedException { diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java index 3a961ca..e3663fa 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java @@ -106,14 +106,37 @@ public List getTransactions(UserSnowflake user) { public void createTransaction(UserSnowflake user, int itemId, Guild guild) { try (Connection connection = dataSource.getConnection()) { - var statement = connection.prepareStatement("INSERT INTO transactions (\"user_id\", \"item_id\", \"expires_at\") VALUES(?, ?, ?)"); + var item = getItem(itemId); + + // check if user already has item of this type + var statement = connection.prepareStatement(""" + SELECT transaction_id, expires_at FROM transactions + JOIN items ON items.item_id = transactions.item_id + JOIN item_types ON item_types.base_type_id = items.type_id + WHERE user_id = ? AND transactions.item_id = ? + """); statement.setLong(1, user.getIdLong()); statement.setInt(2, itemId); - var item = getItem(itemId); + var result = statement.executeQuery(); + // if so just increase the duration of the first item + if (result.next()) { + statement = connection.prepareStatement("UPDATE transactions SET expires_at = ? WHERE transaction_id = ?"); + statement.setLong(1,result.getLong("expires_at") + item.duration); + statement.setLong(2, result.getInt("transaction_id")); + statement.execute(); + return; + } + + statement = connection.prepareStatement("INSERT INTO transactions (\"user_id\", \"item_id\", \"expires_at\") VALUES(?, ?, ?)"); + statement.setLong(1, user.getIdLong()); + statement.setInt(2, itemId); + statement.setLong(3, System.currentTimeMillis() + item.duration); + if (item.roleId > 0) { guild.addRoleToMember(user, guild.getRoleById(item.roleId())).queue(); } + statement.execute(); } catch (SQLException e) { throw new RuntimeException(e); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java new file mode 100644 index 0000000..cd4f402 --- /dev/null +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java @@ -0,0 +1,46 @@ +package com.github.kaktushose.nplaybot.rank; + +import com.github.kaktushose.nplaybot.Bot; +import com.github.kaktushose.nplaybot.rank.model.XpChangeResult; +import com.github.kaktushose.nplaybot.scheduler.ScheduledTask; + +import java.util.concurrent.TimeUnit; + +public class RankDecayTask { + + private static final int START_DECAY_RANK_ID = 2; + private static final int DECAY_XP_AMOUNT = 150; + + @ScheduledTask(period = 7, unit = TimeUnit.DAYS) + public void accept(Bot bot) { + bot.getGuild().loadMembers(member -> { + var currentRank = bot.getDatabase().getRankService().getUserInfo(member); + + if (currentRank.currentRank().rankId() < START_DECAY_RANK_ID) { + return; + } + + var userXpAfterDecay = currentRank.currentXp() - DECAY_XP_AMOUNT; + var newRank = bot.getDatabase().getRankService().getRankByXp(userXpAfterDecay); + + XpChangeResult result; + // if new rank is present + if (newRank.isPresent()) { + // check if we fall under the minimum rank and set xp to lower bound + if (newRank.get().rankId() < START_DECAY_RANK_ID) { + var minimumRank = bot.getDatabase().getRankService().getRankInfo(START_DECAY_RANK_ID); + result = bot.getDatabase().getRankService().setXp(member, minimumRank.orElseThrow().xpBound()); + // if we don't fall under the minimum rank just remove all xp as planned + } else { + result = bot.getDatabase().getRankService().setXp(member, userXpAfterDecay); + } + // if new rank isn't present we went too far and also set xp to lower bound + } else { + var minimumRank = bot.getDatabase().getRankService().getRankInfo(START_DECAY_RANK_ID); + result = bot.getDatabase().getRankService().setXp(member, minimumRank.orElseThrow().xpBound()); + } + bot.getDatabase().getRankService().onXpChange(result, member, bot.getGuild(), bot.getEmbedCache()); + }); + } + +} diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index cdccc1b..3a0a76c 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -62,7 +62,7 @@ public void indexMembers(Guild guild) { guild.loadMembers(member -> createUser(member.getUser())); } - private Optional getRankInfo(int rankId) { + public Optional getRankInfo(int rankId) { log.debug("Querying rank info for rank: {}", rankId); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -77,6 +77,7 @@ private Optional getRankInfo(int rankId) { if (result.next()) { return Optional.of(new RankInfo( + result.getInt("rank_id"), result.getLong("role_id"), result.getString("name"), result.getString("color"), @@ -302,6 +303,7 @@ public XpChangeResult addRandomXp(UserSnowflake user) { log.debug("New xp: {}", result.getInt("current_xp")); return new XpChangeResult( result.getBoolean("rank_changed"), + getRankInfo(result.getInt("previous_rank")), getRankInfo(result.getInt("current_rank")).orElseThrow(), getRankInfo(result.getInt("next_rank")), result.getInt("current_xp") @@ -322,6 +324,7 @@ public XpChangeResult addXp(UserSnowflake user, int amount) { result.next(); return new XpChangeResult( result.getBoolean("rank_changed"), + getRankInfo(result.getInt("previous_rank")), getRankInfo(result.getInt("current_rank")).orElseThrow(), getRankInfo(result.getInt("next_rank")), result.getInt("current_xp") @@ -342,6 +345,7 @@ public XpChangeResult setXp(UserSnowflake user, int value) { result.next(); return new XpChangeResult( result.getBoolean("rank_changed"), + getRankInfo(result.getInt("previous_rank")), getRankInfo(result.getInt("current_rank")).orElseThrow(), getRankInfo(result.getInt("next_rank")), value @@ -361,6 +365,17 @@ public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedC } log.debug("Applying changes. New rank: {}", result.currentRank()); + if (result.previousRank().isPresent()) { + if (result.currentRank().rankId() < result.previousRank().get().rankId()) { + var embed = getRankInfo(result.currentRank().rankId() - 1).isPresent() ? "rankDecrease" : "rankDecreaseMax"; + var message = new MessageCreateBuilder().addContent(member.getAsMention()) + .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).toMessageEmbed()) + .build(); + getBotChannel(guild).sendMessage(message).queue(); + return; + } + } + Optional lootbox; String reward = ""; if (result.currentRank().lootboxReward()) { @@ -403,6 +418,20 @@ public Lootbox getRandomLootbox() { } } + public Optional getRankByXp(int xp) { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement("SELECT * FROM ranks WHERE ? >= bound ORDER BY bound DESC LIMIT 1"); + statement.setInt(1, xp); + var result = statement.executeQuery(); + if (!result.next()) { + return Optional.empty(); + } + return getRankInfo(result.getInt("rank_id")); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public record Lootbox(int id, int xpReward, int karmaReward, int itemId){} private TextChannel getBotChannel(Guild guild) { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java index f02de01..6770991 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java @@ -1,4 +1,4 @@ package com.github.kaktushose.nplaybot.rank.model; -public record RankInfo(long roleId, String name, String color, int xpBound, boolean lootboxReward, int itemRewardId) { +public record RankInfo(int rankId, long roleId, String name, String color, int xpBound, boolean lootboxReward, int itemRewardId) { } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java index 9b1e7d4..ce056a1 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java @@ -6,7 +6,7 @@ import java.util.Map; import java.util.Optional; -public record XpChangeResult(boolean rankChanged, RankInfo currentRank, Optional nextRank, int currentXp) { +public record XpChangeResult(boolean rankChanged, Optional previousRank, RankInfo currentRank, Optional nextRank, int currentXp) { public Map getEmbedValues(UserSnowflake user) { var result = new HashMap() {{ diff --git a/src/main/resources/db/migration/V3.2.0__setup_items.sql b/src/main/resources/db/migration/V3.2.0__setup_items.sql index 1ffbe85..77e0626 100644 --- a/src/main/resources/db/migration/V3.2.0__setup_items.sql +++ b/src/main/resources/db/migration/V3.2.0__setup_items.sql @@ -85,3 +85,53 @@ BEGIN END LOOP; END; $$ LANGUAGE plpgsql; + +DROP FUNCTION set_xp; +DROP FUNCTION add_xp; +DROP FUNCTION add_random_xp; + +CREATE OR REPLACE FUNCTION set_xp(id BIGINT, new_xp INT) +RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank int) AS +$$ +DECLARE + old_rank INT; + new_rank INT; +BEGIN + SELECT INTO old_rank users.rank_id FROM users WHERE users.user_id = id; + IF new_xp < 0 THEN + new_xp := 0; + END IF; + UPDATE users SET xp = new_xp WHERE users.user_id = id; + SELECT INTO new_rank users.rank_id FROM users WHERE users.user_id = id; + + + SELECT INTO rank_changed old_rank <> new_rank; + SELECT INTO previous_rank old_rank; + SELECT INTO current_rank new_rank; + SELECT INTO next_rank new_rank + 1; + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION add_xp(id BIGINT, xp_to_add INT) +RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank INT, current_xp INT) AS +$$ +DECLARE +BEGIN + SELECT xp_to_add + users.xp INTO current_xp FROM users WHERE users.user_id = id; + SELECT INTO rank_changed, previous_rank, current_rank, next_rank * FROM set_xp(id, current_xp); + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION add_random_xp(id BIGINT) +RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank INT, current_xp INT) AS +$$ +DECLARE + xp INT; +BEGIN + SELECT get_random_xp INTO xp FROM get_random_xp(); + SELECT INTO rank_changed, previous_rank, current_rank, next_rank, current_xp * FROM add_xp(id, xp); + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; From e60bb53f29896782bf0d278b812571c56d2840d2 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Tue, 2 Jul 2024 13:53:57 +0200 Subject: [PATCH 60/66] check roles on every message --- .../com/github/kaktushose/nplaybot/rank/RankListener.java | 1 + .../com/github/kaktushose/nplaybot/rank/RankService.java | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index b5e85ec..2a91e8c 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -50,6 +50,7 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { return; } + rankService.updateRankRoles(event.getMember(), event.getGuild(), rankService.getUserInfo(author).currentRank()); rankService.increaseTotalMessageCount(); if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 3a0a76c..1d98eb5 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -357,7 +357,7 @@ public XpChangeResult setXp(UserSnowflake user, int value) { public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedCache embedCache) { log.debug("Checking for rank up: {}", member); - updateRankRoles(member, guild, result); + updateRankRoles(member, guild, result.currentRank()); if (!result.rankChanged()) { log.debug("Rank hasn't changed"); @@ -453,8 +453,8 @@ private TextChannel getBotChannel(Guild guild) { } } - public void updateRankRoles(Member member, Guild guild, XpChangeResult result) { - var validRole = guild.getRoleById(result.currentRank().roleId()); + public void updateRankRoles(Member member, Guild guild, RankInfo currentRank) { + var validRole = guild.getRoleById(currentRank.roleId()); var invalidRoles = getRankRoleIds().stream() .map(guild::getRoleById) .filter(it -> it != validRole) From 9d34489b16ea1f1c4ab51dc5b229e72404cab4e5 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Tue, 2 Jul 2024 13:58:59 +0200 Subject: [PATCH 61/66] add missing item emojis in texts --- .../com/github/kaktushose/nplaybot/rank/LootboxListener.java | 4 +++- .../java/com/github/kaktushose/nplaybot/rank/RankService.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java index a463729..d3670d9 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java @@ -72,8 +72,10 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { if (lootbox.itemId() >= 0) { bot.getDatabase().getItemService().createTransaction(user, lootbox.itemId(), bot.getGuild()); var item = bot.getDatabase().getItemService().getItem(lootbox.itemId()); + var emoji = bot.getDatabase().getItemService().getTypeEmoji(item.typeId()); event.getChannel().sendMessage(user.getAsMention()) - .and(event.getChannel().sendMessage(bot.getEmbedCache().getEmbed("onLootboxItem").injectValue("item", item.name()).toMessageCreateData())) + .and(event.getChannel().sendMessage(bot.getEmbedCache().getEmbed("onLootboxItem") + .injectValue("item", String.format("%s %s", emoji, item.name())).toMessageCreateData())) .queue(); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 1d98eb5..59803f0 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -387,7 +387,9 @@ public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedC if (result.currentRank().itemRewardId() > 0) { itemService.createTransaction(member, result.currentRank().itemRewardId(), guild); - reward = itemService.getItem(result.currentRank().itemRewardId()).name(); + var item = itemService.getItem(result.currentRank().itemRewardId()); + var emoji = bot.getDatabase().getItemService().getTypeEmoji(item.typeId()); + reward = String.format("%s %s", emoji, item.name()); } var embed = result.nextRank().isPresent() ? "rankIncrease" : "rankIncreaseMax"; From f7a9a0ba5d85cc60d8a7c7e32d8c93dbf5c11f47 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Tue, 2 Jul 2024 14:10:00 +0200 Subject: [PATCH 62/66] move bot_token to env --- .env.example | 1 + .../github/kaktushose/nplaybot/Bootstrapper.java | 2 +- .../java/com/github/kaktushose/nplaybot/Bot.java | 14 +++++++------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index a4573c8..0ae90c4 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,4 @@ POSTGRES_PASSWORD=password POSTGRES_URL=jdbc:postgresql://postgres:5432/database GF_SECURITY_ADMIN_PASSWORD=password BOT_GUILD=0123456789 +BOT_TOKEN=bot_token diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java index 6262b7b..1eecdb3 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bootstrapper.java @@ -15,7 +15,7 @@ public static void main(String[] args) { try { log.info("Starting NPLAY-Bot..."); - var bot = Bot.start(Long.parseLong(System.getenv("BOT_GUILD"))); + var bot = Bot.start(Long.parseLong(System.getenv("BOT_GUILD")), System.getenv("BOT_TOKEN")); Thread.setDefaultUncaughtExceptionHandler((t, e) -> log.error("An uncaught exception has occurred!", e)); Runtime.getRuntime().addShutdownHook(new Thread(bot::shutdown)); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index e43cf45..9d706ae 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -31,7 +31,7 @@ public class Bot { private final Guild guild; @SuppressWarnings("DataFlowIssue") - private Bot(long guildId) throws InterruptedException, RuntimeException { + private Bot(long guildId, String token) throws InterruptedException, RuntimeException { try { database = new Database(this); } catch (Exception e) { @@ -40,7 +40,7 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { embedCache = new EmbedCache("embeds.json"); - jda = JDABuilder.createDefault(database.getSettingsService().getBotToken(guildId)) + jda = JDABuilder.createDefault(token) .enableIntents( GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES, @@ -59,21 +59,21 @@ private Bot(long guildId) throws InterruptedException, RuntimeException { ) .build().awaitReady(); - database.getRankService().indexMembers(jda.getGuildById(guildId)); + guild = jda.getGuildById(guildId); + + database.getRankService().indexMembers(guild); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); jdaCommands.getDependencyInjector().registerProvider(this); jdaCommands.getImplementationRegistry().setPermissionsProvider(new CustomPermissionsProvider(database)); - guild = jda.getGuildById(guildId); - jda.getPresence().setPresence(OnlineStatus.ONLINE, Activity.customStatus("Version 3.0.0")); taskScheduler = new TaskScheduler(this); } - public static Bot start(long guildId) throws InterruptedException { - return new Bot(guildId); + public static Bot start(long guildId, String token) throws InterruptedException { + return new Bot(guildId, token); } public void shutdown() { From 42b4bc97c1f57f73e256a383117db14d7d7ff820 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Tue, 2 Jul 2024 16:21:13 +0200 Subject: [PATCH 63/66] refactor database code --- .../com/github/kaktushose/nplaybot/Bot.java | 31 +- .../github/kaktushose/nplaybot/Database.java | 57 ++- .../events/collect/CollectEventCommands.java | 8 +- .../events/collect/CollectEventListener.java | 10 +- .../events/collect/CollectEventService.java | 15 +- .../events/contest/ContestCommands.java | 2 +- .../events/contest/ContestEventService.java | 11 +- .../events/contest/ContestListener.java | 22 +- .../nplaybot/items/AddItemCommand.java | 2 +- .../nplaybot/items/ItemExpirationTask.java | 10 +- .../nplaybot/items/ItemService.java | 11 +- .../nplaybot/items/RemoveItemCommand.java | 2 +- .../nplaybot/karma/KarmaConfigCommands.java | 14 +- .../nplaybot/karma/KarmaListener.java | 21 +- .../nplaybot/karma/KarmaService.java | 48 +- .../permissions/PermissionsService.java | 3 +- .../nplaybot/rank/LootboxListener.java | 6 +- .../nplaybot/rank/RankDecayTask.java | 19 +- .../nplaybot/rank/RankListener.java | 21 +- .../kaktushose/nplaybot/rank/RankService.java | 127 +++-- .../rank/commands/ModifyXpCommands.java | 4 +- .../rank/commands/RankConfigCommands.java | 24 +- .../rank/commands/RankInfoCommand.java | 5 +- .../nplaybot/settings/SettingsService.java | 26 +- .../nplaybot/starboard/StarboardListener.java | 14 +- .../nplaybot/starboard/StarboardService.java | 11 +- .../db/migration/V1.0.0__setup_nplaybot.sql | 468 ++++++++++++++++++ .../migration/V1.0.0__setup_rank_system.sql | 198 -------- .../migration/V2.0.0__setup_event_system.sql | 46 -- .../V2.1.0__add_role_permissions.sql | 23 - .../migration/V3.0.0__setup_karma_system.sql | 18 - .../db/migration/V3.1.0__add_starboard.sql | 12 - .../db/migration/V3.2.0__setup_items.sql | 137 ----- 33 files changed, 764 insertions(+), 662 deletions(-) create mode 100644 src/main/resources/db/migration/V1.0.0__setup_nplaybot.sql delete mode 100644 src/main/resources/db/migration/V1.0.0__setup_rank_system.sql delete mode 100644 src/main/resources/db/migration/V2.0.0__setup_event_system.sql delete mode 100644 src/main/resources/db/migration/V2.1.0__add_role_permissions.sql delete mode 100644 src/main/resources/db/migration/V3.0.0__setup_karma_system.sql delete mode 100644 src/main/resources/db/migration/V3.1.0__add_starboard.sql delete mode 100644 src/main/resources/db/migration/V3.2.0__setup_items.sql diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 9d706ae..2aa4948 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -32,12 +32,6 @@ public class Bot { @SuppressWarnings("DataFlowIssue") private Bot(long guildId, String token) throws InterruptedException, RuntimeException { - try { - database = new Database(this); - } catch (Exception e) { - throw new RuntimeException("Unable to connect to database!", e); - } - embedCache = new EmbedCache("embeds.json"); jda = JDABuilder.createDefault(token) @@ -49,19 +43,26 @@ private Bot(long guildId, String token) throws InterruptedException, RuntimeExce .setMemberCachePolicy(MemberCachePolicy.ALL) .setActivity(Activity.customStatus("starting...")) .setStatus(OnlineStatus.IDLE) - .addEventListeners( - new RankListener(database, embedCache, this), - new JoinLeaveListener(database.getRankService()), - new ContestListener(database.getContestEventService()), - new CollectEventListener(database, embedCache), - new KarmaListener(database, embedCache), - new StarboardListener(database, embedCache) - ) .build().awaitReady(); guild = jda.getGuildById(guildId); - database.getRankService().indexMembers(guild); + try { + database = new Database(this); + } catch (Exception e) { + throw new RuntimeException("Unable to connect to database!", e); + } + + jda.addEventListener( + new RankListener(database, embedCache, this), + new JoinLeaveListener(database.getRankService()), + new ContestListener(database.getContestEventService()), + new CollectEventListener(database, embedCache), + new KarmaListener(database, embedCache), + new StarboardListener(database, embedCache) + ); + + database.getRankService().indexMembers(); jdaCommands = JDACommands.start(jda, Bot.class, "com.github.kaktushose.nplaybot"); jdaCommands.getDependencyInjector().registerProvider(this); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index 72e3d0a..bfb2fef 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -11,6 +11,10 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; + +import java.sql.Connection; +import java.sql.SQLException; public class Database { @@ -34,14 +38,53 @@ public Database(Bot bot) { dataSource = new HikariDataSource(config); - settingsService = new SettingsService(dataSource); - itemService = new ItemService(dataSource); + initialSetup(bot.getGuild().getIdLong()); + + settingsService = new SettingsService(dataSource, bot); + itemService = new ItemService(dataSource, bot); rankService = new RankService(dataSource, itemService, bot); - contestEventService = new ContestEventService(dataSource); - collectEventService = new CollectEventService(dataSource); - permissionsService = new PermissionsService(dataSource); - karmaService = new KarmaService(dataSource, rankService, itemService); - starboardService = new StarboardService(dataSource); + contestEventService = new ContestEventService(dataSource, bot); + collectEventService = new CollectEventService(dataSource, bot); + permissionsService = new PermissionsService(dataSource, bot); + karmaService = new KarmaService(dataSource, rankService, itemService, bot); + starboardService = new StarboardService(dataSource, bot); + } + + private void initialSetup(long guildId) { + try (Connection connection = dataSource.getConnection()) { + // try to query bot_settings for current guild + var statement = connection.prepareStatement("SELECT * FROM bot_settings WHERE guild_id = ?"); + statement.setLong(1, guildId); + var result = statement.executeQuery(); + + // if present, guild is already setup + if (result.next()) { + return; + } + + //initialize all settings table with default values + statement = connection.prepareStatement("INSERT INTO bot_settings VALUES(?) ON CONFLICT DO NOTHING"); + statement.setLong(1, guildId); + statement.execute(); + + statement = connection.prepareStatement("INSERT INTO rank_settings VALUES(?) ON CONFLICT DO NOTHING"); + statement.setLong(1, guildId); + statement.execute(); + + statement = connection.prepareStatement("INSERT INTO event_settings VALUES(?) ON CONFLICT DO NOTHING"); + statement.setLong(1, guildId); + statement.execute(); + + statement = connection.prepareStatement("INSERT INTO karma_settings VALUES(?) ON CONFLICT DO NOTHING"); + statement.setLong(1, guildId); + statement.execute(); + + statement = connection.prepareStatement("INSERT INTO starboard_settings VALUES(?) ON CONFLICT DO NOTHING"); + statement.setLong(1, guildId); + statement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } } public void closeDataSource() { diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java index 109f7f9..ea0d34b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventCommands.java @@ -27,23 +27,23 @@ public void onCollectEventStart(CommandEvent event, @Param("Der Name des Events") String eventName, @Param("Der Name der Währung die gesammelt werden soll, z.B. \"Schneemänner\"") String currencyName, @Param("Die Emoji-Repräsentation der Währung, die gesammelt werden soll") String emoji) { - if (database.getCollectEventService().isActive(event.getGuild())) { + if (database.getCollectEventService().isActive()) { event.reply(embedCache.getEmbed("collectEventStartError")); return; } - database.getCollectEventService().startCollectEvent(event.getGuild(), eventName, currencyName, emoji); + database.getCollectEventService().startCollectEvent(eventName, currencyName, emoji); event.reply(embedCache.getEmbed("collectEventStart").injectValue("name", eventName)); } @SlashCommand(value = "events collect-event stop", desc = "Stoppt das aktuelle Collect Event", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onCollectEventStop(CommandEvent event) { - database.getCollectEventService().stopCollectEvent(event.getGuild()); + database.getCollectEventService().stopCollectEvent(); event.reply(embedCache.getEmbed("collectEventStop")); } @SlashCommand(value = "events set collect-loot-chance", desc = "Legt die Wahrscheinlichkeit für zufällige Collect-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetXpLootDropChance(CommandEvent event, @Param("Die Wahrscheinlichkeit in Prozent") @Min(1) @Max(100) double chance) { - database.getCollectEventService().updateCollectLootChance(event.getGuild(), chance); + database.getCollectEventService().updateCollectLootChance(chance); event.reply(embedCache.getEmbed("collectLootChanceUpdate").injectValue("chance", chance)); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java index 87dea88..a9f899d 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventListener.java @@ -52,7 +52,7 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { return; } - if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { + if (!rankService.isValidChannel(event.getChannel())) { return; } @@ -80,7 +80,7 @@ private void onCollectPointChange(int oldPoints, int newPoints, Member member, G if (reward.xp() > 0) { var xpChangeResult = rankService.addXp(member, reward.xp()); - rankService.onXpChange(xpChangeResult, member, guild, embedCache); + rankService.onXpChange(xpChangeResult, member, embedCache); } if (reward.roleId() > 0) { @@ -90,7 +90,7 @@ private void onCollectPointChange(int oldPoints, int newPoints, Member member, G var builder = new MessageCreateBuilder().addContent(member.getAsMention()) .addEmbeds(EmbedBuilder.fromData(DataObject.fromJson(reward.embed())).build()) .build(); - settingsService.getBotChannel(guild).sendMessage(builder).queue(); + settingsService.getBotChannel().sendMessage(builder).queue(); } private void onCollectLootDrop(MessageReceivedEvent event) { @@ -102,7 +102,7 @@ private void onCollectLootDrop(MessageReceivedEvent event) { } collectLootDrops.add(event.getMessageIdLong()); - event.getMessage().addReaction(Emoji.fromUnicode(eventService.getCollectCurrency(event.getGuild()).emoji())).queue(); + event.getMessage().addReaction(Emoji.fromUnicode(eventService.getCollectCurrency().emoji())).queue(); } @Override @@ -115,7 +115,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { if (!collectLootDrops.contains(messageId)) { return; } - var currency = eventService.getCollectCurrency(event.getGuild()); + var currency = eventService.getCollectCurrency(); if (!event.getEmoji().equals(Emoji.fromUnicode(currency.emoji()))) { return; } diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java index abdf815..fa8cc7d 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/collect/CollectEventService.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot.events.collect; +import com.github.kaktushose.nplaybot.Bot; import com.github.kaktushose.nplaybot.events.contest.ContestEventService; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; @@ -19,12 +20,14 @@ public class CollectEventService { private static final Logger log = LoggerFactory.getLogger(ContestEventService.class); private final DataSource dataSource; + private final Guild guild; - public CollectEventService(DataSource dataSource) { + public CollectEventService(DataSource dataSource, Bot bot) { this.dataSource = dataSource; + this.guild = bot.getGuild(); } - public boolean isActive(Guild guild) { + public boolean isActive() { log.debug("Querying collect event active flag for guild {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -43,7 +46,7 @@ public boolean isActive(Guild guild) { } } - public void startCollectEvent(Guild guild, String eventName, String currencyName, String emoji) { + public void startCollectEvent(String eventName, String currencyName, String emoji) { log.debug("Starting new collect event [{}, {}, {}] for guild {}", eventName, currencyName, emoji, guild); try (Connection connection = dataSource.getConnection()) { connection.prepareStatement("UPDATE users SET collect_points = 0").execute(); @@ -69,7 +72,7 @@ public void startCollectEvent(Guild guild, String eventName, String currencyName } } - public void stopCollectEvent(Guild guild) { + public void stopCollectEvent() { log.debug("Stopping collect event for guild {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -101,7 +104,7 @@ public void createCollectReward(String name, int threshold, int xp, Role role, S } } - public void updateCollectLootChance(Guild guild, double chance) { + public void updateCollectLootChance(double chance) { log.debug("Updating collect loot chance for guild: {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -134,7 +137,7 @@ public int getCollectLootDrop(Message message) { } } - public CollectCurrency getCollectCurrency(Guild guild) { + public CollectCurrency getCollectCurrency() { log.debug("Querying collect currency emoji"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java index cdb9c6e..3273a0f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestCommands.java @@ -34,7 +34,7 @@ public void onContestEventStart(CommandEvent event, @SlashCommand(value = "events contest-event stop", desc = "Stoppt das aktuelle Contest-Event und zeigt die Gewinner an", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) public void onContestEventStop(CommandEvent event) { - var result = database.getContestEventService().stopContestEvent(event.getGuild()); + var result = database.getContestEventService().stopContestEvent(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < result.size(); i++) { var row = result.get(i); diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java index 31253ff..10735ed 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestEventService.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot.events.contest; +import com.github.kaktushose.nplaybot.Bot; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; @@ -16,12 +17,14 @@ public class ContestEventService { private static final Logger log = LoggerFactory.getLogger(ContestEventService.class); private final DataSource dataSource; + private final Guild guild; - public ContestEventService(DataSource dataSource) { + public ContestEventService(DataSource dataSource, Bot bot) { this.dataSource = dataSource; + this.guild = bot.getGuild(); } - public long getContestEventChannel(Guild guild) { + public long getContestEventChannel() { log.debug("Querying contest event channel"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -40,7 +43,7 @@ public long getContestEventChannel(Guild guild) { } } - public String getVoteEmoji(Guild guild) { + public String getVoteEmoji() { log.debug("Querying vote emoji"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -81,7 +84,7 @@ public void startContestEvent(TextChannel channel, String emoji) { } } - public List stopContestEvent(Guild guild) { + public List stopContestEvent() { log.debug("Stopping current contest event"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" diff --git a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java index 4302b73..a624f3e 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/events/contest/ContestListener.java @@ -26,7 +26,7 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (!event.isFromGuild()) { return; } - if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel()) { return; } if (event.getAuthor().isBot()) { @@ -35,7 +35,7 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (eventService.createContestEntry(event.getMessage())) { log.debug("Created new contest entry: {}", event.getMessage()); - event.getMessage().addReaction(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild()))).queue(); + event.getMessage().addReaction(Emoji.fromFormatted(eventService.getVoteEmoji())).queue(); } else { log.debug("User already has a contest entry, deleting message"); event.getMessage().delete().queue(); @@ -49,7 +49,7 @@ public void onMessageDelete(@NotNull MessageDeleteEvent event) { @Override public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { - if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel()) { return; } if (event.getUser().isBot()) { @@ -60,7 +60,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { event.retrieveMessage().flatMap(message -> message.removeReaction(event.getEmoji(), event.getUser())).queue(); return; } - if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild())))) { + if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji()))) { log.debug("Removing invalid emoji from contest entry"); event.retrieveMessage().flatMap(message -> message.removeReaction(event.getEmoji(), event.getUser())).queue(); return; @@ -70,13 +70,13 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { @Override public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { - if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel()) { return; } if (event.getUser().isBot()) { return; } - if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild())))) { + if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji()))) { return; } eventService.decreaseVoteCount(event.getMessageIdLong(), event.getUserIdLong()); @@ -84,27 +84,27 @@ public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { @Override public void onMessageReactionRemoveEmoji(@NotNull MessageReactionRemoveEmojiEvent event) { - if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel()) { return; } - if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild())))) { + if (!event.getEmoji().equals(Emoji.fromFormatted(eventService.getVoteEmoji()))) { return; } log.debug("Detected removal of all vote emojis. Adding initial emoji again"); event.getChannel().retrieveMessageById(event.getMessageId()).flatMap(message -> - message.addReaction(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild()))) + message.addReaction(Emoji.fromFormatted(eventService.getVoteEmoji())) ).queue(); } @Override public void onMessageReactionRemoveAll(@NotNull MessageReactionRemoveAllEvent event) { - if (event.getChannel().getIdLong() != eventService.getContestEventChannel(event.getGuild())) { + if (event.getChannel().getIdLong() != eventService.getContestEventChannel()) { return; } log.debug("Detected removal of all vote emojis. Adding initial emoji again"); event.getChannel().retrieveMessageById(event.getMessageId()).flatMap(message -> - message.addReaction(Emoji.fromFormatted(eventService.getVoteEmoji(event.getGuild()))) + message.addReaction(Emoji.fromFormatted(eventService.getVoteEmoji())) ).queue(); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/AddItemCommand.java b/src/main/java/com/github/kaktushose/nplaybot/items/AddItemCommand.java index 79dbd78..2c7209e 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/items/AddItemCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/items/AddItemCommand.java @@ -52,7 +52,7 @@ public void onItemAdd(CommandEvent event, Member target) { @StringSelectMenu("Wähle ein Item aus") @SelectOption(label = "dummy option", value = "dummy option") public void onItemAddSelect(ComponentEvent event, List selection) { - selection.forEach(id -> database.getItemService().createTransaction(target, Integer.parseInt(id), event.getGuild())); + selection.forEach(id -> database.getItemService().createTransaction(target, Integer.parseInt(id))); event.reply(embedCache.getEmbed("itemAdd")); event.removeComponents(); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/ItemExpirationTask.java b/src/main/java/com/github/kaktushose/nplaybot/items/ItemExpirationTask.java index f066154..b125fee 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/items/ItemExpirationTask.java +++ b/src/main/java/com/github/kaktushose/nplaybot/items/ItemExpirationTask.java @@ -27,16 +27,12 @@ public void onCheckItems(Bot bot) { for (var transaction : transactions) { executor.schedule(() -> { - bot.getDatabase().getItemService().deleteTransaction( - UserSnowflake.fromId(transaction.userId()), - transaction.transactionId(), - bot.getGuild() - ); + bot.getDatabase().getItemService().deleteTransaction(UserSnowflake.fromId(transaction.userId()), transaction.transactionId()); if (transaction.isPlayActivity()) { var rankInfo = bot.getDatabase().getRankService().getUserInfo(UserSnowflake.fromId(transaction.userId())); if (rankInfo.karma() - rankInfo.lastKarma() >= PLAY_ACTIVITY_KARMA_THRESHOLD) { - bot.getDatabase().getItemService().addPlayActivity(UserSnowflake.fromId(transaction.userId()), bot.getGuild()); + bot.getDatabase().getItemService().addPlayActivity(UserSnowflake.fromId(transaction.userId())); bot.getDatabase().getItemService().updateLastKarma(UserSnowflake.fromId(transaction.userId())); messageUser(transaction, bot.getEmbedCache().getEmbed("playActivityRenew").toEmbedBuilder(), bot); @@ -60,7 +56,7 @@ private void messageUser(ItemService.Transaction transaction, EmbedBuilder embed var user = bot.getJda().getUserById(transaction.userId()); user.openPrivateChannel().flatMap(privateChannel -> privateChannel.sendMessageEmbeds(embed.build())) .queue(null, new ErrorHandler().handle(ErrorResponse.CANNOT_SEND_TO_USER, exception -> { - TextChannel channel = bot.getDatabase().getSettingsService().getBotChannel(bot.getGuild()); + TextChannel channel = bot.getDatabase().getSettingsService().getBotChannel(); channel.sendMessage(user.getAsMention()).and(channel.sendMessageEmbeds(embed.build())).queue(); })); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java index e3663fa..bf5716f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot.items; +import com.github.kaktushose.nplaybot.Bot; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.UserSnowflake; @@ -14,9 +15,11 @@ public class ItemService { private final DataSource dataSource; private static final int PLAY_ACTIVITY_ITEM_ID = 7; + private final Guild guild; - public ItemService(DataSource dataSource) { + public ItemService(DataSource dataSource, Bot bot) { this.dataSource = dataSource; + this.guild = bot.getGuild(); } public List getAllItems() { @@ -104,7 +107,7 @@ public List getTransactions(UserSnowflake user) { } } - public void createTransaction(UserSnowflake user, int itemId, Guild guild) { + public void createTransaction(UserSnowflake user, int itemId) { try (Connection connection = dataSource.getConnection()) { var item = getItem(itemId); @@ -143,7 +146,7 @@ public void createTransaction(UserSnowflake user, int itemId, Guild guild) { } } - public void deleteTransaction(UserSnowflake user, int transactionId, Guild guild) { + public void deleteTransaction(UserSnowflake user, int transactionId) { try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement("SELECT item_id FROM transactions WHERE transaction_id = ?"); statement.setInt(1, transactionId); @@ -185,7 +188,7 @@ public void updateLastKarma(UserSnowflake user) { } - public void addPlayActivity(UserSnowflake user, Guild guild) { + public void addPlayActivity(UserSnowflake user) { try (Connection connection = dataSource.getConnection()) { var item = getItem(PLAY_ACTIVITY_ITEM_ID); var statement = connection.prepareStatement("INSERT INTO transactions (\"user_id\", \"item_id\", \"expires_at\", \"is_play_activity\") VALUES(?, ?, ?, true)"); diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/RemoveItemCommand.java b/src/main/java/com/github/kaktushose/nplaybot/items/RemoveItemCommand.java index 8b4b67e..85ea173 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/items/RemoveItemCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/items/RemoveItemCommand.java @@ -50,7 +50,7 @@ public void onItemRemove(CommandEvent event, Member target) { @StringSelectMenu("Wähle ein oder mehrere Items aus") @SelectOption(label = "dummy option", value = "dummy option") public void onItemRemoveSelect(ComponentEvent event, List selection) { - selection.forEach(id -> database.getItemService().deleteTransaction(target, Integer.parseInt(id), event.getGuild())); + selection.forEach(id -> database.getItemService().deleteTransaction(target, Integer.parseInt(id))); event.reply(embedCache.getEmbed("itemDelete")); event.removeComponents(); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java index f64f70a..e741f3e 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaConfigCommands.java @@ -30,9 +30,9 @@ public void onAddKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE database.getKarmaService().addKarma(target, amount); if (newKarma > oldKarma) { - database.getKarmaService().onKarmaIncrease(oldKarma, newKarma, event.getMember(), event.getGuild(), embedCache); + database.getKarmaService().onKarmaIncrease(oldKarma, newKarma, event.getMember(), embedCache); } else if (newKarma < oldKarma) { - database.getKarmaService().onKarmaDecrease(oldKarma, newKarma, event.getMember(), event.getGuild(), embedCache); + database.getKarmaService().onKarmaDecrease(oldKarma, newKarma, event.getMember(), embedCache); } event.reply(embedCache.getEmbed("addKarmaResult") @@ -48,9 +48,9 @@ public void onSetKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE database.getKarmaService().setKarma(target, value); if (value > oldKarma) { - database.getKarmaService().onKarmaIncrease(oldKarma, value, event.getMember(), event.getGuild(), embedCache); + database.getKarmaService().onKarmaIncrease(oldKarma, value, event.getMember(), embedCache); } else if (value < oldKarma) { - database.getKarmaService().onKarmaDecrease(oldKarma, value, event.getMember(), event.getGuild(), embedCache); + database.getKarmaService().onKarmaDecrease(oldKarma, value, event.getMember(), embedCache); } event.reply(embedCache.getEmbed("setKarmaResult") @@ -62,19 +62,19 @@ public void onSetKarma(CommandEvent event, Member target, @Min(Integer.MIN_VALUE @SlashCommand(value = "karma-config set default-karma-tokens", desc = "Legt die tägliche Anzahl an Karma-Tokens für jeden Nutzer fest", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) @Permissions(BotPermissions.MANAGE_KARMA_SETTINGS) public void onSetKarmaTokens(CommandEvent event, @Min(1) @Max(Integer.MAX_VALUE) int value) { - database.getKarmaService().setDefaultTokens(event.getGuild(), value); + database.getKarmaService().setDefaultTokens(value); onGetKarmaConfig(event); } @SlashCommand(value = "karma-config display", desc = "Zeigt die Einstellungen für das Karma System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) @Permissions(BotPermissions.MANAGE_KARMA_SETTINGS) public void onGetKarmaConfig(CommandEvent event) { - var emojis = database.getKarmaService().getValidEmojis(event.getGuild()); + var emojis = database.getKarmaService().getValidEmojis(); var builder = new StringBuilder(); emojis.forEach(it -> builder.append(it.getFormatted()).append(" ")); event.reply(embedCache.getEmbed("karmaConfig") .injectValue("emojis", builder) - .injectValue("tokens", database.getKarmaService().getDefaultTokens(event.getGuild())) + .injectValue("tokens", database.getKarmaService().getDefaultTokens()) ); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java index bdd1775..360c662 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaListener.java @@ -2,22 +2,13 @@ import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.Database; -import com.github.kaktushose.nplaybot.items.ItemService; import com.github.kaktushose.nplaybot.rank.RankService; -import com.github.kaktushose.nplaybot.settings.SettingsService; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; -import net.dv8tion.jda.api.utils.data.DataObject; -import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.jetbrains.annotations.NotNull; -import static com.github.kaktushose.nplaybot.items.ItemExpirationTask.PLAY_ACTIVITY_KARMA_THRESHOLD; - public class KarmaListener extends ListenerAdapter { private final KarmaService karmaService; @@ -32,7 +23,7 @@ public KarmaListener(Database database, EmbedCache embedCache) { @Override public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { - if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { + if (!rankService.isValidChannel(event.getChannel())) { return; } if (event.getUser().isBot()) { @@ -41,23 +32,23 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { if (event.getUser().getIdLong() == event.getMessageAuthorIdLong()) { return; } - if (!karmaService.getValidEmojis(event.getGuild()).contains(event.getEmoji())) { + if (!karmaService.getValidEmojis().contains(event.getEmoji())) { return; } int oldKarma = rankService.getUserInfo(UserSnowflake.fromId(event.getMessageAuthorIdLong())).karma(); int newKarma = karmaService.onKarmaVoteAdd(event.getUser(), UserSnowflake.fromId(event.getMessageAuthorIdLong())); - event.retrieveMessage().queue(message -> karmaService.onKarmaIncrease(oldKarma, newKarma, message.getMember(), message.getGuild(), embedCache)); + event.retrieveMessage().queue(message -> karmaService.onKarmaIncrease(oldKarma, newKarma, message.getMember(), embedCache)); } @Override public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { - if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { + if (!rankService.isValidChannel(event.getChannel())) { return; } if (event.getUser().isBot()) { return; } - if (!karmaService.getValidEmojis(event.getGuild()).contains(event.getEmoji())) { + if (!karmaService.getValidEmojis().contains(event.getEmoji())) { return; } event.retrieveMessage().queue(message -> { @@ -66,7 +57,7 @@ public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { } int oldKarma = rankService.getUserInfo(message.getAuthor()).karma(); int newKarma = karmaService.onKarmaVoteRemove(event.getUser(), message.getAuthor()); - karmaService.onKarmaDecrease(oldKarma, newKarma, message.getMember(), message.getGuild(), embedCache); + karmaService.onKarmaDecrease(oldKarma, newKarma, message.getMember(), embedCache); }); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java index df98f34..01bc846 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java @@ -1,6 +1,7 @@ package com.github.kaktushose.nplaybot.karma; import com.github.kaktushose.jda.commands.data.EmbedCache; +import com.github.kaktushose.nplaybot.Bot; import com.github.kaktushose.nplaybot.items.ItemService; import com.github.kaktushose.nplaybot.rank.RankService; import net.dv8tion.jda.api.EmbedBuilder; @@ -26,13 +27,17 @@ public class KarmaService { private static final Logger log = LoggerFactory.getLogger(KarmaService.class); private final DataSource dataSource; + private final Bot bot; private final RankService rankService; private final ItemService itemService; + private final Guild guild; - public KarmaService(DataSource dataSource, RankService rankService, ItemService itemService) { + public KarmaService(DataSource dataSource, RankService rankService, ItemService itemService, Bot bot) { this.dataSource = dataSource; + this.bot = bot; this.rankService = rankService; this.itemService = itemService; + this.guild = bot.getGuild(); } public void setKarma(UserSnowflake user, int karma) { @@ -69,7 +74,7 @@ public void addKarma(UserSnowflake user, int karma) { } } - public void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild guild, EmbedCache embedCache) { + public void onKarmaIncrease(int oldKarma, int newKarma, Member member, EmbedCache embedCache) { // play activity role var rankInfo = rankService.getUserInfo(member); if (newKarma - rankInfo.lastKarma() >= PLAY_ACTIVITY_KARMA_THRESHOLD) { @@ -77,13 +82,13 @@ public void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild gui return; } - itemService.addPlayActivity(member, guild); + itemService.addPlayActivity(member); itemService.updateLastKarma(member); var builder = new MessageCreateBuilder().addContent(member.getAsMention()) .addEmbeds(embedCache.getEmbed("playActivityAdd").toMessageEmbed()) .build(); - getBotChannel(guild).sendMessage(builder).queue(); + bot.getDatabase().getSettingsService().getBotChannel().sendMessage(builder).queue(); } // karma rewards @@ -100,7 +105,7 @@ public void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild gui if (reward.xp() > 0) { var xpChangeResult = rankService.addXp(member, reward.xp()); - rankService.onXpChange(xpChangeResult, member, guild, embedCache); + rankService.onXpChange(xpChangeResult, member, embedCache); } if (reward.roleId() > 0) { @@ -110,10 +115,10 @@ public void onKarmaIncrease(int oldKarma, int newKarma, Member member, Guild gui var builder = new MessageCreateBuilder().addContent(member.getAsMention()) .addEmbeds(EmbedBuilder.fromData(DataObject.fromJson(reward.embed())).build()) .build(); - getBotChannel(guild).sendMessage(builder).queue(); + bot.getDatabase().getSettingsService().getBotChannel().sendMessage(builder).queue(); } - public void onKarmaDecrease(int oldKarma, int newKarma, Member member, Guild guild, EmbedCache embedCache) { + public void onKarmaDecrease(int oldKarma, int newKarma, Member member, EmbedCache embedCache) { var rewards = getKarmaRewards(); var optional = rewards.stream() .filter(it -> it.threshold() < oldKarma) @@ -127,7 +132,7 @@ public void onKarmaDecrease(int oldKarma, int newKarma, Member member, Guild gui if (reward.xp() > 0) { var xpChangeResult = rankService.addXp(member, -reward.xp()); - rankService.onXpChange(xpChangeResult, member, guild, embedCache); + rankService.onXpChange(xpChangeResult, member, embedCache); } if (reward.roleId() > 0) { @@ -141,26 +146,7 @@ public void onKarmaDecrease(int oldKarma, int newKarma, Member member, Guild gui .toEmbedBuilder() .build() ).build(); - getBotChannel(guild).sendMessage(builder).queue(); - } - - private TextChannel getBotChannel(Guild guild) { - log.debug("Querying bot channel"); - try (Connection connection = dataSource.getConnection()) { - var statement = connection.prepareStatement(""" - SELECT bot_channel_id - FROM guild_settings - WHERE guild_id = ? - """ - ); - statement.setLong(1, guild.getIdLong()); - - var result = statement.executeQuery(); - result.next(); - return guild.getTextChannelById(result.getLong(1)); - } catch (SQLException e) { - throw new RuntimeException(e); - } + bot.getDatabase().getSettingsService().getBotChannel().sendMessage(builder).queue(); } public void resetTokens() { @@ -244,7 +230,7 @@ public int getUserTokens(UserSnowflake user) { } } - public int getDefaultTokens(Guild guild) { + public int getDefaultTokens() { log.debug("Querying default karma tokens for guild {}", guild); try (var connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -263,7 +249,7 @@ public int getDefaultTokens(Guild guild) { } } - public void setDefaultTokens(Guild guild, int value) { + public void setDefaultTokens(int value) { log.debug("Setting default karma tokens for guild {}", guild); try (var connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -281,7 +267,7 @@ public void setDefaultTokens(Guild guild, int value) { } } - public List getValidEmojis(Guild guild) { + public List getValidEmojis() { log.debug("Querying valid karma emojis for guild {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" diff --git a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java index 4b74f3d..7ca8133 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/permissions/PermissionsService.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot.permissions; +import com.github.kaktushose.nplaybot.Bot; import net.dv8tion.jda.api.entities.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,7 +17,7 @@ public class PermissionsService { private static final Logger log = LoggerFactory.getLogger(PermissionsService.class); private final DataSource dataSource; - public PermissionsService(DataSource dataSource) { + public PermissionsService(DataSource dataSource, Bot bot) { this.dataSource = dataSource; } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java index d3670d9..0fae5d2 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/LootboxListener.java @@ -51,7 +51,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { if (lootbox.xpReward() > 0) { var result = bot.getDatabase().getRankService().addXp(user, lootbox.xpReward()); - bot.getDatabase().getRankService().onXpChange(result, event.getMember(), bot.getGuild(), bot.getEmbedCache()); + bot.getDatabase().getRankService().onXpChange(result, event.getMember(), bot.getEmbedCache()); event.getChannel().sendMessage(user.getAsMention()) .and(event.getChannel().sendMessage(bot.getEmbedCache().getEmbed("onLootboxXp").injectValue("xp", lootbox.xpReward()).toMessageCreateData())) .queue(); @@ -62,7 +62,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { var oldKarma = bot.getDatabase().getRankService().getUserInfo(user).karma(); var newKarma = oldKarma + lootbox.karmaReward(); bot.getDatabase().getKarmaService().addKarma(user, lootbox.karmaReward()); - bot.getDatabase().getKarmaService().onKarmaIncrease(oldKarma, newKarma, event.getMember(), bot.getGuild(), bot.getEmbedCache()); + bot.getDatabase().getKarmaService().onKarmaIncrease(oldKarma, newKarma, event.getMember(), bot.getEmbedCache()); event.getChannel().sendMessage(user.getAsMention()) .and(event.getChannel().sendMessage(bot.getEmbedCache().getEmbed("onLootboxKarma").injectValue("karma", lootbox.karmaReward()).toMessageCreateData())) .queue(); @@ -70,7 +70,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { } if (lootbox.itemId() >= 0) { - bot.getDatabase().getItemService().createTransaction(user, lootbox.itemId(), bot.getGuild()); + bot.getDatabase().getItemService().createTransaction(user, lootbox.itemId()); var item = bot.getDatabase().getItemService().getItem(lootbox.itemId()); var emoji = bot.getDatabase().getItemService().getTypeEmoji(item.typeId()); event.getChannel().sendMessage(user.getAsMention()) diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java index cd4f402..b42d9f1 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java @@ -8,27 +8,27 @@ public class RankDecayTask { - private static final int START_DECAY_RANK_ID = 2; - private static final int DECAY_XP_AMOUNT = 150; - @ScheduledTask(period = 7, unit = TimeUnit.DAYS) public void accept(Bot bot) { + var startDecayRankId = bot.getDatabase().getRankService().getStartDecayRankId(); + var decayXp = bot.getDatabase().getRankService().getDecayXp(); + bot.getGuild().loadMembers(member -> { var currentRank = bot.getDatabase().getRankService().getUserInfo(member); - if (currentRank.currentRank().rankId() < START_DECAY_RANK_ID) { + if (currentRank.currentRank().rankId() < startDecayRankId) { return; } - var userXpAfterDecay = currentRank.currentXp() - DECAY_XP_AMOUNT; + var userXpAfterDecay = currentRank.currentXp() - decayXp; var newRank = bot.getDatabase().getRankService().getRankByXp(userXpAfterDecay); XpChangeResult result; // if new rank is present if (newRank.isPresent()) { // check if we fall under the minimum rank and set xp to lower bound - if (newRank.get().rankId() < START_DECAY_RANK_ID) { - var minimumRank = bot.getDatabase().getRankService().getRankInfo(START_DECAY_RANK_ID); + if (newRank.get().rankId() < startDecayRankId) { + var minimumRank = bot.getDatabase().getRankService().getRankInfo(startDecayRankId); result = bot.getDatabase().getRankService().setXp(member, minimumRank.orElseThrow().xpBound()); // if we don't fall under the minimum rank just remove all xp as planned } else { @@ -36,11 +36,10 @@ public void accept(Bot bot) { } // if new rank isn't present we went too far and also set xp to lower bound } else { - var minimumRank = bot.getDatabase().getRankService().getRankInfo(START_DECAY_RANK_ID); + var minimumRank = bot.getDatabase().getRankService().getRankInfo(startDecayRankId); result = bot.getDatabase().getRankService().setXp(member, minimumRank.orElseThrow().xpBound()); } - bot.getDatabase().getRankService().onXpChange(result, member, bot.getGuild(), bot.getEmbedCache()); + bot.getDatabase().getRankService().onXpChange(result, member, bot.getEmbedCache()); }); } - } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index 2a91e8c..badc846 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -19,18 +19,14 @@ public class RankListener extends ListenerAdapter { - private static final int LOOTBOX_CHANCE = 70; - private static final int LOOTBOX_RETRIEVE_LIMIT = 30; private static final Logger log = LoggerFactory.getLogger(RankListener.class); private final RankService rankService; - private final SettingsService settingsService; private final EmbedCache embedCache; private final Bot bot; private final Map xpLootDrops; public RankListener(Database database, EmbedCache embedCache, Bot bot) { this.rankService = database.getRankService(); - this.settingsService = database.getSettingsService(); this.embedCache = embedCache; this.bot = bot; xpLootDrops = new HashMap<>(); @@ -50,10 +46,10 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { return; } - rankService.updateRankRoles(event.getMember(), event.getGuild(), rankService.getUserInfo(author).currentRank()); + rankService.updateRankRoles(event.getMember(), rankService.getUserInfo(author).currentRank()); rankService.increaseTotalMessageCount(); - if (!rankService.isValidChannel(event.getChannel(), event.getGuild())) { + if (!rankService.isValidChannel(event.getChannel())) { return; } @@ -70,11 +66,14 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { } private void onCheckForLootbox(MessageReceivedEvent event) { - if (ThreadLocalRandom.current().nextInt(100) >= LOOTBOX_CHANCE) { + var lootboxChance = rankService.getLootboxChance(); + var lootboxQueryLimit = rankService.getLootboxQueryLimit(); + + if (ThreadLocalRandom.current().nextDouble(100) >= lootboxChance) { return; } - event.getChannel().getHistory().retrievePast(LOOTBOX_RETRIEVE_LIMIT).queue(messages -> { - var message = messages.get(ThreadLocalRandom.current().nextInt(LOOTBOX_RETRIEVE_LIMIT)); + event.getChannel().getHistory().retrievePast(lootboxQueryLimit).queue(messages -> { + var message = messages.get(ThreadLocalRandom.current().nextInt(lootboxQueryLimit)); var lootbox = rankService.getRandomLootbox(); LootboxListener.newListener(bot, lootbox, event.getMember(), message); }); @@ -84,7 +83,7 @@ private void onAddRegularXp(MessageReceivedEvent event) { rankService.updateValidMessage(event.getAuthor()); var result = rankService.addRandomXp(event.getAuthor()); - rankService.onXpChange(result, event.getMember(), event.getGuild(), embedCache); + rankService.onXpChange(result, event.getMember(), embedCache); } private void onXpLootDrop(MessageReceivedEvent event) { @@ -117,7 +116,7 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { var xp = xpLootDrops.get(messageId); var result = rankService.addXp(event.getMember(), xp); - rankService.onXpChange(result, event.getMember(), event.getGuild(), embedCache); + rankService.onXpChange(result, event.getMember(), embedCache); xpLootDrops.remove(messageId); event.retrieveMessage().queue(message -> { diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 59803f0..7470f08 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -12,7 +12,6 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.UserSnowflake; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import org.slf4j.Logger; @@ -29,11 +28,13 @@ public class RankService { private final DataSource dataSource; private final ItemService itemService; private final Bot bot; + private final Guild guild; public RankService(DataSource dataSource, ItemService itemService, Bot bot) { this.dataSource = dataSource; this.itemService = itemService; this.bot = bot; + this.guild = bot.getGuild(); } public void createUser(UserSnowflake user) { @@ -58,7 +59,7 @@ public void removeUser(UserSnowflake user) { } } - public void indexMembers(Guild guild) { + public void indexMembers() { guild.loadMembers(member -> createUser(member.getUser())); } @@ -123,7 +124,7 @@ public UserInfo getUserInfo(UserSnowflake user) { } } - public RankConfig getRankConfig(Guild guild) { + public RankConfig getRankConfig() { log.debug("Querying rank config for guild: {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -147,7 +148,7 @@ public RankConfig getRankConfig(Guild guild) { } } - public void updateCooldown(Guild guild, int cooldown) { + public void updateCooldown(int cooldown) { log.debug("Updating cooldown for guild: {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -165,7 +166,7 @@ public void updateCooldown(Guild guild, int cooldown) { } } - public void updateMinMessageLength(Guild guild, int length) { + public void updateMinMessageLength(int length) { log.debug("Updating minimum message length for guild: {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -183,7 +184,7 @@ public void updateMinMessageLength(Guild guild, int length) { } } - public void updateXpLootChance(Guild guild, double chance) { + public void updateXpLootChance(double chance) { log.debug("Updating xp loot chance for guild: {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -201,7 +202,7 @@ public void updateXpLootChance(Guild guild, double chance) { } } - public Set getValidChannels(Guild guild) { + public Set getValidChannels() { log.debug("Querying valid channels for guild {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -220,7 +221,7 @@ public Set getValidChannels(Guild guild) { } } - public void updateValidChannels(Guild guild, Set validChannels) { + public void updateValidChannels(Set validChannels) { log.debug("Querying valid channels for guild {}", guild); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -355,9 +356,9 @@ public XpChangeResult setXp(UserSnowflake user, int value) { } } - public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedCache embedCache) { + public void onXpChange(XpChangeResult result, Member member, EmbedCache embedCache) { log.debug("Checking for rank up: {}", member); - updateRankRoles(member, guild, result.currentRank()); + updateRankRoles(member, result.currentRank()); if (!result.rankChanged()) { log.debug("Rank hasn't changed"); @@ -371,13 +372,13 @@ public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedC var message = new MessageCreateBuilder().addContent(member.getAsMention()) .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).toMessageEmbed()) .build(); - getBotChannel(guild).sendMessage(message).queue(); + bot.getDatabase().getSettingsService().getBotChannel().sendMessage(message).queue(); return; } } Optional lootbox; - String reward = ""; + String reward = "keine Belohnung"; if (result.currentRank().lootboxReward()) { lootbox = Optional.of(getRandomLootbox()); reward = "\uD83C\uDF81 eine Lootbox"; @@ -386,7 +387,7 @@ public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedC } if (result.currentRank().itemRewardId() > 0) { - itemService.createTransaction(member, result.currentRank().itemRewardId(), guild); + itemService.createTransaction(member, result.currentRank().itemRewardId()); var item = itemService.getItem(result.currentRank().itemRewardId()); var emoji = bot.getDatabase().getItemService().getTypeEmoji(item.typeId()); reward = String.format("%s %s", emoji, item.name()); @@ -397,7 +398,9 @@ public void onXpChange(XpChangeResult result, Member member, Guild guild, EmbedC .addEmbeds(embedCache.getEmbed(embed).injectValues(result.getEmbedValues(member)).injectValue("reward", reward).toMessageEmbed()) .build(); - getBotChannel(guild).sendMessage(message).queue(msg -> lootbox.ifPresent(it -> LootboxListener.newListener(bot, it, member, msg))); + bot.getDatabase().getSettingsService().getBotChannel().sendMessage(message).queue(msg -> + lootbox.ifPresent(it -> LootboxListener.newListener(bot, it, member, msg)) + ); } public Lootbox getRandomLootbox() { @@ -436,26 +439,7 @@ public Optional getRankByXp(int xp) { public record Lootbox(int id, int xpReward, int karmaReward, int itemId){} - private TextChannel getBotChannel(Guild guild) { - log.debug("Querying bot channel"); - try (Connection connection = dataSource.getConnection()) { - var statement = connection.prepareStatement(""" - SELECT bot_channel_id - FROM guild_settings - WHERE guild_id = ? - """ - ); - statement.setLong(1, guild.getIdLong()); - - var result = statement.executeQuery(); - result.next(); - return guild.getTextChannelById(result.getLong(1)); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - public void updateRankRoles(Member member, Guild guild, RankInfo currentRank) { + public void updateRankRoles(Member member, RankInfo currentRank) { var validRole = guild.getRoleById(currentRank.roleId()); var invalidRoles = getRankRoleIds().stream() .map(guild::getRoleById) @@ -591,7 +575,7 @@ public int getXpLootDrop(Message message) { } } - public boolean isValidChannel(MessageChannelUnion channel, Guild guild) { + public boolean isValidChannel(MessageChannelUnion channel) { log.debug("Checking channel: {}", channel); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -623,4 +607,77 @@ public boolean isValidChannel(MessageChannelUnion channel, Guild guild) { throw new RuntimeException(e); } } + + public int getStartDecayRankId() { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT rank_decay_start + FROM rank_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getDecayXp() { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT rank_decay_xp_loss + FROM rank_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getLootboxChance() { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT lootbox_chance + FROM rank_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public int getLootboxQueryLimit() { + try (Connection connection = dataSource.getConnection()) { + var statement = connection.prepareStatement(""" + SELECT lootbox_query_limit + FROM rank_settings + WHERE guild_id = ? + """ + ); + statement.setLong(1, guild.getIdLong()); + + var result = statement.executeQuery(); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java index 3e08a25..4421363 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/ModifyXpCommands.java @@ -35,7 +35,7 @@ public void onAddXp(CommandEvent event, Member target, @Min(1) @Max(Integer.MAX_ .injectValue("xp", amount) ); - database.getRankService().onXpChange(result, event.getMember(), event.getGuild(), embedCache); + database.getRankService().onXpChange(result, event.getMember(), embedCache); } @SlashCommand(value = "balance set xp", desc = "Setzt die XP von einem User auf den angegebenen Wert", enabledFor = Permission.BAN_MEMBERS, isGuildOnly = true) @@ -47,6 +47,6 @@ public void onSetXp(CommandEvent event, Member target, @Min(0) @Max(Integer.MAX_ .injectValue("xp", value) ); - database.getRankService().onXpChange(result, event.getMember(), event.getGuild(), embedCache); + database.getRankService().onXpChange(result, event.getMember(), embedCache); } } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java index f22e6ab..0b81f5b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankConfigCommands.java @@ -25,30 +25,30 @@ public class RankConfigCommands { @SlashCommand(value = "rank-config display", desc = "Zeigt die Einstellungen für das Rank System an", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onGetRankConfig(CommandEvent event) { - event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); + event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig())); } @SlashCommand(value = "rank-config set cooldown", desc = "Legt den Cooldown für gewertete Nachrichten fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetCooldown(CommandEvent event, @Param("Die Dauer in Millisekunden") @Min(0) @Max(Integer.MAX_VALUE) int cooldown) { - database.getRankService().updateCooldown(event.getGuild(), cooldown); - event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); + database.getRankService().updateCooldown(cooldown); + event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig())); } @SlashCommand(value = "rank-config set message-length", desc = "Legt die Mindestlänge für gewertete Nachrichten fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetMinMessageLength(CommandEvent event, @Param("Die Mindestanzahl an Buchstaben pro Nachricht") @Min(0) @Max(Integer.MAX_VALUE) int length) { - database.getRankService().updateMinMessageLength(event.getGuild(), length); - event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); + database.getRankService().updateMinMessageLength(length); + event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig())); } @SlashCommand(value = "rank-config set xp-loot-chance", desc = "Legt die Wahrscheinlichkeit für zufällige XP-Loot-Drops fest", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onSetXpLootDropChance(CommandEvent event, @Param("Die Wahrscheinlichkeit in Prozent") @Min(1) @Max(100) double chance) { - database.getRankService().updateXpLootChance(event.getGuild(), chance); - event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig(event.getGuild()))); + database.getRankService().updateXpLootChance(chance); + event.reply(embedCache.getEmbed("rankConfig").injectFields(database.getRankService().getRankConfig())); } @SlashCommand(value = "rank-config valid-channels list", desc = "Zeigt die Textkanäle an, in denen Nachrichten gewertet werden", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onValidChannelsList(CommandEvent event) { - var channels = database.getRankService().getValidChannels(event.getGuild()); + var channels = database.getRankService().getValidChannels(); StringBuilder result = new StringBuilder(); channels.forEach(it -> result.append(String.format("<#%d>", it)).append("\n")); event.reply(embedCache.getEmbed("validChannels").injectValue("channels", result)); @@ -56,9 +56,9 @@ public void onValidChannelsList(CommandEvent event) { @SlashCommand(value = "rank-config valid-channels add", desc = "Fügt einen Textkanal zu der Liste der gewerteten Kanäle hinzu", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onValidChannelsAdd(CommandEvent event, @Param("Der Kanal der gewertet werden soll") TextChannel channel) { - var channels = database.getRankService().getValidChannels(event.getGuild()); + var channels = database.getRankService().getValidChannels(); channels.add(channel.getIdLong()); - database.getRankService().updateValidChannels(event.getGuild(), channels); + database.getRankService().updateValidChannels(channels); StringBuilder result = new StringBuilder(); channels.forEach(it -> result.append(String.format("<#%d>", it)).append("\n")); event.reply(embedCache.getEmbed("validChannels").injectValue("channels", result)); @@ -66,9 +66,9 @@ public void onValidChannelsAdd(CommandEvent event, @Param("Der Kanal der gewerte @SlashCommand(value = "rank-config valid-channels remove", desc = "Entfernt einen Textkanal von der Liste der gewerteten Kanäle", isGuildOnly = true, enabledFor = Permission.BAN_MEMBERS) public void onValidChannelsRemove(CommandEvent event, @Param("Der Kanal der nicht mehr gewertet werden soll") TextChannel channel) { - var channels = database.getRankService().getValidChannels(event.getGuild()); + var channels = database.getRankService().getValidChannels(); channels.remove(channel.getIdLong()); - database.getRankService().updateValidChannels(event.getGuild(), channels); + database.getRankService().updateValidChannels(channels); StringBuilder result = new StringBuilder(); channels.forEach(it -> result.append(String.format("<#%d>", it)).append("\n")); event.reply(embedCache.getEmbed("validChannels").injectValue("channels", result)); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java index 6cf3796..0f85118 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/commands/RankInfoCommand.java @@ -40,9 +40,8 @@ private void sendReply(UserInfo userInfo, User user, CommandEvent event) { .injectValues(userInfo.getEmbedValues(user)) .toEmbedBuilder(); - var guild = event.getGuild(); - if (database.getCollectEventService().isActive(guild)) { - var currency = database.getCollectEventService().getCollectCurrency(guild); + if (database.getCollectEventService().isActive()) { + var currency = database.getCollectEventService().getCollectCurrency(); var points = database.getCollectEventService().getCollectPoints(user); embed.addField(currency.name(), String.format("%s %d", currency.emoji(), points), false); } diff --git a/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java b/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java index c21c572..4e84cc5 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/settings/SettingsService.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot.settings; +import com.github.kaktushose.nplaybot.Bot; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import org.slf4j.Logger; @@ -13,31 +14,14 @@ public class SettingsService { private static final Logger log = LoggerFactory.getLogger(SettingsService.class); private final DataSource dataSource; + private final Guild guild; - public SettingsService(DataSource dataSource) { + public SettingsService(DataSource dataSource, Bot bot) { this.dataSource = dataSource; + this.guild = bot.getGuild(); } - public String getBotToken(long guildId) { - log.debug("Querying bot token"); - try (Connection connection = dataSource.getConnection()) { - var statement = connection.prepareStatement(""" - SELECT bot_token - FROM guild_settings - WHERE guild_id = ? - """ - ); - statement.setLong(1, guildId); - - var result = statement.executeQuery(); - result.next(); - return result.getString(1); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - public TextChannel getBotChannel(Guild guild) { + public TextChannel getBotChannel() { log.debug("Querying bot channel"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" diff --git a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java index 23f4540..c957dd8 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java @@ -60,19 +60,19 @@ public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { if (!starboardService.entryExists(messageId)) { starboardService.createEntry(messageId); } - if (count.get() < starboardService.getThreshold(event.getGuild())) { + if (count.get() < starboardService.getThreshold()) { return; } if (!starboardService.isRewarded(messageId)) { starboardService.setRewarded(messageId); var oldKarma = rankService.getUserInfo(event.getUser()).karma(); - karmaService.addKarma(UserSnowflake.fromId(event.getMessageAuthorIdLong()), starboardService.getKarmaReward(event.getGuild())); + karmaService.addKarma(UserSnowflake.fromId(event.getMessageAuthorIdLong()), starboardService.getKarmaReward()); var newKarma = rankService.getUserInfo(event.getUser()).karma(); - karmaService.onKarmaIncrease(oldKarma, newKarma, event.getMember(), event.getGuild(), embedCache); + karmaService.onKarmaIncrease(oldKarma, newKarma, event.getMember(), embedCache); } - var starboardChannel = event.getGuild().getTextChannelById(starboardService.getStarboardChannelId(event.getGuild())); + var starboardChannel = event.getGuild().getTextChannelById(starboardService.getStarboardChannelId()); if (starboardService.isPosted(messageId)) { starboardChannel.retrieveMessageById(starboardService.getPostId(messageId)) .flatMap(msg -> msg.editMessage(MessageEditData.fromCreateData(buildMessage(message, count.get())))) @@ -96,7 +96,7 @@ public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { } AtomicInteger count = new AtomicInteger(0); - var starboardChannel = event.getGuild().getTextChannelById(starboardService.getStarboardChannelId(event.getGuild())); + var starboardChannel = event.getGuild().getTextChannelById(starboardService.getStarboardChannelId()); event.getChannel().retrieveMessageById(event.getMessageIdLong()).queue(message -> { Optional.ofNullable(message.getReaction(Emoji.fromFormatted("⭐"))).ifPresent( it -> count.set(it.getCount()) @@ -119,7 +119,7 @@ public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) { starboardService.createEntry(messageId); } - if (count.get() < starboardService.getThreshold(event.getGuild()) && starboardService.isPosted(messageId)) { + if (count.get() < starboardService.getThreshold() && starboardService.isPosted(messageId)) { starboardChannel.retrieveMessageById(starboardService.getPostId(messageId)).flatMap(Message::delete).queue(); starboardService.setPostId(messageId, -1); return; @@ -185,7 +185,7 @@ private MessageCreateData buildMessage(Message message, int count) { private void removeEntry(GenericMessageEvent event) { starboardService.setPostId(event.getMessageIdLong(), -1); - event.getGuild().getTextChannelById(starboardService.getStarboardChannelId(event.getGuild())) + event.getGuild().getTextChannelById(starboardService.getStarboardChannelId()) .retrieveMessageById(starboardService.getPostId(event.getMessageIdLong())) .flatMap(Message::delete) .queue(); diff --git a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardService.java b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardService.java index aa1bb8e..5cab131 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardService.java @@ -1,5 +1,6 @@ package com.github.kaktushose.nplaybot.starboard; +import com.github.kaktushose.nplaybot.Bot; import net.dv8tion.jda.api.entities.Guild; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,9 +13,11 @@ public class StarboardService { private static final Logger log = LoggerFactory.getLogger(StarboardService.class); private final DataSource dataSource; + private final Guild guild; - public StarboardService(DataSource dataSource) { + public StarboardService(DataSource dataSource, Bot bot) { this.dataSource = dataSource; + this.guild = bot.getGuild(); } public boolean entryExists(long messageId) { @@ -110,7 +113,7 @@ public void setPostId(long messageId, long postId) { } } - public int getThreshold(Guild guild) { + public int getThreshold() { log.debug("Querying starboard threshold"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -128,7 +131,7 @@ public int getThreshold(Guild guild) { } } - public int getKarmaReward(Guild guild) { + public int getKarmaReward() { log.debug("Querying starboard karma reward"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" @@ -146,7 +149,7 @@ public int getKarmaReward(Guild guild) { } } - public long getStarboardChannelId(Guild guild) { + public long getStarboardChannelId() { log.debug("Querying starboard channel id"); try (Connection connection = dataSource.getConnection()) { var statement = connection.prepareStatement(""" diff --git a/src/main/resources/db/migration/V1.0.0__setup_nplaybot.sql b/src/main/resources/db/migration/V1.0.0__setup_nplaybot.sql new file mode 100644 index 0000000..709a95b --- /dev/null +++ b/src/main/resources/db/migration/V1.0.0__setup_nplaybot.sql @@ -0,0 +1,468 @@ +DELIMITER // +-- clear old database +DROP SCHEMA public CASCADE; +CREATE SCHEMA public; + + +-- create tables +CREATE TABLE bot_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + bot_channel_id BIGINT NOT NULL DEFAULT -1, + log_channel_id BIGINT NOT NULL DEFAULT -1 +); + +CREATE TABLE rank_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + message_cooldown BIGINT NOT NULL DEFAULT 600000, + min_message_length INT NOT NULL DEFAULT 10, + valid_channels BIGINT[] NOT NULL DEFAULT ARRAY[]::BIGINT[], + xp_loot_chance REAL NOT NULL DEFAULT 2.3, + rank_decay_start INT NOT NULL DEFAULT 8, + ranK_decay_xp_loss INT NOT NULL DEFAULT 150, + lootbox_chance REAL NOT NULL DEFAULT 1.0, + lootbox_query_limit INT NOT NULL DEFAULT 30 +); + +CREATE TABLE ranks ( + rank_id INT NOT NULL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL, + bound INT NOT NULL, + role_id BIGINT NOT NULL, + lootbox_reward BOOLEAN NOT NULL, + item_reward_id INT NOT NULL +); + +CREATE TABLE users ( + user_id BIGINT NOT NULL PRIMARY KEY, + daily_message BOOLEAN NOT NULL DEFAULT FALSE, + xp INT NOT NULL DEFAULT 0, + rank_id INT NOT NULL REFERENCES ranks(rank_id) DEFAULT 1, + last_valid_message BIGINT NOT NULL DEFAULT 0, + message_count BIGINT NOT NULL DEFAULT 0, + start_xp INT NOT NULL DEFAULT 0, + karma_points INT NOT NULL DEFAULT 0, + karma_tokens INT NOT NULL DEFAULT 5, + last_karma INT NOT NULL DEFAULT 0, + permissions INTEGER DEFAULT 1, + collect_points INTEGER DEFAULT 0 +); + +CREATE TABLE xp_chances ( + chance_id SERIAL NOT NULL PRIMARY KEY, + amount INT NOT NULL, + chance INT NOT NULL +); + +CREATE TABLE rank_statistics ( + date date NOT NULL PRIMARY KEY, + total_message_count INT NOT NULL DEFAULT 0, + valid_message_count INT NOT NULL DEFAULT 0, + total_xp_gain INT NOT NULL DEFAULT 0, + total_rank_ups INT NOT NULL DEFAULT 0 +); + +CREATE TABLE event_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + contest_channel_id BIGINT NOT NULL DEFAULT -1, + contest_vote_emoji VARCHAR NOT NULL DEFAULT '', + collect_event_name VARCHAR NOT NULL DEFAULT '', + collect_currency_name VARCHAR NOT NULL DEFAULT '', + collect_currency_emoji VARCHAR NOT NULL DEFAULT '', + collect_loot_chance REAL NOT NULL DEFAULT 10, + collect_event_active BOOLEAN NOT NULL DEFAULT FALSE +); + +CREATE TABLE contest_entries ( + user_id BIGINT NOT NULL PRIMARY KEY, + votes INT NOT NULL DEFAULT 0, + message_id BIGINT NOT NULL +); + +CREATE TABLE collect_rewards ( + reward_id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR NOT NULL DEFAULT 0, + threshold INT NOT NULL, + xp INT NOT NULL DEFAULT 0, + role_id BIGINT NOT NULL DEFAULT 0, + embed JSONB NOT NULL +); + +CREATE TABLE role_permissions ( + role_id BIGINT NOT NULL PRIMARY KEY, + permissions INTEGER NOT NULL DEFAULT 1 +); + +CREATE TABLE karma_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + VALID_EMOJIS VARCHAR[] NOT NULL DEFAULT ARRAY[]::VARCHAR[], + default_tokens INT NOT NULL DEFAULT 5, + play_activity_threshold INT NOT NULL DEFAULT 30 +); + +CREATE TABLE karma_rewards ( + reward_id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR NOT NULL DEFAULT 0, + threshold INT NOT NULL, + xp INT NOT NULL DEFAULT 0, + role_id BIGINT NOT NULL DEFAULT 0, + embed JSONB NOT NULL +); + +CREATE TABLE starboard_settings ( + guild_id BIGINT NOT NULL PRIMARY KEY, + channel_id BIGINT NOT NULL DEFAULT 0, + threshold INT NOT NULL DEFAULT 5, + karma_reward INT NOT NULL DEFAULT 5 +); + +CREATE TABLE starboard_entries ( + message_id BIGINT NOT NULL PRIMARY KEY, + post_id BIGINT NOT NULL DEFAULT -1, + is_rewarded BOOLEAN NOT NULL DEFAULT false +); + +CREATE TABLE item_types( + base_type_id INT NOT NULL PRIMARY KEY, + name VARCHAR NOT NULL, + emoji VARCHAR NOT NULL, + role_id BIGINT NOT NULL +); + +CREATE TABLE items( + item_id INT NOT NULL PRIMARY KEY, + type_id INT NOT NULL REFERENCES item_types(base_type_id), + name VARCHAR NOT NULL, + duration BIGINT +); + +CREATE TABLE transactions( + transaction_id SERIAL NOT NULL PRIMARY KEY, + user_id BIGINT NOT NULL, + item_id INT NOT NULL REFERENCES items(item_id), + expires_at BIGINT NOT NULL, + is_play_activity BOOLEAN NOT NULL DEFAULT FALSE +); + +CREATE TABLE lootbox_rewards ( + reward_id SERIAL NOT NULL PRIMARY KEY, + xp_reward INT NOT NULL DEFAULT -1, + karma_reward INT NOT NULL DEFAULT -1, + item_reward INT REFERENCES items(item_id), + chance INT NOT NULL DEFAULT 0 +); + +-- create functions + + +-- update rank trigger +CREATE FUNCTION update_ranks() +RETURNS TRIGGER AS +$$ +DECLARE + new_rank ranks; +BEGIN + SELECT INTO new_rank * FROM ranks WHERE NEW.xp >= bound ORDER BY bound DESC LIMIT 1; + + UPDATE users SET rank_id = new_rank.rank_id WHERE user_id = NEW.user_id; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER update_rank_trigger +AFTER UPDATE OF xp ON users +FOR EACH ROW +EXECUTE FUNCTION update_ranks(); + + +-- xp modification +CREATE FUNCTION set_xp(id BIGINT, new_xp INT) +RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank int) AS +$$ +DECLARE + old_rank INT; + new_rank INT; +BEGIN + SELECT INTO old_rank users.rank_id FROM users WHERE users.user_id = id; + IF new_xp < 0 THEN + new_xp := 0; + END IF; + UPDATE users SET xp = new_xp WHERE users.user_id = id; + SELECT INTO new_rank users.rank_id FROM users WHERE users.user_id = id; + + + SELECT INTO rank_changed old_rank <> new_rank; + SELECT INTO previous_rank old_rank; + SELECT INTO current_rank new_rank; + SELECT INTO next_rank new_rank + 1; + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION add_xp(id BIGINT, xp_to_add INT) +RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank INT, current_xp INT) AS +$$ +DECLARE +BEGIN + SELECT xp_to_add + users.xp INTO current_xp FROM users WHERE users.user_id = id; + SELECT INTO rank_changed, previous_rank, current_rank, next_rank * FROM set_xp(id, current_xp); + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; + + +-- random xp generation +CREATE FUNCTION get_random_xp() +RETURNS INT AS +$$ +DECLARE + result_row RECORD; + xp_chance INT; + chance_sum INT; +BEGIN + xp_chance := floor(random() * 100) + 1; + chance_sum := 0; + FOR result_row IN SELECT * FROM xp_chances + LOOP + chance_sum := chance_sum + result_row.chance; + IF chance_sum >= xp_chance THEN + RETURN result_row.amount; + END IF; + END LOOP; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION add_random_xp(id BIGINT) +RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank INT, current_xp INT) AS +$$ +DECLARE + xp INT; + bonus_xp INT; + karma INT; +BEGIN + SELECT get_random_xp INTO xp FROM get_random_xp(); + SELECT users.karma_points INTO karma FROM users WHERE users.user_id = id; + bonus_xp := 0; + IF karma < 0 THEN + bonus_xp := -1; + ELSIF karma >= 300 THEN + bonus_xp := 3; + ELSIF karma >= 200 THEN + bonus_xp := 2; + ELSIF karma >= 100 THEN + bonus_xp := 1; + END IF; + IF (SELECT EXISTS(SELECT 1 FROM transactions JOIN items ON transactions.item_id = items.item_id WHERE transactions.user_id=id AND type_id = 1)) THEN + bonus_xp := bonus_xp + 2; + END IF; + SELECT INTO rank_changed, previous_rank, current_rank, next_rank, current_xp * FROM add_xp(id, xp + bonus_xp); + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; + + +-- loot drops + + +CREATE FUNCTION get_xp_loot_drop(id BIGINT) +RETURNS TABLE (xp INT) AS +$$ +DECLARE + chance INT; + drop_chance REAL; +BEGIN + SELECT rank_settings.xp_loot_chance INTO drop_chance FROM rank_settings WHERE guild_id = id; + chance := floor(random() * 100) + 1; + IF ROUND(drop_chance) >= chance THEN + RETURN QUERY SELECT get_random_xp FROM get_random_xp(); + ELSE + RETURN QUERY SELECT 0; + END IF; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION get_random_lootbox() +RETURNS INT AS +$$ +DECLARE + result_row RECORD; + box_chance INT; + chance_sum INT; +BEGIN + box_chance := floor(random() * 100) + 1; + chance_sum := 0; + FOR result_row IN SELECT * FROM lootbox_rewards + LOOP + chance_sum := chance_sum + result_row.chance; + IF chance_sum >= box_chance THEN + RETURN result_row.reward_id; + END IF; + END LOOP; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION get_collect_loot_drop(id BIGINT) +RETURNS TABLE (points INT) AS +$$ +DECLARE + chance INT; + drop_chance REAL; +BEGIN + SELECT event_settings.collect_loot_chance INTO drop_chance FROM event_settings WHERE guild_id = id; + chance := floor(random() * 100) + 1; + IF ROUND(drop_chance) >= chance THEN + RETURN QUERY SELECT 1; + ELSE + RETURN QUERY SELECT 0; + END IF; +END; +$$ LANGUAGE plpgsql; + + +-- permissions +CREATE FUNCTION get_role_permissions(ids BIGINT[]) +RETURNS TABLE (permissions INT) AS +$$ +DECLARE + combined_permissions INTEGER := 0; + role_permission INTEGER; + id BIGINT; +BEGIN + FOREACH id IN ARRAY ids LOOP + SELECT role_permissions.permissions INTO role_permission FROM role_permissions WHERE role_permissions.role_id = id; + IF NOT FOUND THEN + CONTINUE; + END IF; + combined_permissions := combined_permissions | role_permission; + END LOOP; + RETURN QUERY SELECT combined_permissions; +END; +$$ LANGUAGE plpgsql; + + +-- statistics + + +-- valid message statistics +CREATE FUNCTION update_valid_message_statistics() +RETURNS TRIGGER AS +$$ +BEGIN + INSERT INTO rank_statistics (DATE, valid_message_count) VALUES (CURRENT_DATE, NEW.message_count - OLD.message_count) + ON CONFLICT (DATE) DO UPDATE SET valid_message_count = rank_statistics.valid_message_count + (NEW.message_count - OLD.message_count); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER valid_message_trigger +BEFORE UPDATE OF message_count ON users +FOR EACH ROW +EXECUTE FUNCTION update_valid_message_statistics(); + + +-- xp gain statistics +CREATE FUNCTION update_xp_gain_statistics() +RETURNS TRIGGER AS +$$ +BEGIN + INSERT INTO rank_statistics (DATE, total_xp_gain) VALUES (CURRENT_DATE, NEW.xp - OLD.xp) + ON CONFLICT (DATE) DO UPDATE SET total_xp_gain = rank_statistics.total_xp_gain + (NEW.xp - OLD.xp); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER xp_gain_trigger +BEFORE UPDATE OF xp ON users +FOR EACH ROW +EXECUTE FUNCTION update_xp_gain_statistics(); + + +-- rank up statistics +CREATE FUNCTION update_rank_ups_statistics() +RETURNS TRIGGER AS +$$ +BEGIN + INSERT INTO rank_statistics (DATE, total_rank_ups) VALUES (CURRENT_DATE, NEW.rank_id - OLD.rank_id) + ON CONFLICT (DATE) DO UPDATE SET total_rank_ups = rank_statistics.total_rank_ups + (NEW.rank_id - OLD.rank_id); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER rank_up_trigger +AFTER UPDATE OF rank_id ON users +FOR EACH ROW +EXECUTE FUNCTION update_rank_ups_statistics(); + + +-- total message statistics +CREATE FUNCTION update_total_message_statistics() +RETURNS VOID AS +$$ +BEGIN + INSERT INTO rank_statistics (DATE, total_message_count) VALUES (CURRENT_DATE, 1) + ON CONFLICT (DATE) DO UPDATE SET total_message_count = rank_statistics.total_message_count + 1; +END; +$$ LANGUAGE plpgsql; + + +-- insert data + + +-- items +INSERT INTO item_types(base_type_id, name, emoji, role_id) VALUES (1, 'XP-Booster', ':moneybag:', -1); +INSERT INTO item_types(base_type_id, name, emoji, role_id) VALUES (2, 'Premium', ':star:', -1); + +INSERT INTO items(item_id, type_id, name, duration) VALUES(1, 1, 'XP-Booster 7 Tage', 604800000); +INSERT INTO items(item_id, type_id, name, duration) VALUES(2, 1, 'XP-Booster 14 Tage', 1209600000); +INSERT INTO items(item_id, type_id, name, duration) VALUES(3, 2, 'Premium 1 Tag', 86400000); +INSERT INTO items(item_id, type_id, name, duration) VALUES(4, 2, 'Premium 3 Tage', 259200000); +INSERT INTO items(item_id, type_id, name, duration) VALUES(5, 2, 'Premium 7 Tage', 604800000); +INSERT INTO items(item_id, type_id, name, duration) VALUES(6, 2, 'Premium 15 Tage', 1296000000); +INSERT INTO items(item_id, type_id, name, duration) VALUES(7, 2, 'Premium 30 Tage', 2592000000); +INSERT INTO items(item_id, type_id, name, duration) VALUES(8, 2, 'Premium 60 Tage', 5184000000); + + +-- lootbox_rewards +INSERT INTO lootbox_rewards(item_reward, chance) VALUES(1, 7); +INSERT INTO lootbox_rewards(item_reward, chance) VALUES(2, 3); +INSERT INTO lootbox_rewards(item_reward, chance) VALUES(3, 9); +INSERT INTO lootbox_rewards(item_reward, chance) VALUES(4, 7); +INSERT INTO lootbox_rewards(item_reward, chance) VALUES(5, 6); +INSERT INTO lootbox_rewards(item_reward, chance) VALUES(6, 4); +INSERT INTO lootbox_rewards(item_reward, chance) VALUES(7, 3); +INSERT INTO lootbox_rewards(item_reward, chance) VALUES(8, 1); + +INSERT INTO lootbox_rewards(xp_reward, chance) VALUES(10, 25); +INSERT INTO lootbox_rewards(xp_reward, chance) VALUES(20, 15); +INSERT INTO lootbox_rewards(xp_reward, chance) VALUES(50, 10); + +INSERT INTO lootbox_rewards(karma_reward, chance) VALUES(1, 5); +INSERT INTO lootbox_rewards(karma_reward, chance) VALUES(2, 3); +INSERT INTO lootbox_rewards(karma_reward, chance) VALUES(3, 2); + + +-- xp_chances +INSERT INTO xp_chances(amount, chance) VALUES(1, 10); +INSERT INTO xp_chances(amount, chance) VALUES(2, 20); +INSERT INTO xp_chances(amount, chance) VALUES(3, 40); +INSERT INTO xp_chances(amount, chance) VALUES(4, 20); +INSERT INTO xp_chances(amount, chance) VALUES(5, 10); + + +-- ranks +INSERT INTO ranks VALUES(1, 'Treckertourist', '#ffffff', 0, -1, false, -1); +INSERT INTO ranks VALUES(2, 'Agrareinsteiger', '#351700', 5, -1, false, 1); +INSERT INTO ranks VALUES(3, 'Ackerbursche', '#441f00', 30, -1, false, 3); +INSERT INTO ranks VALUES(4, 'Scheunendrescher', '#60350a', 50, -1, true, -1); +INSERT INTO ranks VALUES(5, 'Pferdeflüsterer', '#7c4b19', 100, -1, false, 4); +INSERT INTO ranks VALUES(6, 'Ackerkünstler', '#9f7041', 170, -1, true, -1); +INSERT INTO ranks VALUES(7, 'Grubbermeister', '#b58e67', 250, -1, false, 5); +INSERT INTO ranks VALUES(8, 'Hofleiter', '#7d7036', 350, -1, true, -1); +INSERT INTO ranks VALUES(9, 'Grünland-Guru', '#7e944a', 500, -1, false, 2); +INSERT INTO ranks VALUES(10, 'Forstveteran', '#62862f', 700, -1, true, -1); +INSERT INTO ranks VALUES(11, 'XXL-Farmer', '#2f8631', 1100, -1, false, 6); +INSERT INTO ranks VALUES(12, 'Agrarvisionär', '#2f8631', 1700, -1, true, -1); +INSERT INTO ranks VALUES(13, 'Ackerdemiker', '#064027', 2500, -1, false, 7); +INSERT INTO ranks VALUES(14, 'Landlegende', '#003700', 3500, -1, true, -1); +INSERT INTO ranks VALUES(15, 'Ackerdemiker', '#dbaa19', 5000, -1, false, 8); diff --git a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql b/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql deleted file mode 100644 index 458d1cc..0000000 --- a/src/main/resources/db/migration/V1.0.0__setup_rank_system.sql +++ /dev/null @@ -1,198 +0,0 @@ -CREATE TABLE guild_settings ( - guild_id BIGINT NOT NULL PRIMARY KEY, - bot_token VARCHAR(255) NOT NULL, - bot_channel_id BIGINT NOT NULL, - log_channel_id BIGINT NOT NULL -); - -CREATE TABLE rank_settings ( - guild_id BIGINT NOT NULL PRIMARY KEY, - message_cooldown INT NOT NULL, - min_message_length INT NOT NULL, - valid_channels BIGINT[] NOT NULL, - xp_loot_chance REAL NOT NULL -); - -CREATE TABLE ranks ( - rank_id INT NOT NULL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - color VARCHAR(255) NOT NULL, - bound INT NOT NULL, - role_id BIGINT NOT NULL -); - -CREATE TABLE users ( - user_id BIGINT NOT NULL PRIMARY KEY, - daily_message BOOLEAN NOT NULL DEFAULT FALSE, - xp INT NOT NULL DEFAULT 0, - rank_id INT NOT NULL REFERENCES ranks(rank_id) DEFAULT 1, - last_valid_message BIGINT NOT NULL DEFAULT 0, - message_count BIGINT NOT NULL DEFAULT 0, - start_xp INT NOT NULL DEFAULT 0 -); - -CREATE TABLE xp_chances ( - chance_id INT NOT NULL PRIMARY KEY, - amount INT NOT NULL, - chance INT NOT NULL -); - -CREATE TABLE rank_statistics ( - date date NOT NULL PRIMARY KEY, - total_message_count INT NOT NULL DEFAULT 0, - valid_message_count INT NOT NULL DEFAULT 0, - total_xp_gain INT NOT NULL DEFAULT 0, - total_rank_ups INT NOT NULL DEFAULT 0 -); - -CREATE FUNCTION update_rank_trigger() -RETURNS TRIGGER AS -$$ -DECLARE - new_rank ranks; -BEGIN - SELECT INTO new_rank * FROM ranks WHERE NEW.xp >= bound ORDER BY bound DESC LIMIT 1; - - UPDATE users SET rank_id = new_rank.rank_id WHERE user_id = NEW.user_id; - - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER update_rank_trigger -AFTER UPDATE OF xp ON users -FOR EACH ROW -EXECUTE FUNCTION update_rank_trigger(); - -CREATE FUNCTION set_xp(id BIGINT, new_xp INT) -RETURNS TABLE (rank_changed BOOLEAN, current_rank int, next_rank int) AS -$$ -DECLARE - old_rank INT; - new_rank INT; -BEGIN - SELECT INTO old_rank users.rank_id FROM users WHERE users.user_id = id; - UPDATE users SET xp = new_xp WHERE users.user_id = id; - SELECT INTO new_rank users.rank_id FROM users WHERE users.user_id = id; - - - SELECT INTO rank_changed old_rank <> new_rank; - SELECT INTO current_rank new_rank; - SELECT INTO next_rank new_rank + 1; - RETURN NEXT; -END; -$$ LANGUAGE plpgsql; - -CREATE FUNCTION add_xp(id BIGINT, xp_to_add INT) -RETURNS TABLE (rank_changed BOOLEAN, current_rank int, next_rank INT, current_xp INT) AS -$$ -DECLARE -BEGIN - SELECT xp_to_add + users.xp INTO current_xp FROM users WHERE users.user_id = id; - SELECT INTO rank_changed, current_rank, next_rank * FROM set_xp(id, current_xp); - RETURN NEXT; -END; -$$ LANGUAGE plpgsql; - -CREATE FUNCTION get_random_xp() -RETURNS INT AS -$$ -DECLARE - result_row RECORD; - xp_chance INT; - chance_sum INT; -BEGIN - xp_chance := floor(random() * 100) + 1; - chance_sum := 0; - FOR result_row IN SELECT * FROM xp_chances - LOOP - chance_sum := chance_sum + result_row.chance; - IF chance_sum >= xp_chance THEN - RETURN result_row.amount; - END IF; - END LOOP; -END; -$$ LANGUAGE plpgsql; - -CREATE FUNCTION add_random_xp(id BIGINT) -RETURNS TABLE (rank_changed BOOLEAN, current_rank int, next_rank INT, current_xp INT) AS -$$ -DECLARE - xp INT; -BEGIN - SELECT get_random_xp INTO xp FROM get_random_xp(); - SELECT INTO rank_changed, current_rank, next_rank, current_xp * FROM add_xp(id, xp); - RETURN NEXT; -END; -$$ LANGUAGE plpgsql; - -CREATE FUNCTION increase_valid_message_statistics() -RETURNS TRIGGER AS -$$ -BEGIN - INSERT INTO rank_statistics (DATE, valid_message_count) VALUES (CURRENT_DATE, NEW.message_count - OLD.message_count) - ON CONFLICT (DATE) DO UPDATE SET valid_message_count = rank_statistics.valid_message_count + (NEW.message_count - OLD.message_count); - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER valid_message_trigger -BEFORE UPDATE OF message_count ON users -FOR EACH ROW -EXECUTE FUNCTION increase_valid_message_statistics(); - -CREATE FUNCTION increase_total_xp_gain() -RETURNS TRIGGER AS -$$ -BEGIN - INSERT INTO rank_statistics (DATE, total_xp_gain) VALUES (CURRENT_DATE, NEW.xp - OLD.xp) - ON CONFLICT (DATE) DO UPDATE SET total_xp_gain = rank_statistics.total_xp_gain + (NEW.xp - OLD.xp); - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER xp_gain_trigger -BEFORE UPDATE OF xp ON users -FOR EACH ROW -EXECUTE FUNCTION increase_total_xp_gain(); - -CREATE FUNCTION increase_total_rank_ups() -RETURNS TRIGGER AS -$$ -BEGIN - INSERT INTO rank_statistics (DATE, total_rank_ups) VALUES (CURRENT_DATE, NEW.rank_id - OLD.rank_id) - ON CONFLICT (DATE) DO UPDATE SET total_rank_ups = rank_statistics.total_rank_ups + (NEW.rank_id - OLD.rank_id); - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER rank_up_trigger -AFTER UPDATE OF rank_id ON users -FOR EACH ROW -EXECUTE FUNCTION increase_total_rank_ups(); - -CREATE FUNCTION increase_total_message_count() -RETURNS VOID AS -$$ -BEGIN - INSERT INTO rank_statistics (DATE, total_message_count) VALUES (CURRENT_DATE, 1) - ON CONFLICT (DATE) DO UPDATE SET total_message_count = rank_statistics.total_message_count + 1; -END; -$$ LANGUAGE plpgsql; - -CREATE FUNCTION get_xp_loot_drop(id BIGINT) -RETURNS TABLE (xp INT) AS -$$ -DECLARE - chance INT; - drop_chance REAL; -BEGIN - SELECT rank_settings.xp_loot_chance INTO drop_chance FROM rank_settings WHERE guild_id = id; - chance := floor(random() * 100) + 1; - IF ROUND(drop_chance) >= chance THEN - RETURN QUERY SELECT get_random_xp FROM get_random_xp(); - ELSE - RETURN QUERY SELECT 0; - END IF; -END; -$$ LANGUAGE plpgsql; diff --git a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql b/src/main/resources/db/migration/V2.0.0__setup_event_system.sql deleted file mode 100644 index 7582163..0000000 --- a/src/main/resources/db/migration/V2.0.0__setup_event_system.sql +++ /dev/null @@ -1,46 +0,0 @@ -CREATE TABLE event_settings ( - guild_id BIGINT NOT NULL PRIMARY KEY, - contest_channel_id BIGINT NOT NULL DEFAULT -1, - contest_vote_emoji VARCHAR NOT NULL DEFAULT '', - collect_event_name VARCHAR NOT NULL DEFAULT '', - collect_currency_name VARCHAR NOT NULL DEFAULT '', - collect_currency_emoji VARCHAR NOT NULL DEFAULT '', - collect_loot_chance REAL NOT NULL, - collect_event_active BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE TABLE contest_entries ( - user_id BIGINT NOT NULL PRIMARY KEY, - votes INT NOT NULL DEFAULT 0, - message_id BIGINT NOT NULL -); - -ALTER TABLE users ADD COLUMN permissions INTEGER DEFAULT 1; - -ALTER TABLE users ADD COLUMN collect_points INTEGER DEFAULT 0; - -CREATE TABLE collect_rewards ( - reward_id SERIAL PRIMARY KEY, - name VARCHAR NOT NULL DEFAULT 0, - threshold INT NOT NULL, - xp INT NOT NULL DEFAULT 0, - role_id BIGINT NOT NULL DEFAULT 0, - embed JSONB NOT NULL -); - -CREATE FUNCTION get_collect_loot_drop(id BIGINT) -RETURNS TABLE (points INT) AS -$$ -DECLARE - chance INT; - drop_chance REAL; -BEGIN - SELECT event_settings.collect_loot_chance INTO drop_chance FROM event_settings WHERE guild_id = id; - chance := floor(random() * 100) + 1; - IF ROUND(drop_chance) >= chance THEN - RETURN QUERY SELECT 1; - ELSE - RETURN QUERY SELECT 0; - END IF; -END; -$$ LANGUAGE plpgsql; diff --git a/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql b/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql deleted file mode 100644 index 7b5091b..0000000 --- a/src/main/resources/db/migration/V2.1.0__add_role_permissions.sql +++ /dev/null @@ -1,23 +0,0 @@ -CREATE TABLE role_permissions ( - role_id BIGINT PRIMARY KEY NOT NULL, - permissions INTEGER NOT NULL DEFAULT 1 -); - -CREATE FUNCTION get_role_permissions(ids BIGINT[]) -RETURNS TABLE (permissions INT) AS -$$ -DECLARE - combined_permissions INTEGER := 0; - role_permission INTEGER; - id BIGINT; -BEGIN - FOREACH id IN ARRAY ids LOOP - SELECT role_permissions.permissions INTO role_permission FROM role_permissions WHERE role_permissions.role_id = id; - IF NOT FOUND THEN - CONTINUE; - END IF; - combined_permissions := combined_permissions | role_permission; - END LOOP; - RETURN QUERY SELECT combined_permissions; -END; -$$ LANGUAGE plpgsql; diff --git a/src/main/resources/db/migration/V3.0.0__setup_karma_system.sql b/src/main/resources/db/migration/V3.0.0__setup_karma_system.sql deleted file mode 100644 index 6e90ad0..0000000 --- a/src/main/resources/db/migration/V3.0.0__setup_karma_system.sql +++ /dev/null @@ -1,18 +0,0 @@ -ALTER TABLE users -ADD COLUMN karma_points INT NOT NULL DEFAULT 0, -ADD COLUMN karma_tokens INT NOT NULL DEFAULT 5; - -CREATE TABLE karma_settings( - guild_id BIGINT NOT NULL PRIMARY KEY, - VALID_EMOJIS VARCHAR[] NOT NULL, - default_tokens INT NOT NULL DEFAULT 5 -); - -CREATE TABLE karma_rewards ( - reward_id SERIAL PRIMARY KEY, - name VARCHAR NOT NULL DEFAULT 0, - threshold INT NOT NULL, - xp INT NOT NULL DEFAULT 0, - role_id BIGINT NOT NULL DEFAULT 0, - embed JSONB NOT NULL -); diff --git a/src/main/resources/db/migration/V3.1.0__add_starboard.sql b/src/main/resources/db/migration/V3.1.0__add_starboard.sql deleted file mode 100644 index abda1b1..0000000 --- a/src/main/resources/db/migration/V3.1.0__add_starboard.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE starboard_settings ( - guild_id BIGINT NOT NULL PRIMARY KEY, - channel_id BIGINT NOT NULL DEFAULT 0, - threshold INT NOT NULL DEFAULT 5, - karma_reward INT NOT NULL DEFAULT 5 -); - -CREATE TABLE starboard_entries ( - message_id BIGINT PRIMARY KEY NOT NULL, - post_id BIGINT NOT NULL DEFAULT -1, - is_rewarded BOOLEAN NOT NULL DEFAULT false -); diff --git a/src/main/resources/db/migration/V3.2.0__setup_items.sql b/src/main/resources/db/migration/V3.2.0__setup_items.sql deleted file mode 100644 index 77e0626..0000000 --- a/src/main/resources/db/migration/V3.2.0__setup_items.sql +++ /dev/null @@ -1,137 +0,0 @@ -CREATE TABLE item_types( - base_type_id SERIAL PRIMARY KEY, - name VARCHAR NOT NULL, - emoji VARCHAR NOT NULL, - role_id BIGINT NOT NULL -); - -CREATE TABLE items( - item_id SERIAL PRIMARY KEY, - type_id SERIAL NOT NULL REFERENCES item_types(base_type_id), - name VARCHAR NOT NULL, - duration BIGINT -); - -CREATE TABLE transactions( - transaction_id SERIAL PRIMARY KEY, - user_id BIGINT NOT NULL, - item_id SERIAL NOT NULL REFERENCES items(item_id), - expires_at BIGINT NOT NULL, - is_play_activity BOOLEAN NOT NULL DEFAULT FALSE -); - -ALTER TABLE users ADD COLUMN last_karma INT NOT NULL DEFAULT 0; - -INSERT INTO item_types VALUES (1, 'XP-Booster', ':moneybag:', -1); - -INSERT INTO item_types VALUES (2, 'Premium', ':star:', -1); - -CREATE OR REPLACE FUNCTION add_random_xp(id BIGINT) -RETURNS TABLE (rank_changed BOOLEAN, current_rank int, next_rank INT, current_xp INT) AS -$$ -DECLARE - xp INT; - bonus_xp INT; - karma INT; -BEGIN - SELECT get_random_xp INTO xp FROM get_random_xp(); - SELECT users.karma_points INTO karma FROM users WHERE users.user_id = id; - bonus_xp := 0; - IF karma < 0 THEN - bonus_xp := -1; - ELSIF karma >= 300 THEN - bonus_xp := 3; - ELSIF karma >= 200 THEN - bonus_xp := 2; - ELSIF karma >= 100 THEN - bonus_xp := 1; - END IF; - IF (SELECT EXISTS(SELECT 1 FROM transactions JOIN items ON transactions.item_id = items.item_id WHERE transactions.user_id=id AND type_id = 1)) THEN - bonus_xp := bonus_xp + 2; - END IF; - SELECT INTO rank_changed, current_rank, next_rank, current_xp * FROM add_xp(id, xp + bonus_xp); - RETURN NEXT; -END; -$$ LANGUAGE plpgsql; - -ALTER TABLE ranks ADD COLUMN lootbox_reward BOOLEAN NOT NULL DEFAULT FALSE; - -ALTER TABLE ranks ADD COLUMN item_reward_id INT NOT NULL DEFAULT -1; - -CREATE TABLE lootbox_rewards ( - reward_id SERIAL NOT NULL PRIMARY KEY, - xp_reward INT NOT NULL DEFAULT -1, - karma_reward INT NOT NULL DEFAULT -1, - item_reward INT REFERENCES items(item_id), - chance INT NOT NULL DEFAULT 0 -); - -CREATE FUNCTION get_random_lootbox() -RETURNS INT AS -$$ -DECLARE - result_row RECORD; - box_chance INT; - chance_sum INT; -BEGIN - box_chance := floor(random() * 100) + 1; - chance_sum := 0; - FOR result_row IN SELECT * FROM lootbox_rewards - LOOP - chance_sum := chance_sum + result_row.chance; - IF chance_sum >= box_chance THEN - RETURN result_row.reward_id; - END IF; - END LOOP; -END; -$$ LANGUAGE plpgsql; - -DROP FUNCTION set_xp; -DROP FUNCTION add_xp; -DROP FUNCTION add_random_xp; - -CREATE OR REPLACE FUNCTION set_xp(id BIGINT, new_xp INT) -RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank int) AS -$$ -DECLARE - old_rank INT; - new_rank INT; -BEGIN - SELECT INTO old_rank users.rank_id FROM users WHERE users.user_id = id; - IF new_xp < 0 THEN - new_xp := 0; - END IF; - UPDATE users SET xp = new_xp WHERE users.user_id = id; - SELECT INTO new_rank users.rank_id FROM users WHERE users.user_id = id; - - - SELECT INTO rank_changed old_rank <> new_rank; - SELECT INTO previous_rank old_rank; - SELECT INTO current_rank new_rank; - SELECT INTO next_rank new_rank + 1; - RETURN NEXT; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION add_xp(id BIGINT, xp_to_add INT) -RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank INT, current_xp INT) AS -$$ -DECLARE -BEGIN - SELECT xp_to_add + users.xp INTO current_xp FROM users WHERE users.user_id = id; - SELECT INTO rank_changed, previous_rank, current_rank, next_rank * FROM set_xp(id, current_xp); - RETURN NEXT; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION add_random_xp(id BIGINT) -RETURNS TABLE (rank_changed BOOLEAN, previous_rank int, current_rank int, next_rank INT, current_xp INT) AS -$$ -DECLARE - xp INT; -BEGIN - SELECT get_random_xp INTO xp FROM get_random_xp(); - SELECT INTO rank_changed, previous_rank, current_rank, next_rank, current_xp * FROM add_xp(id, xp); - RETURN NEXT; -END; -$$ LANGUAGE plpgsql; From 292559e2ece8b836b58664c81187ffcb2856ed42 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Tue, 2 Jul 2024 16:58:13 +0200 Subject: [PATCH 64/66] update readme and rename project --- .github/workflows/deploy.yml | 4 +- README.md | 72 +++++++++++++++++++++++++++++++++--- docker-compose.yml | 2 +- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5b0c957..3b47e1b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,7 +2,7 @@ name: Deploy on: push: - branches: [ rewrite ] + branches: [ main ] workflow_dispatch: jobs: @@ -28,7 +28,7 @@ jobs: with: image: ${{ steps.build-image.outputs.image }} tags: ${{ steps.build-image.outputs.tags }} - registry: ghcr.io/kaktushose/levelbot + registry: ghcr.io/kaktushose/nplay-bot username: Kaktushose password: ${{ github.token }} extra-args: | diff --git a/README.md b/README.md index 6aae679..51409f5 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,78 @@ -[![Java CI](https://github.com/Kaktushose/Levelbot/actions/workflows/maven.yml/badge.svg?branch=rewrite)](https://github.com/Kaktushose/Levelbot/actions/workflows/maven.yml) -[![Deploy](https://github.com/Kaktushose/Levelbot/actions/workflows/deploy.yml/badge.svg?branch=rewrite)](https://github.com/Kaktushose/Levelbot/actions/workflows/deploy.yml) -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/6eaa0127de99428795b4f5f759da188a)](https://www.codacy.com/gh/Kaktushose/Levelbot/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Kaktushose/Levelbot&utm_campaign=Badge_Grade) -![Generic badge](https://img.shields.io/badge/Version-2.3.0-86c240".svg) +[![Java CI](https://github.com/Kaktushose/NPLAY-Bot/actions/workflows/maven.yml/badge.svg)](https://github.com/Kaktushose/NPLAY-Bot/actions/workflows/maven.yml) +[![Deploy](https://github.com/Kaktushose/NPLAY-Bot/actions/workflows/deploy.yml/badge.svg)](https://github.com/Kaktushose/NPLAY-Bot/actions/workflows/deploy.yml) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/6eaa0127de99428795b4f5f759da188a)](https://www.codacy.com/gh/Kaktushose/NPLAY-Bot/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Kaktushose/Levelbot&utm_campaign=Badge_Grade) +![Generic badge](https://img.shields.io/badge/Version-3.0.0-86c240".svg) [![license-shield](https://img.shields.io/badge/License-Apache%202.0-lightgrey.svg)]() discord - + # NPLAY-Bot -This bot was created specifically for the Discord [server](https://discord.gg/qcpeZQhJf5) of the german YouTuber and Twitch Streamer [nordrheintvplay](https://www.youtube.com/user/nordrheintvplay). The core feature of this bot is a leveling system, similar to Mee6, but with a high level of customization. Besides 3 currencies that can be collected, there are 13 items that all bring different functions and advantages. In addition, there are various temporary events, daily rewards and many other useful features. +This bot was created specifically for the Discord [server](https://discord.gg/qcpeZQhJf5) of the german YouTuber and Twitch Streamer [NPLAY](https://www.youtube.com/nplay). The core feature of this bot is a leveling system, similar to Mee6, but with a high level of customization. +In addition, there are various temporary events, a karma system, rewards, permissions and many other useful features. ## Test Server The bot is in constant development. Join the test [server](https://discord.gg/JYWezvQ) to receive regular updates, make suggestions and test preview versions. This is also the place to get support if you want to host the bot by yourself. + +## Installation +Due to the high level of customization, I do not provide a public instance that anyone can invite. However, you can still host your own version of the bot. Therefore, you should have a basic understanding of Docker, PostgresSQL and of course Discord bots in general. + +> [!IMPORTANT] +> The bot is designed to only run on a single guild. Multiple guilds are *not* supported. + +### 1. Clone the repo + +``` +git clone https://github.com/Kaktushose/NPLAY-Bot.git +``` + +### 2. Initial Configuration + +Rename the `.env.example` file to `.env` and provide the given values. + +```env +POSTGRES_DB=database +POSTGRES_USER=user +POSTGRES_PASSWORD=password +POSTGRES_URL=jdbc:postgresql://postgres:5432/database +GF_SECURITY_ADMIN_PASSWORD=password +BOT_GUILD=0123456789 +BOT_TOKEN=bot_token +``` + +### 3. Start the bot + +Start the bot by running: + +``` +docker compose up +``` + +> [!NOTE] +> The default `docker-compose.yml` will also start up Grafana and Watchtower. These services aren't necessary to run the Bot, so feel free to remove them. + +### 4. Further Configuration + +To make the bot work properly, you now have to populate the Database correctly. + +1. Update the role ids inside the `item_types` and `ranks` table +2. Update the channel ids inside the `bot_settings` table +3. Add your karma vote emojis (e.g. 👍) inside the `karma_settings` table +3. Use the `/rank-config valid-channels add` command to whitelist channels for the xp system +4. Manage the permissions with the `/permissions role edit` and `/permissions user edit` commands + +That's it. The bot is now fully functional. + +## Credits + +I want to thank the following people in now particular order: + +1. SimuPlays for creating the concept +2. Combauer for his support with the concept and the logo creation +3. 1Flo3 for testing +4. MeerBiene for his support and general ideas and for keeping me sane +5. And of course the rest of the NPLAY Discord Team diff --git a/docker-compose.yml b/docker-compose.yml index 4477b44..d1b2c4a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: - postgres bot: - image: ghcr.io/kaktushose/levelbot/nplaybot:latest + image: ghcr.io/kaktushose/nplay-bot/nplaybot:latest container_name: nplay-bot restart: on-failure environment: From 41e58a5375d3b77b6f6a8f604528dcc7bcae4bbf Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Tue, 2 Jul 2024 16:58:50 +0200 Subject: [PATCH 65/66] update dependencies --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 22cf8a9..28c8cae 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ ch.qos.logback logback-core - 1.4.12 + 1.4.14 From c47f35b6af0998229724fc35115c2811dd652a55 Mon Sep 17 00:00:00 2001 From: Kaktushose Date: Tue, 2 Jul 2024 17:04:05 +0200 Subject: [PATCH 66/66] refactor code --- README.md | 4 ++-- src/main/java/com/github/kaktushose/nplaybot/Bot.java | 1 - src/main/java/com/github/kaktushose/nplaybot/Database.java | 2 -- .../com/github/kaktushose/nplaybot/items/ItemService.java | 4 ++-- .../com/github/kaktushose/nplaybot/karma/KarmaService.java | 1 - .../com/github/kaktushose/nplaybot/rank/RankDecayTask.java | 4 ++-- .../com/github/kaktushose/nplaybot/rank/RankListener.java | 1 - .../com/github/kaktushose/nplaybot/rank/RankService.java | 7 ++++--- .../github/kaktushose/nplaybot/rank/model/RankInfo.java | 3 ++- .../kaktushose/nplaybot/rank/model/XpChangeResult.java | 3 ++- .../kaktushose/nplaybot/starboard/StarboardListener.java | 1 - 11 files changed, 14 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 51409f5..105fbc1 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ To make the bot work properly, you now have to populate the Database correctly. 1. Update the role ids inside the `item_types` and `ranks` table 2. Update the channel ids inside the `bot_settings` table 3. Add your karma vote emojis (e.g. 👍) inside the `karma_settings` table -3. Use the `/rank-config valid-channels add` command to whitelist channels for the xp system -4. Manage the permissions with the `/permissions role edit` and `/permissions user edit` commands +4. Use the `/rank-config valid-channels add` command to whitelist channels for the xp system +5. Manage the permissions with the `/permissions role edit` and `/permissions user edit` commands That's it. The bot is now fully functional. diff --git a/src/main/java/com/github/kaktushose/nplaybot/Bot.java b/src/main/java/com/github/kaktushose/nplaybot/Bot.java index 2aa4948..ece05ea 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Bot.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Bot.java @@ -30,7 +30,6 @@ public class Bot { private final TaskScheduler taskScheduler; private final Guild guild; - @SuppressWarnings("DataFlowIssue") private Bot(long guildId, String token) throws InterruptedException, RuntimeException { embedCache = new EmbedCache("embeds.json"); diff --git a/src/main/java/com/github/kaktushose/nplaybot/Database.java b/src/main/java/com/github/kaktushose/nplaybot/Database.java index bfb2fef..5cd29ee 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/Database.java +++ b/src/main/java/com/github/kaktushose/nplaybot/Database.java @@ -10,8 +10,6 @@ import com.github.kaktushose.nplaybot.starboard.StarboardService; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.Guild; import java.sql.Connection; import java.sql.SQLException; diff --git a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java index bf5716f..573f192 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/items/ItemService.java @@ -13,8 +13,8 @@ public class ItemService { - private final DataSource dataSource; private static final int PLAY_ACTIVITY_ITEM_ID = 7; + private final DataSource dataSource; private final Guild guild; public ItemService(DataSource dataSource, Bot bot) { @@ -124,7 +124,7 @@ public void createTransaction(UserSnowflake user, int itemId) { // if so just increase the duration of the first item if (result.next()) { statement = connection.prepareStatement("UPDATE transactions SET expires_at = ? WHERE transaction_id = ?"); - statement.setLong(1,result.getLong("expires_at") + item.duration); + statement.setLong(1, result.getLong("expires_at") + item.duration); statement.setLong(2, result.getInt("transaction_id")); statement.execute(); return; diff --git a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java index 01bc846..126a36f 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/karma/KarmaService.java @@ -6,7 +6,6 @@ import com.github.kaktushose.nplaybot.rank.RankService; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.*; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; import net.dv8tion.jda.api.utils.data.DataObject; diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java index b42d9f1..40158f9 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankDecayTask.java @@ -30,11 +30,11 @@ public void accept(Bot bot) { if (newRank.get().rankId() < startDecayRankId) { var minimumRank = bot.getDatabase().getRankService().getRankInfo(startDecayRankId); result = bot.getDatabase().getRankService().setXp(member, minimumRank.orElseThrow().xpBound()); - // if we don't fall under the minimum rank just remove all xp as planned + // if we don't fall under the minimum rank just remove all xp as planned } else { result = bot.getDatabase().getRankService().setXp(member, userXpAfterDecay); } - // if new rank isn't present we went too far and also set xp to lower bound + // if new rank isn't present we went too far and also set xp to lower bound } else { var minimumRank = bot.getDatabase().getRankService().getRankInfo(startDecayRankId); result = bot.getDatabase().getRankService().setXp(member, minimumRank.orElseThrow().xpBound()); diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java index badc846..a198a99 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankListener.java @@ -3,7 +3,6 @@ import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.Bot; import com.github.kaktushose.nplaybot.Database; -import com.github.kaktushose.nplaybot.settings.SettingsService; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java index 7470f08..c2cf5da 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/RankService.java @@ -399,7 +399,7 @@ public void onXpChange(XpChangeResult result, Member member, EmbedCache embedCac .build(); bot.getDatabase().getSettingsService().getBotChannel().sendMessage(message).queue(msg -> - lootbox.ifPresent(it -> LootboxListener.newListener(bot, it, member, msg)) + lootbox.ifPresent(it -> LootboxListener.newListener(bot, it, member, msg)) ); } @@ -437,8 +437,6 @@ public Optional getRankByXp(int xp) { } } - public record Lootbox(int id, int xpReward, int karmaReward, int itemId){} - public void updateRankRoles(Member member, RankInfo currentRank) { var validRole = guild.getRoleById(currentRank.roleId()); var invalidRoles = getRankRoleIds().stream() @@ -680,4 +678,7 @@ public int getLootboxQueryLimit() { } } + public record Lootbox(int id, int xpReward, int karmaReward, int itemId) { + } + } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java index 6770991..30304cf 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/RankInfo.java @@ -1,4 +1,5 @@ package com.github.kaktushose.nplaybot.rank.model; -public record RankInfo(int rankId, long roleId, String name, String color, int xpBound, boolean lootboxReward, int itemRewardId) { +public record RankInfo(int rankId, long roleId, String name, String color, int xpBound, boolean lootboxReward, + int itemRewardId) { } diff --git a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java index ce056a1..7106158 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java +++ b/src/main/java/com/github/kaktushose/nplaybot/rank/model/XpChangeResult.java @@ -6,7 +6,8 @@ import java.util.Map; import java.util.Optional; -public record XpChangeResult(boolean rankChanged, Optional previousRank, RankInfo currentRank, Optional nextRank, int currentXp) { +public record XpChangeResult(boolean rankChanged, Optional previousRank, RankInfo currentRank, + Optional nextRank, int currentXp) { public Map getEmbedValues(UserSnowflake user) { var result = new HashMap() {{ diff --git a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java index c957dd8..9234d9b 100644 --- a/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java +++ b/src/main/java/com/github/kaktushose/nplaybot/starboard/StarboardListener.java @@ -2,7 +2,6 @@ import com.github.kaktushose.jda.commands.data.EmbedCache; import com.github.kaktushose.nplaybot.Database; -import com.github.kaktushose.nplaybot.karma.KarmaListener; import com.github.kaktushose.nplaybot.karma.KarmaService; import com.github.kaktushose.nplaybot.rank.RankService; import net.dv8tion.jda.api.EmbedBuilder;