Compare commits

...
Sign in to create a new pull request.

556 commits

Author SHA1 Message Date
daba9d2374
fix: don't run seen (track players' IPs) when it doesn't exist (for ayunboom) 2025-06-16 18:59:48 +07:00
2fb5dad02c
fix: hopefully fix errors on stopping 2025-06-16 18:05:43 +07:00
c4e7c9e799
fix: use UUID for clearchat name announcing
refactor: CommandSpyPlugin
2025-06-16 18:04:47 +07:00
0960e8e834
refactor: improve record classes and make FilterManagerPlugin use the FilteredPlayer record + use adventure's TextComponent.Builder instead of keeping setting the component variable 2025-06-14 09:02:55 +07:00
192a6a0922
fix: OOB error thrown on filter and ipfilter remove 2025-06-10 20:27:02 +07:00
0ae4f32c3a
fix: chadgpt broke the command handler again ahh 2025-06-10 20:25:51 +07:00
32f93f247e
fix: delay 5 seconds before thinking that the server is vanilla (this is confirmed to still work on actual vanilla servers and on kaboom too)
5 seconds because the server sends out the commands packet a bit later after login, but it seems like the player left packet (which is used to detect vanish) is sent before that
2025-06-08 11:51:48 +07:00
ef3298e3f8
refactor: put the disconnect reasons that needs 5 seconds delay into a static list 2025-06-08 09:20:24 +07:00
7440dccc9b
refactor: don't ignore if the bot isn't logged in when sending a message in console
although the selector shows nothing when the message gets flushed when we login since it uses the old bot uuid
2025-06-08 09:19:51 +07:00
313c4389a1
fix: EndCommand while cloop running deadlocks the executor causing the bot to not reconnect and bricks (absolute cinema) 2025-06-08 09:08:53 +07:00
3d6f1193e6
refactor: merge cloop task into CommandLoop 2025-06-08 09:07:57 +07:00
233b386af9
fix: queue query when the bot doesn't have enough permissions 2025-06-07 17:53:36 +07:00
12b8359c6b
fix: bot runs out of memory when someone deop or gamemode it while the core is active (music playing, cloop running) 2025-06-07 17:23:30 +07:00
9a81921baa
fix: add goofy code to fix the color after a cspy message when the username has section signs being reset 2025-06-03 19:07:23 +07:00
bcaf59c19d
refactor: don't use .color(TextColor) when we can Component.text(something, color) or Component.translatable(something, color, ...args) 2025-06-03 16:15:54 +07:00
78f84f1c71
refactor: use selector for trusted broadcast instead of username and some code cleanup 2025-06-03 15:58:56 +07:00
ba53a265c5
refactor: small change in TestCommand 2025-06-03 15:54:13 +07:00
3960bf1fab
refactor: improve how the persistent data in PlayerEntry are stored which fixes the authenticated trust level being wiped when unvanish 2025-06-01 07:47:23 +07:00
21100ee7b1
refactor: use Executor.execute instead of submit
refactor: use mcprotocollib's packet handler executor to send the brand and the client information (even though it's for handling packets)
2025-05-31 17:30:23 +07:00
12d01932c1
fix: cannot trust ChadGPT generated code 2025-05-31 14:31:02 +07:00
f542bb6fc1
refactor: improve getting TrustLevel from discord roles and improve CommandHandlerPlugin (finally) 2025-05-31 08:34:43 +07:00
23b7c3351a
feat: god self care (can't believe i don't have one) 2025-05-30 16:45:50 +07:00
271c1e797a
refactor: clarify the mail command's description to be more clear that it can only send mail to players, not real e-mails 2025-05-29 20:16:22 +07:00
5cd9fb3959
fix: be careful with querying when the bot doesn't have enough permissions 2025-05-29 20:11:29 +07:00
7e5d3c8fc8
fix: race condition in ChatPlugin event dispatching 2025-05-29 20:10:49 +07:00
ee8d68c4e3
fix: filter already exists translation 2025-05-27 20:05:07 +07:00
67774125c9
fix: nullable doesn't exist? weird 2025-05-27 20:03:26 +07:00
b21104c306
chore: bump dependencies 2025-05-27 19:01:59 +07:00
76e874e077
refactor: make the discord channel in config based on channel id instead of channel name so we can rename the channels from tonyboom to jorkboom for example, without having to modify the config 2025-05-27 18:21:08 +07:00
48ffc7c01c
refactor: make eval use only 1 connection to hopefully reduce threads amount + some misc refactors
fix: in GetBotInfoFunction, i misspelled `username` as `usernane`, it has been fixed
refactor: remove GetLatestChatMessageFunction, since the functions are now global and not bot-specific (yes, i know i can still implement it somewhere like in EvalPlugin or even call a function in GetLatestChatMessageFunction itself passing the bot, but no one really use this function anyway except the last time when ploat made some telnet ahh eval thing which used this function)
2025-05-27 17:48:17 +07:00
f68ce719f9
feat: store keys per-user using discord instead of having a global key
refactor: use language on DirectMessageEventHandler
refactor: some misc refactors (i forgot what i did lol)
2025-05-26 19:46:38 +07:00
9afbdc26b3
refactor: catch Throwable on some things
mainly the ListenerManagerPlugin because it also handles packets so we don't want the bot to disconnect even on fatal error
2025-05-25 13:12:26 +07:00
a368cc6f13
refactor: reuse the packet handler executor 2025-05-25 13:09:10 +07:00
393715f9da
refactor: use switch-case in MidiConverter + new panning method that should be correct + misc refactors 2025-05-25 12:40:55 +07:00
263ea7458c
fix: stack overflow kick exploit by section sign + characters that aren't in the codes like v 2025-05-25 09:21:54 +07:00
95a1f58fe1
feat: very ass discord slash commands 2025-05-25 09:21:05 +07:00
040be16246
refactor: use the deserializer from LegacyComponentSerializer instead of our own which fixes the wikipedia page for Among Us 2025-05-25 08:04:28 +07:00
c68e300047
feat: add more stuff to AuthCommand but restrict setting other player's level to TrustLevel.MAX only 2025-05-25 07:53:52 +07:00
0105ee95a2
refactor: improve comma adding in SNBTUtilities 2025-05-24 18:14:30 +07:00
007e4b5b59
refactor: improve servereval output string getting + add shell on servereval 2025-05-24 17:42:22 +07:00
edbd96366b
feat: ignore case prefixes so stuff like CBOT:TEST will work 2025-05-24 15:57:30 +07:00
53a3d95032
feat: also deserialize ANSIs in discord embed (for command output)
pretty small change but still
2025-05-23 18:25:29 +07:00
8c8a058f38
fix: limit AuthCommand a bit... it seem dumb idea :( 2025-05-22 20:35:51 +07:00
ad5e203417
feat: *auth so you don't have to type hash every time (this can be abused idk) 2025-05-22 18:50:28 +07:00
8fd2f2ebb3
refactor: just make getBlock return 0 when it fails 2025-05-22 17:24:04 +07:00
f3d3355bd2
fix: bots -> bot's (i think intellij is wrong? but with apostrophe it's less confusing with the plural "bots") 2025-05-22 17:15:03 +07:00
37e69779ae
refactor: move the time formatting thing into TimeUtilities 2025-05-22 17:10:18 +07:00
cdaa82bdfa
fix: OOB on core exist and complete (happen when bot switch to dimensions like nether and/or the end) 2025-05-21 18:15:42 +07:00
8ab00505e4
fix: emergency 9 being replaced with 3 for discord 2025-05-21 16:18:39 +07:00
3f0ae861e6
feat: set commandBlockOutput to false on login 2025-05-20 16:26:47 +07:00
0f28e8c900
fix: make the events work with new adventure and improve SNBTUtilities a bit 2025-05-19 18:39:18 +07:00
b4f0958202
feat: finally use adventure component serializers for component parsing (ComponentUtilities)
there is an issue with the legacy code parsing where it doesn't reset the styles when there's a color code, but that is since the old parser, it's not that important but just something to note
similar thing goes to discord message deserialization, the style goes first then the color, but for the style to be displayed in minecraft, the color has to go first then the style. an example will be like: `§l§c[§cOP§c] §r§c§r§cchayapak§r: abc`, the `l` and `c` should be swapped
hex colors also didn't get rounded correctly for some reason. my custom chat format is completely white in discord, although minimessage rainbow and gradients works perfectly fine, which is very weird, maybe it thinks my custom chat format looks white
2025-05-18 19:52:43 +07:00
d1d7b9fb29
fix: auth timeout seconds 2025-05-18 09:22:49 +07:00
5b56f411a1
refactor: check player left and ip query without queue for faster trusted broadcast and filtering 2025-05-17 17:26:16 +07:00
a60baad6f3
feat: .console discord <message> 2025-05-16 17:50:56 +07:00
5f569246af
fix: null 2025-05-16 17:40:58 +07:00
a3e0482604
refactor: move discord and irc channels configuration to each bot's option for easy server adding and also makes the config less messy 2025-05-16 17:34:42 +07:00
c7a578f1d0
refactor: improve RandomTeleportCommand 2025-05-16 16:51:05 +07:00
8ab8ecd2f7
fix: make findalts use the ip from the current player list first but if not found then use the one from the database 2025-05-15 19:57:15 +07:00
8dad298c50
refactor: improve FindAltsCommand a bit 2025-05-15 18:21:39 +07:00
b699cb2f7a
fix: *music testsong throwing NPE because i didn't set the context 2025-05-15 17:48:19 +07:00
c5da3a8689
feat: actually make the strings like abc123.among.us__69 not require quotes in SNBTUtilities 2025-05-14 20:35:26 +07:00
76e75f3601
fix: prefix self care not working for the Something went wrong while saving the prefix 2025-05-14 16:44:10 +07:00
879c1acf1d
fix: netmsg translations not working (lmao) 2025-05-14 16:43:47 +07:00
2b1a4067ab
fix: i thought i can trust IDEA (*echo spaces not working) 2025-05-11 07:52:42 +07:00
092f83ade4
fix: chat not working 2025-05-10 18:51:24 +07:00
e9cc4f1499
refactor: send music loading messages to the context instead of tellraw 2025-05-10 16:48:55 +07:00
f497651043
fix: removed too much 2025-05-10 16:33:24 +07:00
839b202d11
refactor: let each music converter calculate the block position instead of having one in MusicPlayerPlugin 2025-05-10 16:31:20 +07:00
3f7625895f
fix: hopefully fix the music player racing contest 2025
refactor: change the ordering of variables and things
2025-05-10 16:10:42 +07:00
c392db1903
fix: hopefully fix some errors spam when the bot is stopping sometimes
you don't really notice it unless you host the bot on github actions which changes instance every 6 hours
2025-05-10 15:57:38 +07:00
7660ac57bc
feat: make console prompt [server:1234] > so we can finally see the console server !!! 2025-05-10 13:21:53 +07:00
56c7aa5493
refactor: don't trim the string again since we already did it 2025-05-09 09:42:07 +07:00
4be485df0f
feat: option to log connection status messages or not 2025-05-08 18:47:55 +07:00
ca61686a70
fix: @ in Configuration 2025-05-08 18:16:33 +07:00
8e743b2014
refactor: move translation rendering to I18nUtilities 2025-05-08 06:48:50 +07:00
0c89ffc983
fix: netcmd not showing translated messages 2025-05-07 20:48:36 +07:00
66f7754b78
fix: clearchat not showing translation (lmao) 2025-05-07 19:24:34 +07:00
e8a4c03e8c
feat: tick using debug sample
refactor: remove onAlwaysTick because no one uses it
2025-05-07 17:51:59 +07:00
dc38ef86c6
fix: hbot discord crash exploit but also crashes chomens discord & lint DiscordPlugin a bit 2025-05-07 17:51:32 +07:00
cea28ffd50
fix: rtp translation broken 2025-05-07 16:56:13 +07:00
a7792651bb
fix: help not working (had to change hover event :() 2025-05-07 10:05:02 +07:00
d9608f25cb
fix: NPE on music skip 2025-05-06 20:32:09 +07:00
0e8bd4404b
refactor: add disallowedPacketTypes into Command's toString 2025-05-06 19:05:40 +07:00
9030752007
refactor: improve TabCompletePlugin
- `nextTransactionId` is now an AtomicInteger
- `tabComplete` function has been renamed to only `complete` so instead of calling like `bot.tabComplete.tabComplete()` now it's `bot.tabComplete.complete()`
- also clear the transactions and reset `nextTransactionId` in case the server didn't respond the bot won't leak memory
2025-05-06 19:02:28 +07:00
c952ba5a6f
fix: add language for SongLoaderThread 2025-05-06 17:06:28 +07:00
a915cbbe4b
feat: localization (hopefully i didn't miss anything but surely i will)
it took around 4 hours but i think it's pretty normal for such things like this
2025-05-06 16:11:09 +07:00
0330cc2f75
fix: actually make disconnect write the last seen entries 2025-05-06 08:59:57 +07:00
958730ec99
refactor: make the query interpret more compact 2025-05-06 08:51:41 +07:00
2e382da7c3
feat: ignore the extras clear chat message 2025-05-06 08:33:51 +07:00
5d91373e6b
refactor: make filter manager cloop instead of dynamic 2025-05-06 08:24:59 +07:00
8c1ea040cf
fix: resize on local second tick instead of server to prevent core going to height limit (only for genius) 2025-05-05 18:28:56 +07:00
b38f2a7e1b
fix: vanish self care broken after reconnecting 2025-05-05 17:35:50 +07:00
2b23074b1e
refactor: make the query component size a bit less bloated 2025-05-05 15:08:49 +07:00
e8eb2f3b4d
refactor: make nextTransactionId final in QueryPlugin (small change but still) 2025-05-05 14:40:32 +07:00
9aea972690
refactor: make self-care modular !!! (finally no more messy ahh shit)
refactor: use update time packet from the server to check for self-care
2025-05-05 14:02:52 +07:00
8a868052f5
fix: don't attempt to reconnect when the bot is stopping 2025-05-05 13:20:00 +07:00
14ab8a7bda
fix: array oob when refilling core when switching dimensions lol 2025-05-05 13:12:58 +07:00
3db1002af1
fix: denis check and use AtomicInteger on the thingy 2025-05-04 18:56:30 +07:00
3bf8b78e28
fix: limit nanoseconds cloop 2025-05-04 15:22:35 +07:00
e30132b00d
feat: remove old core when relocating 2025-05-03 20:07:39 +07:00
5884718994
refactor: use uuid for cspy self care + self care refactors 2025-05-03 18:05:10 +07:00
eff32f8437
refactor: improve flags system and support --flag instead of only -flag
had to remove flags in TestCommand sadly
2025-05-03 16:12:27 +07:00
4951286575
fix: also update last seen entry when bot disconnects 2025-05-03 14:14:04 +07:00
059664ca0c
fix: run *alts in the database executor (it was running on the packet thread the whole time 💀💀💀) 2025-05-03 13:54:44 +07:00
21d79192c6
fix: make tempo changer work using ChatGPT !!!!!!! (truly magical) 2025-05-03 13:48:17 +07:00
5d7b576f53
fix: set default locale to Locale.ROOT 2025-05-03 08:53:51 +07:00
711f3e2885
refactor: clean up Main class a bit 2025-05-03 08:42:41 +07:00
5dc3be1994
fix: use interpret in tracked core 2025-05-02 20:32:25 +07:00
a01f749577
refactor: use fastutil 💨💨💨
i hope i didn't miss some things
2025-05-02 15:00:31 +07:00
a3ccacf758
feat: re-add icu self-care that is more accurate with code RIPPED from craftbukkit. 2025-05-01 18:16:09 +07:00
a30f4661b2
feat: rainbow armor (most useless on the bot but only for when it's unvanished) 2025-05-01 16:02:52 +07:00
05601e4cef
fix: limit TranslateCommand chat types 2025-05-01 15:17:15 +07:00
d362919c55
fix: hopefully fix increment next block times 2025-05-01 14:16:55 +07:00
5f4292b240
feat: *cb without arguments showing info 2025-05-01 12:33:40 +07:00
66ab8ceb07
refactor: show chomens mod verified message encrypted via chomens mod instead of tellraw 2025-05-01 11:48:07 +07:00
c8c82df0b4
refactor: improve AuthPlugin second counter to use time update from server instead of local to account for lags 2025-05-01 11:43:19 +07:00
527517ab9e
fix: emergency fix tracked core getting wrong output WRONG IP TRACKING !!!!!!!!!!!!! 2025-05-01 10:59:02 +07:00
9d9955444b
refactor: make core increment use bitwise operators + some fixes and improvements
line 351
> - int y = -64
> + int y = bot.world.minY;
this makes the core never resize when in worlds like the end or nether (which has the min y level as 0)

i'm also limiting the maximum resizing Y level now to the world's maximum y level so it won't overflow

also for some reason when i switch to flatlands using `/world 3` the server doesn't send the current dimension's data (like `min_y` and `height`), so i think the server sends them before at like, login? or are we meant to default to overworld's values?
2025-05-01 10:21:18 +07:00
b8068d72ff
fix: make core resize in core if possible 2025-04-30 20:27:53 +07:00
4e15434863
feat: from utf8 lossy with <2122> like vim 2025-04-30 17:43:33 +07:00
96f5f66f01
fix: support Shift-JIS encoding for midi for japanese MIDIs 2025-04-30 17:24:23 +07:00
a7f6cac49b
feat: support styles in chat types + fix memory leak for chat types 💀
took longer than i thought because i was dumb :(
2025-04-29 20:43:57 +07:00
572f4c2119
feat: unset slot after place block core ran 2025-04-29 19:20:26 +07:00
5c87e95c30
fix: harry zhou NBS code not loading song "Note Block Megacollab" that is 19 minutes (short overflow... absolute cinema) 2025-04-29 17:14:45 +07:00
dd10e6f1ab
fix: oops i meant continue 2025-04-28 12:14:52 +07:00
a744c16f20
fix: multiply NBS stereo X position by 2 so it sounds like in OpenNBS + ignore notes with 0 volume since they are pointless 2025-04-28 12:07:18 +07:00
7b6f50cfab
fix: ignore overlay messages 2025-04-27 08:37:11 +07:00
76b7df54e2
refactor: use time updates from the server for second tick 2025-04-26 11:11:33 +07:00
896b75c40f
fix: ip getting on optboom 2025-04-26 08:08:16 +07:00
d4a56051ce
fix: make the brand payload valid 2025-04-26 07:46:59 +07:00
4fbff56231
fix: update language file 💀 2025-04-23 19:20:22 +07:00
b1925edf00
refactor: make some constant things that are List immutable by using List.of() 2025-04-23 16:15:17 +07:00
c90840265a
refactor: improve language stuff in ComponentUtilities 2025-04-23 16:08:05 +07:00
2ab3a4322a
refactor: use java 21 switch case ahh thing for packet checking 2025-04-23 15:20:29 +07:00
6a93b3ef63
refactor: some improvements to listener manager 2025-04-23 15:16:53 +07:00
00e7bd59c0
fix: some debug shit about the ticker being completely frozen 2025-04-23 08:28:36 +07:00
2b8185222a
refactor: remove failed to set block error logging for the chunk thing since it spams the logs a lot 2025-04-22 19:15:57 +07:00
ac319b6281
feat: accept discord messages when the bot isn't logged in + some refactors in CorePlugin and GuildMessageEventHandler 2025-04-22 17:36:35 +07:00
8290473631
fix: console sender being null when the bot is not logged in to that server 2025-04-22 17:13:32 +07:00
788f5e88e6
fix: core refill custom name not working on <1.21.5 2025-04-22 16:53:39 +07:00
4861443bd8
fix: full temporary fix for the events + final changes before merging to master 2025-04-22 15:34:04 +07:00
181b907ac7
fix: core command block ids and states & bossbar manager text display broken 2025-04-22 15:34:04 +07:00
db56afd319
feat: initial 1.21.5 with adventure still not being updated 2025-04-22 15:34:02 +07:00
ac9c26bcd0
fix: some dumb NPE bossbar fix 2025-04-22 10:09:35 +07:00
ceb9bfd34d
fix: bruhify showing ?? for characters like the fire emoji 🔥 2025-04-22 09:09:55 +07:00
8b4286bc31
refactor: use the executor service for eval bridge functions 2025-04-21 20:28:48 +07:00
f1d24b8a59
fix: AAAA I COMMIT THE TEST CODE 2025-04-21 20:23:43 +07:00
b8ea989026
fix: make the lists actually thread-safe (maybe, but pretty likely)
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Collections.html#synchronizedList(java.util.List)
> It is imperative that the user manually synchronize on the returned list when traversing it via Iterator, Spliterator or Stream
> Failure to follow this advice may result in non-deterministic behavior.
2025-04-21 20:15:47 +07:00
1bb4d1713e
refactor: some improvements and fixes to bossbar manager + fix some NPEs 2025-04-21 14:43:44 +07:00
061371f40b
refactor: move all information things for chomens mod into the encrypted payload and some improvements 2025-04-19 15:34:38 +07:00
14bdf45c89
feat: draft for extras messaging on chomens mod, not sure how to implement on minecraft side :( 2025-04-19 14:05:27 +07:00
2f65276225
feat: use this unicode for paused music 2025-04-18 19:19:05 +07:00
53860c3c5a
feat: alias .console server to .csvr for lazy people like me 2025-04-18 16:19:18 +07:00
2e79c7599f
refactor: make commandsPer* public in CorePlugin (tryna debug something) 2025-04-18 14:43:57 +07:00
d639fa7f6b
fix: some nbs songs not loading (layers issue) 2025-04-18 11:41:35 +07:00
2cc5e6dc9c
fix: partially fix music speed 2025-04-18 08:43:01 +07:00
0bd20d2163
fix: music player using the global ticker instead of the local one 2025-04-17 19:15:07 +07:00
56e3aba21a
feat: proper flags parser stolen from HBot
now code looks a lot less messy
2025-04-17 10:48:12 +07:00
20d89b14d7
fix: some fixes 2025-04-17 08:57:26 +07:00
bb3635137c
feat: re-add ChronoUnit cloop unit that should actually work this time 2025-04-16 19:55:33 +07:00
d746140005
fix: command suggestions and chomens mod messages leaking & fix command suggestions
yes you can probably guess i rewrote the string "_request_command_suggestion" by hand instead of copying and pasting
2025-04-16 17:02:52 +07:00
a3b6f7fecf
fix: music bossbar Paused never getting shown 2025-04-16 15:41:37 +07:00
decd15a926
refactor: have global Listener interface instead of creating one in each plugin
not sure how much this improves the code, readability and/or the performance but i hope they are better
2025-04-16 15:34:20 +07:00
346c55c3d6
fix: comment out nbs Tempo Changer causing empty gaps after tempo has been changed
refactor: some refactors i've done while inspecting the issue lol
feat: re-add note counts
2025-04-16 11:05:01 +07:00
ef15c45619
fix: limit query queue size (in the case where there are 10,000 players in the server)
i discovered this while botting my own google cloud shell clone
2025-04-15 13:22:15 +07:00
c6e8585246
refactor: fix some ide warnings 2025-04-15 11:23:34 +07:00
fcc816d0c9
style: commit code styles 2025-04-15 10:26:14 +07:00
4c5fa6d733
refactor: make the discord messages log before hash and other things
now it looks like this https://i.imgur.com/BmW4HuH.png
2025-04-15 08:27:22 +07:00
f2250c656e
refactor: remove jackson annotations from some classes that don't need them anymore 2025-04-15 08:21:24 +07:00
5cd1cc8976
refactor: change translate API again 2025-04-15 08:20:57 +07:00
f06369d112
fix: use new google translate API no more ratelimit error 2025-04-14 18:47:20 +07:00
6cf6fa1ae0
refactor: use only a single Map for discord logs things
not sure how much it improves the performance but it shouldn't be much different
2025-04-14 15:43:24 +07:00
97152d7bd0
refactor: change command handler executeCommand signature to be more clean 2025-04-14 09:07:41 +07:00
b7807686d0
fix: patch negative music values causing unexpected behavior 2025-04-14 08:22:32 +07:00
26ca1eb830
refactor: ginlang's request & some other refactors to *music info 2025-04-14 08:20:26 +07:00
de43240755
feat: ditch TeamJoinerPlugin to prevent people performing suspicious activities with the bot 2025-04-13 17:36:58 +07:00
ae03603e9d
fix: i CANNOT handle that single extra connect message after getting suppressed 2025-04-13 08:36:32 +07:00
8bce548c09
refactor: remove imposter format checker since it's pretty much useless these days, and it's also disabled in the main instance 2025-04-12 15:02:29 +07:00
b9386f86e1
fix: netcmd still executing on all server even when bot not logged in 2025-04-12 11:59:29 +07:00
4685e37b4f
fix: netcmd needing hash, hopefully i don't leak anything
more spaghetti code :D
2025-04-12 11:53:29 +07:00
30a7bcd24b
feat: *netcmd 2025-04-12 10:48:45 +07:00
0ca9990d08
fix: hopefully fix duplicate player entry when someone /username (still can't exactly reproduce idk) 2025-04-12 08:01:56 +07:00
b9207ffb0c
feat: log discord messages to logger (very simple, just contents ONLY) 2025-04-11 18:27:37 +07:00
a5602d1a01
feat: "hashing" via discord by DM-ing the bot hash 2025-04-11 17:08:23 +07:00
9557231010
refactor: only schedule discord ticking once, globally, not each for every server 2025-04-11 14:17:37 +07:00
359cf9a19c
refactor: move the discord message handler into a separate class to make the mess only live in that class
refactor: use ConcurrentHashMap instead of HashMap in the discord message queue
2025-04-11 14:08:39 +07:00
9e486ebbe1
fix: increase max time a bit but i'm not sure if this will make the bot crash with translate crash spam 2025-04-11 14:00:27 +07:00
057ee9095f
fix: selfcare being scheduled every single login, causing spam and memory leak
caused while i refactor the thing
2025-04-11 11:00:25 +07:00
ee6811ccd2
refactor: increase 10MB download & REMOVE AUTO NBS INSTRUMENT SUBTITLES COMPLETIONS IT BREAKS SO MUCH 2025-04-11 08:34:52 +07:00
563df838c5
fix: add depth and reduce max time (not sure if depth will break things, pretty sure not) 2025-04-11 08:20:42 +07:00
a87e1df9c1
fix: catch PatternSyntaxException in CommandBlockCommand 2025-04-10 17:30:38 +07:00
79bb8cc877
fix: more strict regex 2025-04-10 17:11:21 +07:00
dbd4cdb656
refactor: remove invalid comment 2025-04-10 14:08:54 +07:00
185b6d4305
refactor: use JDA's Message.INVITE_PATTERN to replace the discord.gg invite things 2025-04-10 14:01:40 +07:00
dfdc959a16
fix: use legacySection() instead of ampersand, even though it breaks color codes, i still want the url stuff to work because of discord and things 2025-04-10 13:48:07 +07:00
7cd77fe850
refactor: use the built-in adventure LegacyComponentSerializer extractUrls in the serializer builder
still does not fix `&d` leaking and breaking the click event and shit
2025-04-10 13:41:20 +07:00
29ca22101a
feat: *music volume (finally lol) 2025-04-10 11:14:49 +07:00
21ba029e42
refactor: make the core still work while being cloop tp'ed
fix: escape core custom name in fill command (finally)
feat: refill using chat while being cloop tp'ed, command turns to my fork of chipmunkmod (fill x y z x y z command_block), no custom name
2025-04-10 09:30:10 +07:00
0c29f2f8ca
refactor: default coreCommandSpy to true since all clones have them now 2025-04-10 08:38:21 +07:00
58fcd586a4
refactor: improve connection message suppressing in discord and logger
i also saw nbot, fnf bot, and other bots copying the message, so i changed that too, hopefully no one skids it again
2025-04-10 08:10:40 +07:00
70bf2574cf
fix: all null pointers in disconnect listeners (there was so much)
i didn't even know how the bot even functioned for the whole 5 months without me noticing
2025-04-10 07:58:39 +07:00
76eadb1472
fix: discord disconnect message broken (blame VoiceChatPlugin! not discord's fault!) 2025-04-10 07:45:36 +07:00
49209ab8b4
fix: process translation formats the same way as minecraft 2025-04-10 07:29:30 +07:00
94f90575d8
fix: player filter not work 2025-04-09 19:20:51 +07:00
72cbad0a99
fix: emergency fix discord and other stuff not working because of last commit 2025-04-09 19:04:48 +07:00
f811000a07
refactor: make all plugins final to prevent spiders on the cpu/ram setting them to null
tbh if that actually happens then i think they can still be null lol
the bot should just crash at that point...
2025-04-09 17:40:03 +07:00
ed4e7573a9
refactor: use final on basically everything
i'm kinda tired of the inconsistency since some stuff are non-final and some are final so i just made everything final, not sure how much it impacts on stuff but everything should be fine, there were around 1,400 replacements according to the idea code analyzer
2025-04-09 17:05:11 +07:00
2764673f21
feat: also log suspected chomens mod replay attack 👀 2025-04-09 15:14:09 +07:00
31f54ee8f8
refactor: rename command output packet to message packet 2025-04-09 15:03:09 +07:00
ed71cdc55a
feat: prevent replay attacks on chomens mod for those kind of server owners and aren't very nice and also the kind of people that spies on cores (like me !!!) 2025-04-09 14:57:46 +07:00
54f0f36952
refactor: some fixes and improvements to filtering and ip querying stuff
- ip filter delay decreased to 5 seconds, since we already got the `ip` field in PlayerEntry so we can check however we want without spamming the server console (though we still do have to worry about resource usages)
- validate the regex in FilterCommand and log the regex error to console instead of chat, though it shouldn't happen anymore since we got the validation in the command already
- use the newly made event `queriedPlayerIP` in the players plugin listener instead of `playerJoined`, since the ip will 95% be null and will basically just do nothing
2025-04-09 11:01:38 +07:00
297b84801f
refactor: improve and fix bossbar manager 2025-04-09 09:04:56 +07:00
ffdfcb421d
fix: player list NOT thread safe.... 2025-04-09 08:58:01 +07:00
82aec3869b
fix: essentials message component parsing being completely white, no colors at all
section sign parsing is also kinda broken i guess
2025-04-08 20:04:46 +07:00
80aec4fca8
fix: hopefully patch chinese chat crash (idk)
refactor: rename `addPlural`
2025-04-08 19:35:38 +07:00
dd2b8ce26d
fix: duplicate connected player in chomens mod 2025-04-08 17:59:01 +07:00
dffc22674b
feat: show IP in ListCommand 2025-04-08 17:07:34 +07:00
93f582bba0
feat: smp-like encryption for chomens mod instead of public-private keys 2025-04-08 16:42:17 +07:00
11f08138fe
fix: use stringifyDiscordAnsi instead of stringifyAnsi on discord disconnect message 2025-04-08 11:03:41 +07:00
16cde68c4a
refactor: make consoleOnly parameter in Command constructor optional
i did the refactoring command-by-command this time, just to make sure everything's fine (yes i know this can still be done with idea replacing thing)
2025-04-08 09:18:35 +07:00
31ace7334e
refactor: disallow some chat packet types for some commands 2025-04-08 09:08:59 +07:00
50ba4e1c41
refactor: change log archive limit to 500mb and also log to console (probably won't help much if there's a lot of log though) 2025-04-08 08:11:53 +07:00
37326ede51
fix: FINALLY fix chat parser styles leaking !!!
still a lot of duplicate/redundant/unnecessary codes though, might want to optimize them later
2025-04-07 17:59:42 +07:00
572d175c17
fix: fix keybind component !!! 2025-04-07 16:35:09 +07:00
1a2d9c136c
refactor: improve NamedTextColor and style getting in ComponentUtilities 2025-04-07 15:27:29 +07:00
607a131fa5
fix: also include discord.com 2025-04-07 13:49:40 +07:00
00a374104d
fix: mabe 2025-04-07 08:18:15 +07:00
5e06c7cf8b
refactor: drop support for creayun since shit is getting messy with it 2025-04-07 08:05:07 +07:00
c0390b806f
refactor: replace ONLY discord.gg with discord{zwsp}.{zwsp}gg instead of every single . with {zwsp}.{zwsp}
the reason i have to do this is because people keep copying players ips from `/seen` logs (which contain zwsp) and put them into ipfilter, and since they contain zwsp, they'll never work, gonna consider adding IP regex check
2025-04-07 07:59:33 +07:00
1c7cd3038c
refactor: very small change in BotVisibilityCommand that caught my eye 2025-04-06 14:33:30 +07:00
af67c228ce
refactor: FINALLY improve the color palette, no more ColorUtilities.getColorByString(bot.config.colorPalette.something) every time i want use a color palette 2025-04-06 14:32:11 +07:00
59749d85df
refactor: add return messages to ClearChatQueueCommand and RefillCoreCommand to reduce a lot of player confusion 2025-04-06 08:49:27 +07:00
ae91b3d78b
fix: make chat splitting 4x faster which fixes *eval run bridge.chat('a'.repeat(69420)) along with all chat issues chat splitting all things FIXED. 2025-04-06 08:39:36 +07:00
65cc3971af
refactor: make queries less spammy in console like bukkit.lastKnownName and seen <uuid>
fix: `*mail sendselecteditem` and `*music playitem` invalid/doesn't exist/empty check not working
2025-04-05 17:52:08 +07:00
ac44bedf8c
feat: remove ICU self care 2025-04-04 10:05:53 +07:00
a711e21437
fix: delete loader thread on *music stop and disconnect 2025-04-03 20:27:54 +07:00
db1be06b17
fix: database throwing errors LoL 2025-04-03 11:02:07 +07:00
884c525f2e
refactor: make executeCommand in CommandHandlerPlugin return void instead of a Component 2025-04-03 10:50:41 +07:00
386bf69e7a
refactor: rename static executors to uppercase snake case 2025-04-03 10:12:04 +07:00
8ce017f7d0
refactor: inline thread name 2025-04-03 10:02:23 +07:00
e80530c847
refactor: some console things 2025-04-03 10:00:24 +07:00
13385de5d5
refactor: improve the CommandHandlerPlugin a bit 2025-04-03 09:46:37 +07:00
9d6b0d8dc4
fix: socialspy self care not working 2025-04-03 08:20:39 +07:00
8c4aae3401
refactor: use uuid on nick self care 2025-04-03 08:11:23 +07:00
b8bea93754
fix: accidentally removed vanish enable detector 2025-04-02 09:34:14 +07:00
1d7190dd44
fix: the previous commit actually broke everything that uses the global plugins
this is why you should test your code on production THEN commit and push your changes
2025-04-01 19:51:50 +07:00
58e722ba06
refactor: make global plugins in Bot use the one from the Main class
and also initialize LoggerPlugin in Bot because i was dumb lol, now it's the first plugin to load in each bot
2025-04-01 19:34:44 +07:00
7617818720
fix: make voicechat work again & make the 2 years old code modern 2025-04-01 19:04:38 +07:00
2fd205bd61
refactor: run Code Inspection and fix most of the warnings 2025-04-01 10:15:50 +07:00
6edb683274
refactor: finally fix deprecation warning in the simple voice chat FriendlyByteBuf thingy 2025-04-01 10:04:42 +07:00
90323d3552
refactor: Reformat Code with chome NS code style !!!
too lazy to export it somewhere, if you need it feel free to ask me
2025-04-01 09:58:31 +07:00
156a5af47f
feat: /tp cooldown when going down from height limit 2025-03-31 17:13:56 +07:00
bb0900eeb6
refactor: make packetReceived listeners private
Intelligent IDEA Replacing Feature.
2025-03-31 16:55:52 +07:00
dddead69ba
fix: lazy fix for the empty string username kind of people 2025-03-31 15:22:59 +07:00
3ee759211b
refactor: correct way to get the target (i didn't know the server sends it) 2025-03-31 14:59:45 +07:00
e0f0b4c174
refactor: make PlayerMessage a record 2025-03-31 14:54:27 +07:00
6c98da4184
fix: core not being relocated when over simulation distance 2025-03-31 14:27:50 +07:00
2196e822d5
feat: support ANSI color/styles in discord reply and also some refactors in ComponentUtilities 2025-03-31 10:48:11 +07:00
92c4726fcd
refactor: move command contexts into a package 2025-03-31 09:03:32 +07:00
6f92ea38d5
fix: netmsg click to open URL not working 2025-03-30 19:04:32 +07:00
e68f4c874c
fix: tld length in url regex
according to a random stackoverflow answer DNS allows 1 - 63 range.
2025-03-30 18:22:57 +07:00
821bfe2626
fix: players removing fix again 2025-03-30 18:12:40 +07:00
363d7a9270
fix: whitespace exploit in ICU selfcare. FIX 2025-03-30 17:44:40 +07:00
9e7351dca8
fix: LoL forgor to remove some stuff in Configuration for the previous commit 2025-03-30 17:39:08 +07:00
9c6c35677c
feat: better icu self care, no more spam garbage
it actually works pretty well, now it doesn't spam when you cloop tp the bot or something
also i have tested cloop tp-ing the bot, and it indeed can't refill the core, not really sure how to fix this, but the bot usually is in vanish anyway and this scenario usually happens with some random person doing clooping `tp @a <someone/somewhere>` which affects everyone (except if the selector is like `@a[name=!BubbleCode]`)
also interestingly the bot can still refill the core while being obd leashed
2025-03-30 17:33:57 +07:00
56aae42a94
feat: support URL in discord, netmsg, and console
feat: support copy to clipboard on NetMessageCommand messages
2025-03-30 17:18:35 +07:00
c70e8b5779
feat: latency and gamemode in ListCommand 2025-03-30 17:02:38 +07:00
8c65eb0ec1
refactor: t_packet 2025-03-30 16:55:06 +07:00
1f2355aa77
fix: player left event being called twice !!
refactor: some refactors inside the player plugin too
2025-03-30 16:52:46 +07:00
209cd1a6dd
refactor: improve vanish selfcare & BotVisibilityCommand 2025-03-30 16:30:12 +07:00
7c6c2c23ce
refactor: make the stuff in QueryPlugin a bit more readable and also completely remove the fucky auto remover to eliminate all issues. 2025-03-30 13:15:32 +07:00
50addd1abd
fix: /msg outgoing message target fix
fix: forgor to merge the namespace remover with the one moved to StringUtilities lol
2025-03-28 20:34:12 +07:00
517e685c8d
refactor: attempt to make the bot more thread-safe (specifically the core) 2025-03-27 18:21:16 +07:00
1e6421a17c
feat: chunking on chomens mod (only sending for now)
fix: `writeString` not working correctly (i put the normal string length instead of the bytes length lol)
2025-03-26 18:32:59 +07:00
d9e22906ef
fix: transfer state never actually getting sent to the server
since we are making a new Session once we reconnect, and we also didn't migrate the old flags, setting CLIENT_TRANSFERRING to true doesn't mean anything
2025-03-26 15:29:55 +07:00
dd629d1dfa
fix: make seen not run on non-essentials servers 2025-03-26 10:53:51 +07:00
450b2a7b51
feat: support styles in WikipediaCommand (using HTML magic) 2025-03-26 09:54:05 +07:00
b6133600f5
refactor: improve the chat messages checking a bit 2025-03-26 08:52:21 +07:00
05854223b1
refactor: second ticker in TickPlugin 2025-03-25 20:05:50 +07:00
a2b564abf5
feat: make the chomens mod integration feel more like chomens proxy and also fix AuthPlugin
most stuff are still pretty broken, there's no chunking things yet, so long datas will simply just break, but it works!
2025-03-25 19:14:27 +07:00
265a35080b
feat: chomens mod integration (completely unused)
i will probably make it a server instead idk, right now i just wanted to make the chipmunkmod core refill silent
2025-03-25 10:10:23 +07:00
043efe9547
fix: last commit didn't fix it lol 2025-03-24 17:02:54 +07:00
87ef3b8a95
fix: small bug in InfoCommand lol 2025-03-24 17:00:25 +07:00
e37d3b1a6e
feat: support ascii85 in *music playitem 2025-03-24 16:56:52 +07:00
b61d7ee2fa
refactor: some refactors in MusicCommand 2025-03-24 16:37:12 +07:00
2740c4d87b
fix: actually use long in TimestampUtilities 2025-03-24 16:37:02 +07:00
bfbed35c06
feat: use chat types from registry & some other refactors in ChatPlugin
past me didn't know that you don't have to define `bool` shit
2025-03-24 10:37:18 +07:00
15c8301f65
feat: add clear into filter 2025-03-23 15:47:39 +07:00
90a43ad2c0
refactor, feat: rewrite DiscordPlugin a bit to make it more readable and support message forwarding 2025-03-23 15:34:27 +07:00
0a8efe0189
refactor: make listeners an interface
(except the one in Bot, which extends MCProtocolLib's SessionAdaptor, not sure how exactly make them complement)
2025-03-23 14:18:55 +07:00
60356a5eea
fix: remove the debug exception logging (oopsies) 2025-03-23 08:45:06 +07:00
3b200d8a2b
feat: re-add AuthPlugin 2025-03-23 08:41:12 +07:00
d77c05389e
fix: forgor to remove debug line 💀 2025-03-22 19:08:35 +07:00
cc635ca8c6
refactor: fix FindAltsCommand (again) but also attempt to make it more readable 2025-03-22 15:39:02 +07:00
210dd7aaa1
fix: some fixes to make the bot work on bagel fabric kaboom clone and also vanilla servers
can't believe that paragraph in InfoCommand was wrong the whole time ("works on vanilla servers too")
2025-03-22 11:39:00 +07:00
83ac7632c6
fix: console hotfix 2025-03-22 10:50:20 +07:00
4a752ef0a2
fix: RefillCoreCommand not working correctly 2025-03-21 17:13:38 +07:00
185b835f7e
refactor: improve HelpCommand 2025-03-21 16:42:51 +07:00
8a9b123f49
refactor: store player ip in PlayerEntry, so we don't have to query them in stuff like IP filter which spams console 2025-03-21 16:31:23 +07:00
5834a0584a
fix: finally fix inaccurate player position (it was caused by me not handling ClientboundEntityPositionSyncPacket) 2025-03-21 14:22:06 +07:00
38aab5d1f6
refactor: move some things idk
this will probably improve performance by 1 nanosecond
2025-03-21 11:09:56 +07:00
6f8fb1d3b6
feat: customizable console chat format 2025-03-21 10:39:46 +07:00
37d7bbe2a7
fix: .console server not working correctly (chome ns moment)
it probably happened when i was doing the `getServerString` replacements and accidentally replaced `eachBot` with `bot`
2025-03-21 10:27:44 +07:00
bed87d2396
refactor: check server features using commands packet instead of tab completion (now tab completion is completely unused!) 2025-03-20 16:13:53 +07:00
5587565325
fix: *cb command output component root's style getting overwritten with white 2025-03-19 20:02:33 +07:00
9d6fc74a34
feat: support all OpenNBS hidden features (including rainbow easter egg !!!) 2025-03-19 18:40:00 +07:00
a933653112
fix: checkOverloadArgs not working correctly 2025-03-19 17:25:19 +07:00
0d0c8a2bce
refactor: remove TODO in RandomStringUtilities since the alphanumeric username is how we identify chomens bot 2025-03-19 17:07:44 +07:00
0bf8ce54f8
refactor: reduce the messy trust level checking by at least a bit
it looks a lot better now, the if statements at the discord roles still look pretty messy though
2025-03-19 17:05:36 +07:00
079c579b0e
fix: core tracked query issue 2025-03-19 15:14:24 +07:00
5fdb86c81c
feat: *cb {uuid/username{...}} showing [player] (output) 2025-03-19 14:50:29 +07:00
85b394c86d
fix: increase the query timeout even more to 5 minutes 2025-03-19 14:12:35 +07:00
04b0377bd9
refactor: use long in cloop interval instead of int
feat: `getLong` in CommandContext (i didn't know it doesn't exist lol)
2025-03-19 09:39:16 +07:00
07b61540ef
refactor: rewrite list command
fix: set `target.listed` in PlayersPlugin (vanishing)
2025-03-19 09:28:34 +07:00
3ce56fc110
feat: don't use tab complete in PlayersPlugin 2025-03-19 09:08:48 +07:00
52130e85ae
Revert "feat: use ChronoUnit for cloop (forever cloop now real!!!)"
This reverts commit 04a8d10e1f.
2025-03-18 19:20:01 +07:00
04a8d10e1f
feat: use ChronoUnit for cloop (forever cloop now real!!!) 2025-03-18 19:05:31 +07:00
f882c15d39
refactor: remove useless / in tab complete command
minecraft code:
```
if (stringReader.canRead() && stringReader.peek() == '/') {
	stringReader.skip();
}
```
i also removed `minecraft:` in the vanish detection tab complete, this should not break anything on kaboom servers
2025-03-18 17:05:49 +07:00
670c678ced
refactor: fix mess in essentials selfcare 2025-03-18 17:00:52 +07:00
c6adc4cc0f
feat: smarter username selfcare by checking if time difference < 2 seconds 2025-03-18 16:53:09 +07:00
04fd9e8e17
feat: add isSupported to ExtrasMessengerPlugin 2025-03-17 16:55:31 +07:00
7270354573
chore: bump a lot of dependencies
haven't updated them for a long time lol
2025-03-17 16:39:18 +07:00
669ddb38f2
refactor: change default color in default config to gray instead of white (like the main instance) 2025-03-17 14:15:57 +07:00
a0e32ad146
refactor: only tellraw to players without the nomusic tags 2025-03-17 14:14:53 +07:00
164f097249
refactor: reduce duplicate code in MusicPlayerPlugin:loadSong 2025-03-17 14:11:14 +07:00
dd0d80f265
refactor: remove useless exception throwing when song loading failed 2025-03-17 14:10:09 +07:00
e02de9232d
fix: hopefully fix discord ticker breaking ??? 2025-03-17 07:54:43 +07:00
60350a1adb
fix: limit the queue amount on the database executor 2025-03-16 20:39:12 +07:00
8e20cf2285
feat: automatically unmount when being ridden to an entity 2025-03-16 17:37:09 +07:00
3771143e49
fix: fix FindAltsCommand for player name with spaces 2025-03-16 13:45:41 +07:00
bee4fb5d5b
fix: send ServerboundPlayerLoadedPacket 2025-03-16 13:21:23 +07:00
c86e2714e6
feat: cloop interval units (now we can loop at 1 chomeNS and crash the server :D)
feat: show possible values of the enum in the command context's `getEnum`
2025-03-16 12:53:27 +07:00
fb8dd58de1
fix: no it did not fix that 2025-03-16 09:25:37 +07:00
d75611ff6f
fix: hopefully fix the pointer overflow 2025-03-16 08:55:17 +07:00
381b3bdc69
feat: core resizing (finally !!!) 2025-03-16 08:48:21 +07:00
553def4a3e
fix: interval in QueryPlugin being too short causing output leaks 2025-03-16 07:40:00 +07:00
0f80598881
fix: small memory leak fix in QueryPlugin
refactor: make the transactions and the uuids public for debugging/checking via servereval on production instance
2025-03-15 17:14:07 +07:00
e7ac8f9a7e
fix: 69 DDOS exploit in eval 2025-03-15 07:52:24 +07:00
65837f2afd
refactor: make the core increment at least a bit faster
i know i can use some bitwise operators or something smarter but i kinda just got lazy lol
2025-03-14 18:24:45 +07:00
a375818487
refactor: remove the exception logging from the previous commit's debug lines since it fills up the bot's log 2025-03-14 14:01:34 +07:00
9ad02a35c9
?: add debug lines to the chunk setblock to see more clearly about what is causing error 2025-03-13 14:58:41 +07:00
f6a3cad40a
fix: some core refilling issues when switching to dimensions with different minimum Y levels like minecraft:the_end 2025-03-13 09:02:18 +07:00
2a225b9169
fix: *music playitem on 1.21.4 (finally) 2025-03-13 08:16:32 +07:00
6bc879b1a7
feat: copyable discord messages 2025-03-12 08:14:36 +07:00
6bab5e50ec
refactor: remove outofmemoryerror check because we don't need it anymore 2025-03-12 07:53:47 +07:00
d75e04b2d3
fix: lazy fix for the arabica kind of kaboom clone 2025-03-12 07:49:44 +07:00
a00a77763c
fix: FilterCommand add usage 2025-03-11 19:25:30 +07:00
4ef0266d1d
fix: custom name not showing on >= 1.21.4 place block core
feat: check for invalid custom name JSON in the place block core
2025-03-11 09:11:00 +07:00
aeb94a871a
fix: update to latest extras messenger & also fix minecraft:register available channels check 2025-03-10 13:48:02 +07:00
fc51221035
feat: use a maintained version of luajava instead of the one from 2014 2025-03-10 09:06:44 +07:00
d11eb4d0f6
refactor: \0 2025-03-09 17:50:09 +07:00
f24258ccf2
refactor: fix the TODOs in ExtrasMessengerPlugin 2025-03-09 17:35:00 +07:00
7069c7d0ab
refactor: change extras messenger _ to :
i didn't know it accepts all ASCII chars
2025-03-09 17:12:13 +07:00
2b328c3d0d
refactor: only do trusted broadcast in console ONCE & ignore broadcast if server is same as broadcast origin 2025-03-09 16:21:31 +07:00
b9381755c1
refactor: rewrite logging stuff 2025-03-09 16:06:49 +07:00
e58e951943
feat: extras messaging !!! (unused for now) 2025-03-09 14:18:59 +07:00
c002868c47
fix: NPE every time eval gets run 2025-03-09 14:18:11 +07:00
7febe0705a
refactor: rewrite a bit about transfer (but still transfer to same server) 2025-03-08 08:00:18 +07:00
171b3836df
refactor: more strict commandspy children size check 2025-03-07 15:05:37 +07:00
34acf10e82
refactor: use HashMap#remove on the transactions instead of getting then removing 2025-03-06 19:35:01 +07:00
2d67f616be
fix: memory leaks (surprisingly a lot)
can't believe i forgot to remove some elements....... especially in the query plugin, which is used A LOT by the tracked core, wows.......
most of the leaks are caused by me not clearing the transactions IDs map, back in the days i didn't even know i have to do those since i didn't know i have to manage memory stuff
2025-03-06 19:03:13 +07:00
ece89a68c5
refactor: clear server plugins on disconnected (ServerPluginsManagerPlugin) 2025-03-06 18:53:27 +07:00
f3b31bde85
fix: discord enabled check in GrepLogCommand never works 2025-03-06 18:45:03 +07:00
e1eb52ce2e
refactor: fix very small grammar error in DiscordPlugin 2025-03-06 18:44:26 +07:00
7b08c0dc36
feat: migrate to mariadb (finally) 2025-03-06 18:04:18 +07:00
d962fa325b
refactor: use @p[... instead of @a[limit=1,... on selector 2025-03-06 09:26:35 +07:00
60bf424f27
refactor: optimize getting player IPs from database stuff 2025-03-06 09:21:02 +07:00
fbe6f1711b
fix: there's some unknown issue with y index being out of bounds for some reason on kaboom ONLY 2025-03-05 16:59:12 +07:00
02b9fd4f0b
refactor: optimize the bot even more!!!
it improved a lot, memory usage is pretty good now, just reducing the amount of threads for each executor and adding some more delay to the filtering stuff
2025-03-05 15:44:44 +07:00
de4e3e8e96
refactor: remove unused arguments in HashingPlugin.checkHash 2025-03-05 15:10:05 +07:00
e1c04f89a9
refactor: optimize the bot by at least a little bit 2025-03-05 15:06:35 +07:00
63d6eaa359
refactor: change fizz custom instrument sound to block.fire.extinguish 2025-03-05 12:39:58 +07:00
c4504df4ce
fix: some fixes to NBS custom instruments replacement 2025-03-05 11:54:03 +07:00
e9052c83da
refactor: reindent some files 2025-03-05 11:12:05 +07:00
3b4c8f5fda
refactor: move session listener out to Bot instead of making an anonymous class inside session.addListener() 2025-03-05 11:04:57 +07:00
76034805a0
refactor: use string UUIDs instead of int array when possible 2025-03-05 10:54:18 +07:00
0a70b17c6f
refactor: fix chipmunk's TODO 2025-03-05 10:50:34 +07:00
bb766652f6
feat: hidden servers 2025-03-05 10:47:27 +07:00
4e37021b50
feat: JVM memory usage in *info server 2025-03-03 17:43:14 +07:00
d2fd3860f9
refactor: better replacements for nbs custom instruments 2025-03-03 16:56:37 +07:00
54be006f7a
refactor: check for finite float/double 2025-03-03 12:55:35 +07:00
2000662247
fix: FINALLY fix discord ctrl+c stopping message not showing 2025-03-03 12:45:58 +07:00
c976878997
fix: some changes to custom pitch pitch 2025-03-03 12:33:14 +07:00
f3ffa19bfa
refactor: remove debug song loading error logging in SongLoaderThread & some minor refactors with the comments 2025-03-02 15:38:12 +07:00
04c7c64c9e
fix: FINALLY FIX THE PITCH ISSUE AFTER 2 YEARS !!!!!!! ISSUE RESOLVED 2025/02/03 2:40 PM WOWWWWWWWWWWWWW 2025-03-02 14:45:23 +07:00
d309e7c96c
fix: fix the config not found message never showing 2025-03-02 13:31:51 +07:00
ed5b89acba
refactor: add component and level to TrustLevel and some style improvements 2025-03-01 19:48:25 +07:00
f4cb86effb
feat: more garbage 2025-03-01 19:23:07 +07:00
633ae80aac
refactor: remove TagPlugin since we have teams now and it also spams the server console 2025-03-01 08:33:57 +07:00
31236e77b0
fix: FINALLY fix nbs stereo issue (it was wrong calculation)
the unsigned byte caused it, everything works now but the timing pitch or whatever those stuff are, are still not fixed yet, but NBS with stereo sounds a lot better now
2025-02-23 14:32:39 +07:00
25c353f588
refactor: ability to enable/disable PacketSnifferPlugin through servereval 2025-02-22 13:45:00 +07:00
9d0e67a86e
chore: update gradle wrapper 2025-02-21 12:35:19 +07:00
c8ccdfd561
feat: discord stickers
the getMessageComponent is getting VERY messy lol i will probably refactor it but for now it's gonna be very hard to read
2025-02-21 12:26:05 +07:00
3684cde6a7
refactor: make console ANSIs look the EXACT SAME as in minecraft (uses rgb) 2025-02-21 11:52:41 +07:00
2912e6c9c7
refactor: better way to patch HUGE_TRANSLATOR without also breaking other things 2025-02-21 11:28:54 +07:00
31bdc301be
fix: some more fixes 2025-02-21 11:14:47 +07:00
fc1d88b1d4
feat: packet send in PacketSnifferPlugin 2025-02-21 10:33:29 +07:00
3428ca49ad
fix: D..3k+EV:.+C]82+CS_qAKZ)5+DPh/DBNA)GpdYU@r-($AH 2025-02-21 10:33:02 +07:00
eba10601d1
refactor: use SkinPart.VALUES instead of hardcoding all skin parts 2025-02-21 08:28:30 +07:00
d44a29528c
refactor: organize stuff in data package 2025-02-19 16:15:05 +07:00
b534c57f0f
fix: harry zhou's mistake 2025-02-19 09:03:59 +07:00
f91f9ef5ff
fix: more fix 2025-02-19 09:03:39 +07:00
fbc68057b8
fix: 2025-02-19 08:55:05 +07:00
1c70b978a9
fix: some chunk issue OOB thing
feat: redirect and some other things
2025-02-19 08:37:52 +07:00
c1d269b812
feat: FINALLY chunk parsing with actual working core complete check 2025-02-18 20:41:51 +07:00
2462fdd361
refactor: only relocate core when the old core chunk isn't loaded 2025-02-18 19:41:50 +07:00
98eace0386
feat: simulation distance in WorldPlugin 2025-02-18 19:41:09 +07:00
c682597fb8
refactor: remove unnecessary try-catch in DiscordPlugin ticker 2025-02-18 19:08:11 +07:00
859b599af9
refactor: only add s to unit when amount > 1 (for *info uptime) 2025-02-17 15:28:46 +07:00
8b5e706769
refactor: bot.getServerString() to reduce mess 2025-02-16 08:03:38 +07:00
4061bc5874
fix: discord attachment not working for non-image files
feat: hoverEvent on [Attachment] (showing the filename)
2025-02-15 19:10:40 +07:00
6ba3a1cd2d
refactor: make the bot disabled message only show when command is valid 2025-02-13 18:32:21 +07:00
ea671ec4d2
feat: alias botlogintime to InfoCommand 2025-02-13 18:29:47 +07:00
186d2a4f4c
feat: option to enable ClearChatNameAnnouncerPlugin in config 2025-02-12 07:03:54 +07:00
10a7f1ab1c
feat: respond to ClientboundCustomQueryPacket 2025-02-12 06:49:05 +07:00
9224c6661c
not fix: core place block thingy issue (VERY ANNOYING)
another issue is the discord randomly breaking (not sending log messages, but messages sent using sendMessageInstantly still works)
2025-02-04 16:37:16 +07:00
6c9766b25a
feat: ip filter reason 2025-01-26 08:49:03 +07:00
e4256436ad
fix: Comparison method violates its general contract! exception fix in FindAltsCommand 2025-01-21 16:26:21 +07:00
dbb8c2479f
fix: hopefully fix volume clamp issue in music player 2025-01-19 08:20:45 +07:00
7d72776806 Merge pull request 'Small refactors' () from amy/chomens-bot:master into master
i actually was planning to update mcprotocollib today lol
2025-01-18 19:53:36 -05:00
4063385ac2
fix: hotfix for the bot not being able to reconnect 2025-01-19 07:52:22 +07:00
b73940453d
refactor: adventure-ify colorutilities 2025-01-18 14:22:54 -03:00
13ad030b82
fix: get the run task working in IntelliJ 2025-01-18 14:22:54 -03:00
fc9b993c49
chore: fixes for latest MCProtocolLib 2025-01-18 14:22:54 -03:00
90fe3d1145
feat?: replace ANSI codes and ZWSP for discord message with nothing so we can read the bot's log message easily from in game 2025-01-18 16:43:14 +07:00
4478d0287d
fix: hopefully fix ghost players for PlayersPlugin but it probably won't 2025-01-18 16:18:51 +07:00
9434c16e70
refactor: remove the ghost player for filters list in FilterManagerPlugin every tick instead of relying on the player left event 2025-01-18 16:17:40 +07:00
655a52b28d
fix: i forgot the previous commit will actually make all filter systems useless. i ended up doing something else lazy instead to fix this 2025-01-18 15:31:43 +07:00
e96e8bc951
fix: removing or those stuff on IP filtering 2025-01-18 15:28:05 +07:00
ae7fe63846
refactor: use a central filtering manager 2025-01-18 15:20:15 +07:00
54389f2e2b
feat: alias InfoCommand to discord, creator, botuser, and uptime which on each alias just does the action in the main info 2025-01-18 13:38:31 +07:00
a8e4b65355
refactor: some alts refactoring and maybe fixes 2025-01-17 19:53:15 +07:00
af78fe2611
refactor: remove unused decimal formatter 2025-01-17 18:55:11 +07:00
d19e02b3a7
not fix: attempt to fix NBS pitch(?) garbage and still failed 2025-01-16 15:04:08 +07:00
a1a3be3ee5
feat: all server support for FindAltsCommand 2025-01-14 17:18:34 +07:00
72fde45465
fix: also set the last seen entry on join to prevent empty last seen entries 2025-01-14 16:51:55 +07:00
6a524a6595
fix: lazily fix integer overflow on translate placeholder %6942000000000000$s 2025-01-08 17:10:20 +07:00
eacd91a369
refactor: remove awaitTermination 2025-01-07 17:14:30 +07:00
ae8be94be3
refactor: rewrite AuthPlugin to be based on IP 2025-01-07 17:03:45 +07:00
6164483e11
refactor: turn off allowsListing in client information packet 2025-01-07 16:30:03 +07:00
7c861e1869
refactor: use the new latest getFirst() instead of get(0) 2025-01-06 19:46:03 +07:00
ebc7560d2e
refactor: make filter reason greedy since it's the last argument 2025-01-06 18:51:01 +07:00
793b1538a0
feat: ACTUALLY print the errors using the thing i did in the commit before this 2025-01-06 17:54:35 +07:00
4f93704459
feat: print stack traces using LoggerUtilities
refactor: use the `console` in Main to access `reader` which can then `printAbove()`
2025-01-06 17:24:51 +07:00
d503416337
fix: deprecation warnings for URL 2025-01-06 17:05:03 +07:00
fd0d69c000
feat: shutdown hook and stopping reason 2025-01-06 17:01:36 +07:00
5e82250e37
refactor: improve a LOT of stuff in music playing
here are some details:
- made a new ticker instead of using the tick plugin to reduce lag
- removed notes displaying to reduce the amount of core commands the bot has to run
- added notes count to *music info since we removed the notes count in the bossbar
- some minor changes to make the code more readable
- changed note volume to multiply by 2
- changed block position range from 4 to 5
2025-01-05 17:48:21 +07:00
f3e502682c
fix: no comment 2025-01-04 10:52:44 +07:00
da998c050a
emergency fix: discord message with attachment having no space when it should 2025-01-03 18:40:23 +07:00
693cfe0f42
fix: reset on end parse (ComponentUtilities.ComponentParser) 2025-01-03 18:27:28 +07:00
0cf8d94ff9
fix: duplicate entry check for IPFilterCommand too 2025-01-03 17:54:40 +07:00
145a7fe2fe
fix: add [reason] to filter command (forgor 💀) 2025-01-03 17:50:19 +07:00
b7f2023977
feat: filter reason
fix: check for duplicate entries (filter)
refactor: some other refactorings in the command
2025-01-03 17:48:01 +07:00
a90eec985a
feat: quote parsing in argument parser 2025-01-03 16:33:54 +07:00
f7f3813ec8
refactor: getAction in context 2025-01-03 14:41:37 +07:00
1acc0fdbfd
fix: 95% WORKING PARSER NOW YAY (styles still get stuck sometimes) 2025-01-03 14:32:38 +07:00
1d8ec221ad
fix: disguised chat showing only name (forgor to commit) 2025-01-02 18:43:00 +07:00
82ecf3833d
fix: some block position stereo thingy (extend range so it's more noticeable) 2025-01-01 19:24:30 +07:00
309683bde3
feat: JDA 5.2.2 (bump) 2024-12-31 17:20:44 +07:00
eed3073ee7
refactor: turn back to using getContentDisplay because the stripped one doesn't look good 2024-12-31 09:19:29 +07:00
ba5f88df36
feat: support replying in discord
refactor: some minor refactoring in the discord plugin too like moving the get component part into a function
2024-12-31 09:17:08 +07:00
cb487b5939
refactor: weather command refactoring which includes more features 2024-12-30 14:31:41 +07:00
e4743fd503
URGENT FIX: bot using EXTREMELY HIGH MEMORY (icu self care blame) 2024-12-30 13:38:00 +07:00
0bae8b268c
feat,refactor: reply discord command outputs in a file if the output is > 2000 & use UTF-8 for getBytes() (does it make a difference?) 2024-12-30 08:50:23 +07:00
6cbcdd8737
fix: player join bossbar not showing 2024-12-29 18:55:17 +07:00
d20f7532bf
fix: only show [click for usages] when in game 2024-12-29 18:45:21 +07:00
15ea941da7
fix: command block states 2024-12-29 18:08:34 +07:00
c3ae616292
refactor: use database to handle getPlayerIP when possible & some misc refactor with findalts 2024-12-29 17:54:47 +07:00
9413b493ea
refactor: remove notes per second in music along with the completely wrong calculation (very genius) 2024-12-29 16:39:10 +07:00
8948d04495
fix: a bit of update but still not fully fix. 2024-12-29 12:19:44 +07:00
4d9c1ca99b
feat: uuid support for getEntry 2024-12-29 08:03:16 +07:00
e50f9cf433
feat: 1.21.4 2024-12-28 20:10:11 +07:00
052e57a799
refactor: rewrite ComponentUtilities component parser (still a bit broken but at least a bit better on the codestyle side) 2024-12-28 17:55:04 +07:00
9b675bfed4
fix: NullPointerException when *music loop current is run without song playing 2024-12-28 13:54:31 +07:00
4ace6afccc
refactor: don't remove bossbar when skip is ran and there are songs left to play 2024-12-28 13:49:14 +07:00
0a42fb68b2
fix: ignore tpsbar on if it is already enabled 2024-12-28 13:41:33 +07:00
9c75b6309f
refactor: use Component.equals on bossbar title checking and new placeholder title 2024-12-28 13:38:57 +07:00
90aef4e594
fix: fix stopping when database is disabled 2024-12-28 13:21:20 +07:00
a1da3dbc2c
refactor: dev files into .gitignore 2024-12-28 13:16:49 +07:00
a912ee012a
refactor: fix typo in the last commit (chome ns moment) 2024-12-26 15:47:51 +07:00
05337f0274
refactor: change bossbarNamespace to namespace since its invalid
first commit on this arch install !!!
2024-12-26 15:46:00 +07:00
2df59435d0
fix: some emergency issue i don't know what is causing 2024-12-26 08:55:47 +07:00
8217606437
refactor: [click for usages] when invalid argument 2024-12-25 08:54:05 +07:00
497155a827
refactor: show disabled message and make music locking for non-console sender instead of all 2024-12-25 08:41:09 +07:00
eba4f92e45
feat: stop/restart reason 2024-12-25 08:08:01 +07:00
8e84565090
fix: forgor to remove the duplicate entry on unvanish 2024-12-21 15:41:36 +07:00
3a25f28635
feat: check /username 2024-12-21 15:40:23 +07:00
54c0efb447
fix: incoming /w not working 2024-12-21 14:08:52 +07:00
50dc16bee4
fix: fix disconnect 20 -> 6 (last commit i think) 2024-12-19 16:23:21 +07:00
c504918571
fix: does nothing 2024-12-18 20:10:15 +07:00
cdaa625962
refactor: some changes to newTotalConnects changing to 6 2024-12-18 18:23:33 +07:00
e798a9dc18
refactor: make validate return the trust level 2024-12-18 17:35:31 +07:00
c1efc1c3d5
refactor: use switch-case in stringPartially in ComponentUtilities (now the bot requires java >= 21) 2024-12-17 18:44:53 +07:00
36fa5b3f9b
feat: RestartCommand 2024-12-17 16:36:35 +07:00
2340991012
refactor: rename bossBarNamespace to namespace for global and some other config things 2024-12-17 16:36:19 +07:00
0995c12b70
feat: add players as an alias to list 2024-12-17 16:23:17 +07:00
e72d9c8bb8
fix/refactor: some stuff in ChatPlugin 2024-12-15 15:33:14 +07:00
f5a3599adb
fix: fix mail read also returning the output 2024-12-15 12:47:56 +07:00
7e42a5cf43
fix: even more ansi color fix, but now it's section sign color codes 2024-12-14 17:16:54 +07:00
e9de7eb801
fix: ansi extra depth color fix (thanks Yaode_owo for mentioning) 2024-12-14 17:08:49 +07:00
5a06ee53b6
fix: actually handle stopping the database when the bot is stopping 2024-12-14 16:54:28 +07:00
6a11f237ef
fix: fix getting invalid player data in database throwing error 2024-12-14 16:54:09 +07:00
621f06f2e5
BIG feat,fix,refactor: use database and some minor refactoring which also fixes memory issues and * related to PersistentDataUtilities 2024-12-14 15:39:24 +07:00
d09b9b37db
fix: finally fix icu self care spam 2024-12-14 15:22:33 +07:00
7e4139cebc
fix: lock.lock(); 2024-12-10 17:53:18 +07:00
634c8cf087
fix: misc irc send tick 2024-12-10 17:49:47 +07:00
aa871dc080
fix: more garbage try-catch es 2024-12-10 17:37:43 +07:00
a541371ce1
style: use stringifyAnsi() on irc command output 2024-12-10 15:59:48 +07:00
ab85cc8e2a
fix: finally fix the IRC message 2024-12-10 15:52:25 +07:00
15835d7ac6
feat,refactor: add click event and insertion for HelpCommand (idea from Werkku) and some refactoring 2024-12-10 14:03:44 +07:00
935dfdcb32
fix: some team stuff 2024-12-10 08:23:38 +07:00
24b7ada58f
refactor: misc irc stuff 2024-12-10 08:23:20 +07:00
6dc47be63e
fix,refactor: fix command block states and some refactoring to CorePlugin 2024-12-07 18:43:48 +07:00
1d3b51cd3f
feat: toString() for data classes and some other more 2024-12-07 18:25:01 +07:00
1afcfde5e5
fix: finally fix going up from void 2024-12-07 18:10:34 +07:00
b3beac6c3b
feat,refactor: query plugin which also means refactor 2024-12-07 17:59:18 +07:00
9b320d64b5
refactor: move minecraft:brand custom payload to the Bot class 2024-12-07 16:07:27 +07:00
bf70930cd8
feat: re-add ClearChatNameAnnouncerPlugin (it's under a different name) 2024-12-07 16:01:18 +07:00
c2bab61c09
feat: update to 1.21.2-1.21.3 protocol
i tried compiling for 1.21.4, and it seemed to work, but most clones' viaversion is not that updated yet so i just stayed at 1.21.3
2024-12-07 14:15:46 +07:00
c2cdf8bcf9
feat: finally add customizable essentials messages 2024-12-05 15:20:32 +07:00
79b6ae13a6
refactor: imports refactoring in ChatPlugin 2024-12-05 15:02:18 +07:00
2c517b7326
fix: fix for unlimited 2024-12-05 14:47:17 +07:00
4600eaf942
refactor: use Thread for console line processing 2024-12-05 14:46:13 +07:00
f25618d4c1
refactor: refactor some stuff in the CorePlugin 2024-12-05 14:02:57 +07:00
cde1e0d07a
fix: mabe 2024-12-05 14:02:28 +07:00
5f15ea2d4d
fix: hopefully finally fix it 2024-12-05 13:39:10 +07:00
7e09ea3257
fix: add login checks before sending packet when the state is not PLAY 2024-12-05 13:37:35 +07:00
f2c9d3a30e
fix: *** laziest fix i have ever done 2024-12-05 11:53:40 +07:00
55f4d1fece
fix: more weird kind of "fixes" 2024-12-05 11:27:51 +07:00
d0d28cc55b
"fix": nothing 2024-12-05 10:27:46 +07:00
808e29c0af
fix: maybe some more concurrent fard fix 2024-12-05 10:20:07 +07:00
3782c97d21
refactor: some refactoring to ChatCommandHandlerPlugin 2024-12-05 09:42:03 +07:00
7fba5711d1
feat,refactor: migrate to jackson and some refactoring/optimizations 2024-12-05 09:27:48 +07:00
bd4ceda4f1
feat,refactor: team joiner plugin and some refactoring 2024-12-04 19:49:24 +07:00
20cd93a826
refactor: change tag interval to 2 minutes 2024-12-04 19:07:00 +07:00
4ef6e334b0
fix: an emergency fix that probably doesn't work 2024-12-03 20:17:04 +07:00
e045d88cfc
fix: fix format checker even though it's not going to be used 2024-12-03 18:34:30 +07:00
f83ead52bb
refactor: getOrDefault in PersistentDataUtilities 2024-12-03 16:24:59 +07:00
c1be374457
HOTFIX: persistent data not writing to file when stopping 2024-12-02 18:04:28 +07:00
89dc2e2781
fix,refactor: /teammsg color fix and some refactoring to the get chat type function 2024-12-01 15:37:56 +07:00
1ebb798077
feat: max translate depth (messy !) 2024-12-01 14:32:52 +07:00
bb0bc090df
fix: small fix for runTracked throwing when the bot is not logged in 2024-11-30 19:07:35 +07:00
36a138becc
feat,fix: add admin trust level and some other fixies 2024-11-30 15:03:02 +07:00
651db6c8ce
fix: fix the fix that actually caused more problems 2024-11-30 14:10:35 +07:00
ed4c171f11
fix: make checkAllPlayers run in the ExecutorService to reduce the login lag by at least a bit 2024-11-28 18:42:42 +07:00
65bae4ce7d
refactor: make TagPlugin interval do every 30 seconds instead of 20 seconds 2024-11-28 18:42:10 +07:00
dc023758f2
refactor: make bruhify a part of the tick listener
i don't even know how i missed this
2024-11-28 18:07:59 +07:00
f7187e560f
refactor: some refactors in Main, backup and config 2024-11-28 17:27:54 +07:00
60a1d069c7
fix: make jsonObject in PersistentDataUtilities synchronous (hopefully) 2024-11-28 16:50:24 +07:00
06522fadcc
feat: stop skids from making 69420 clones + some more stuff 2024-11-27 16:58:18 +07:00
392120f28b
fix: disable some stuff in the default config 2024-11-24 17:06:55 +07:00
975199bf4f
feat: build info
i have this plan for quite a while now, finally i am able to do this
2024-11-24 16:20:27 +07:00
d8c8a9750d
refactor: make handleLine private in ConsolePlugin 2024-11-23 17:29:10 +07:00
832d6ee5fa
feat: *info botlogintime 2024-11-22 17:02:35 +07:00
a45477ed1b
refactor: run idea's "Code Cleanup" 2024-11-22 16:28:17 +07:00
249 changed files with 14817 additions and 8092 deletions
.gitignoreREADME.mdbuild-number.txtbuild.gradlecodestyle.xml
gradle/wrapper
gradlewinspections.xml
src/main/java/me/chayapak1/chomens_bot
Bot.javaConfiguration.javaMain.java
chatParsers
chomeNSMod
chunk
command
commands
data

8
.gitignore vendored
View file

@ -9,8 +9,14 @@ run/
*.ipr
*.iws
# Exploits plugin
# Exploits stuff
src/main/java/me/chayapak1/chomens_bot/plugins/ExploitsPlugin.java
src/main/java/me/chayapak1/chomens_bot/data/exploitMethods/
# Testing plugins
src/main/java/me/chayapak1/chomens_bot/plugins/testing
# Bot's files (made when running via ./gradlew run)
config.yml
logs/
songs/

View file

@ -1,27 +1,22 @@
# ChomeNS Bot Java
A Java verison of ChomeNS Bot.
A Java version of ChomeNS Bot.
# Compiling
You will see that the exploits plugin is missing because I gitignored it to prevent exploit leaks
To make this successfully compile just make `ExploitsPlugin.java` in the plugins folder and add this code:
To make this successfully compile, you will have to fix the missing `ExploitsPlugin.java` in the `plugins` package, and also some enums related to it.
```java
package me.chayapak1.chomens_bot.plugins;
After that, you can now run `./gradlew build` to actually get the `.jar` file.
import me.chayapak1.chomens_bot.Bot;
The .jar file will be at `build/libs`, to run the bot simply do `java -jar chomens_bot-rolling-all.jar`
import java.util.UUID;
# Development
public class ExploitsPlugin {
public ExploitsPlugin (Bot bot) {}
When commiting your changes, please use my code style.
public void kick (UUID uuid) {}
public void pcrash (UUID uuid) {}
}
```
In IntelliJ IDEA:
Then at the root of the project run `./gradlew build` to build.
`Ctrl + Alt + S`, search `Code Style`, go to `Java`, click gear icon, `Import Scheme -> IntelliJ IDEA code style XML`,
use the `codestyle.xml` file in this repository
The .jar file will be at `build/libs`, to run the bot do `java -jar chomens_bot-rolling-all.jar`
`Ctrl + Alt + Shift + H`, click `Configure Inspections...`, click gear icon, `Import Profile...`,
use the `inspections.xml` file in this repository

1
build-number.txt Normal file
View file

@ -0,0 +1 @@
3459

View file

@ -1,3 +1,5 @@
import java.text.SimpleDateFormat
plugins {
id 'java'
id 'application'
@ -7,7 +9,7 @@ plugins {
group = 'me.chayapak1'
version = 'rolling'
description = 'ChomeNS Bot'
java.sourceCompatibility = JavaVersion.VERSION_17
java.sourceCompatibility = JavaVersion.VERSION_21
repositories {
mavenLocal()
@ -39,28 +41,83 @@ repositories {
}
dependencies {
implementation 'org.geysermc.mcprotocollib:protocol:1.21-SNAPSHOT'
implementation 'net.kyori:adventure-text-serializer-ansi:4.15.0'
implementation 'com.google.code.gson:gson:2.11.0'
implementation 'com.google.guava:guava:31.1-jre'
implementation 'org.jline:jline:3.23.0'
implementation 'ch.qos.logback:logback-classic:1.5.11'
implementation 'org.geysermc.mcprotocollib:protocol:1.21.5-SNAPSHOT'
implementation 'net.kyori:adventure-text-serializer-plain:4.21.0'
implementation 'net.kyori:adventure-text-serializer-legacy:4.21.0'
implementation 'net.kyori:adventure-text-serializer-ansi:4.21.0'
implementation 'com.google.code.gson:gson:2.13.1'
implementation 'com.google.guava:guava:33.4.8-jre'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.19.0'
implementation 'org.mariadb.jdbc:mariadb-java-client:3.5.3'
implementation 'org.jline:jline:3.30.3'
implementation 'it.unimi.dsi:fastutil:8.5.15'
implementation 'ch.qos.logback:logback-classic:1.5.18'
implementation 'com.github.pircbotx:pircbotx:2.3.1'
implementation 'com.github.ricksbrown:cowsay:1.1.0'
implementation 'org.yaml:snakeyaml:2.0'
implementation 'org.luaj:luaj-jse:3.0.1'
implementation 'net.dv8tion:JDA:5.0.0-beta.12'
implementation 'net.kyori:adventure-text-serializer-legacy:4.15.0'
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.20.0'
implementation 'io.socket:socket.io-client:2.1.0'
implementation 'org.yaml:snakeyaml:2.4'
implementation 'party.iroiro.luajava:luajava:4.0.2'
implementation 'party.iroiro.luajava:lua54:4.0.2'
runtimeOnly 'party.iroiro.luajava:lua54-platform:4.0.2:natives-desktop' // what is this? is this necessary?
implementation 'net.dv8tion:JDA:5.5.1'
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.24.3'
implementation 'io.socket:socket.io-client:2.1.2'
implementation 'de.maxhenkel.opus4j:opus4j:2.0.2'
implementation 'org.concentus:Concentus:1.0-SNAPSHOT'
}
static def getGitCommitHash() {
try {
return 'git rev-parse --short HEAD'.execute().text.trim()
} catch (Exception e) {
e.printStackTrace()
return "unknown"
}
}
static def getGitCommitCount() {
try {
return 'git rev-list --count HEAD'.execute().text.trim()
} catch (Exception e) {
e.printStackTrace()
return "unknown"
}
}
def buildNumberFile = file("build-number.txt")
def buildNumber = 0
if (buildNumberFile.exists()) {
buildNumber = buildNumberFile.text.trim().toInteger() + 1
} else {
buildNumber = 1
}
buildNumberFile.text = buildNumber
ext.buildInfo = [
gitCommitCount: getGitCommitCount(),
compileDate: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").format(new Date()),
gitCommitHash: getGitCommitHash(),
buildNumber: buildNumber,
]
processResources {
inputs.property("buildInfo", buildInfo)
filesMatching("application.properties") {
expand(buildInfo)
}
}
application {
mainClass = 'me.chayapak1.chomens_bot.Main'
}
run {
standardInput = System.in
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
}

30
codestyle.xml Normal file
View file

@ -0,0 +1,30 @@
<code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="GENERATE_FINAL_LOCALS" value="true" />
<option name="GENERATE_FINAL_PARAMETERS" value="true" />
<option name="VISIBILITY" value="protected" />
<option name="SPACE_BEFORE_OPENING_ANGLE_BRACKET_IN_TYPE_PARAMETER" value="true" />
<option name="SPACE_BEFORE_DECONSTRUCTION_LIST" value="true" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="120" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="SPACE_WITHIN_BRACES" value="true" />
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
<option name="SPACE_BEFORE_METHOD_PARENTHESES" value="true" />
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
<option name="KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE" value="true" />
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

3
gradlew vendored
View file

@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

9
inspections.xml Normal file
View file

@ -0,0 +1,9 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true">
<option name="REPORT_VARIABLES" value="true" />
<option name="REPORT_PARAMETERS" value="true" />
</inspection_tool>
</profile>
</component>

View file

@ -1,30 +1,54 @@
package me.chayapak1.chomens_bot;
import it.unimi.dsi.fastutil.objects.ObjectList;
import me.chayapak1.chomens_bot.data.color.ColorPalette;
import me.chayapak1.chomens_bot.data.listener.Listener;
import me.chayapak1.chomens_bot.plugins.*;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.RandomStringUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.network.BuiltinFlags;
import org.geysermc.mcprotocollib.network.ClientSession;
import org.geysermc.mcprotocollib.network.Session;
import org.geysermc.mcprotocollib.network.event.session.*;
import org.geysermc.mcprotocollib.network.factory.ClientNetworkSessionFactory;
import org.geysermc.mcprotocollib.network.packet.Packet;
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession;
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.HandPreference;
import org.geysermc.mcprotocollib.protocol.data.game.setting.ChatVisibility;
import org.geysermc.mcprotocollib.protocol.data.game.setting.ParticleStatus;
import org.geysermc.mcprotocollib.protocol.data.game.setting.SkinPart;
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundCustomPayloadPacket;
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundStoreCookiePacket;
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundTransferPacket;
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundClientInformationPacket;
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
import org.geysermc.mcprotocollib.protocol.packet.cookie.clientbound.ClientboundCookieRequestPacket;
import org.geysermc.mcprotocollib.protocol.packet.cookie.serverbound.ServerboundCookieResponsePacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundLoginPacket;
import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundGameProfilePacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundPlayerLoadedPacket;
import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundCustomQueryPacket;
import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundLoginCompressionPacket;
import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundLoginFinishedPacket;
import org.geysermc.mcprotocollib.protocol.packet.login.serverbound.ServerboundCustomQueryAnswerPacket;
import org.geysermc.mcprotocollib.protocol.packet.login.serverbound.ServerboundLoginAcknowledgedPacket;
import java.util.ArrayList;
import java.util.List;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Bot {
private final ArrayList<Listener> listeners = new ArrayList<>();
public class Bot extends SessionAdapter {
private static final List<String> NEEDS_DELAY_DISCONNECT_REASON = ObjectList.of(
"Wait 5 seconds before connecting, thanks! :)",
"You are logging in too fast, try again later.",
"Connection throttled! Please wait before reconnecting."
);
public final String host;
public final int port;
@ -32,6 +56,7 @@ public class Bot {
public final Configuration.BotOption options;
public final Configuration config;
public final ColorPalette colorPalette;
public final List<Bot> bots;
@ -39,57 +64,69 @@ public class Bot {
public GameProfile profile;
public Session session;
public ClientSession session;
private final Map<Key, byte[]> cookies = new HashMap<>();
private boolean isTransferring = false;
public boolean printDisconnectedCause = false;
public int connectAttempts = 0;
public boolean loggedIn = false;
public long loginTime = System.currentTimeMillis();
public final ExecutorService executorService = Main.executorService;
public final ScheduledExecutorService executor = Main.executor;
public final ExecutorService executorService = Main.EXECUTOR_SERVICE;
public final ScheduledExecutorService executor = Main.EXECUTOR;
public ConsolePlugin console;
public LoggerPlugin logger; // in ConsolePlugin
public DiscordPlugin discord; // same for this one too
public IRCPlugin irc; // AND same for this one too
public final ListenerManagerPlugin listener;
public final LoggerPlugin logger;
public final TickPlugin tick;
public final ChatPlugin chat;
public final CommandSpyPlugin commandSpy;
public final PositionPlugin position;
public final ServerFeaturesPlugin serverFeatures;
public final SelfCarePlugin selfCare;
public final QueryPlugin query;
public final ExtrasMessengerPlugin extrasMessenger;
public final WorldPlugin world;
public final CorePlugin core;
public final TeamPlugin team;
public final PlayersPlugin players;
public final TabCompletePlugin tabComplete;
public final CommandHandlerPlugin commandHandler;
public final ChatCommandHandlerPlugin chatCommandHandler;
public final BossbarManagerPlugin bossbar;
public final MusicPlayerPlugin music;
public final TPSPlugin tps;
public final EvalPlugin eval;
public final TrustedPlugin trusted;
public final GrepLogPlugin grepLog;
public final BruhifyPlugin bruhify;
public final CloopPlugin cloop;
public final ExploitsPlugin exploits;
public final FilterManagerPlugin filterManager;
public final PlayerFilterPlugin playerFilter;
public final CommandSuggestionPlugin commandSuggestion;
public final MailPlugin mail;
public final PacketSnifferPlugin packetSniffer;
public final VoiceChatPlugin voiceChat;
public final BotSelectorBroadcasterPlugin selectorBroadcaster;
public final ChomeNSModIntegrationPlugin chomeNSMod;
public final AuthPlugin auth;
public final ScreensharePlugin screenshare = null;
public final ClearChatNameAnnouncerPlugin clearChatNameAnnouncer;
public final WhitelistPlugin whitelist;
public final PlayersDatabasePlugin playersDatabase;
public final IPFilterPlugin ipFilter;
public final RainbowArmorPlugin rainbowArmor;
public TickPlugin tick;
public ChatPlugin chat;
public CommandSpyPlugin commandSpy;
public PositionPlugin position;
public ServerPluginsManagerPlugin serverPluginsManager;
public SelfCarePlugin selfCare;
public CorePlugin core;
public TeamPlugin team;
public PlayersPlugin players;
public TabCompletePlugin tabComplete;
public CommandHandlerPlugin commandHandler;
public ChatCommandHandlerPlugin chatCommandHandler;
public HashingPlugin hashing;
public BossbarManagerPlugin bossbar;
public MusicPlayerPlugin music;
public TPSPlugin tps;
public EvalPlugin eval;
public TrustedPlugin trusted;
public GrepLogPlugin grepLog;
public BruhifyPlugin bruhify;
public CloopPlugin cloop;
public ExploitsPlugin exploits;
public FilterPlugin filter;
public CommandSuggestionPlugin commandSuggestion;
public MailPlugin mail;
public PacketSnifferPlugin packetSniffer;
public VoiceChatPlugin voiceChat;
public TagPlugin tag;
public WorldPlugin world;
public AuthPlugin auth;
public ScreensharePlugin screenshare;
public FormatCheckerPlugin formatChecker;
public WhitelistPlugin whitelist;
public PlayersPersistentDataPlugin playersPersistent;
public IPFilterPlugin ipFilter;
public Bot (Configuration.BotOption botOption, List<Bot> bots, Configuration config) {
public Bot (
final Configuration.BotOption botOption,
final List<Bot> bots,
final Configuration config
) {
this.host = botOption.host;
this.port = botOption.port;
@ -98,22 +135,28 @@ public class Bot {
this.bots = bots;
this.config = config;
}
this.colorPalette = new ColorPalette(config.colorPalette);
public void connect () {
this.listener = new ListenerManagerPlugin(this);
this.tick = new TickPlugin(this);
this.chat = new ChatPlugin(this);
this.commandSpy = new CommandSpyPlugin(this);
this.query = new QueryPlugin(this);
this.extrasMessenger = new ExtrasMessengerPlugin(this);
this.chomeNSMod = new ChomeNSModIntegrationPlugin(this);
this.commandSuggestion = new CommandSuggestionPlugin(this);
this.logger = new LoggerPlugin(this);
this.position = new PositionPlugin(this);
this.serverPluginsManager = new ServerPluginsManagerPlugin(this);
this.serverFeatures = new ServerFeaturesPlugin(this);
this.selfCare = new SelfCarePlugin(this);
this.world = new WorldPlugin(this);
this.core = new CorePlugin(this);
this.team = new TeamPlugin(this);
this.playersDatabase = new PlayersDatabasePlugin(this);
this.players = new PlayersPlugin(this);
this.tabComplete = new TabCompletePlugin(this);
this.commandHandler = new CommandHandlerPlugin(this);
this.chatCommandHandler = new ChatCommandHandlerPlugin(this);
this.hashing = new HashingPlugin(this);
this.bossbar = new BossbarManagerPlugin(this);
this.music = new MusicPlayerPlugin(this);
this.tps = new TPSPlugin(this);
@ -123,166 +166,256 @@ public class Bot {
this.bruhify = new BruhifyPlugin(this);
this.cloop = new CloopPlugin(this);
this.exploits = new ExploitsPlugin(this);
this.filter = new FilterPlugin(this);
this.commandSuggestion = new CommandSuggestionPlugin(this);
this.filterManager = new FilterManagerPlugin(this);
this.playerFilter = new PlayerFilterPlugin(this);
this.mail = new MailPlugin(this);
this.packetSniffer = new PacketSnifferPlugin(this);
this.voiceChat = new VoiceChatPlugin(this);
this.tag = new TagPlugin(this);
this.world = new WorldPlugin(this);
this.selectorBroadcaster = new BotSelectorBroadcasterPlugin(this);
this.auth = new AuthPlugin(this);
// this.screenshare = new ScreensharePlugin(this);
this.formatChecker = new FormatCheckerPlugin(this);
// this.screenshare = new ScreensharePlugin(this);
this.clearChatNameAnnouncer = new ClearChatNameAnnouncerPlugin(this);
this.whitelist = new WhitelistPlugin(this);
this.playersPersistent = new PlayersPersistentDataPlugin(this);
this.ipFilter = new IPFilterPlugin(this);
this.rainbowArmor = new RainbowArmorPlugin(this);
}
for (Listener listener : listeners) listener.loadedPlugins();
protected void connect () {
reconnect();
}
private void reconnect () {
if (session != null) session = null; // does this do nothing?
connectAttempts++;
for (Listener listener : listeners) {
listener.connecting();
this.listener.dispatch(Listener::onConnecting);
if (!isTransferring) {
username = options.username == null ?
RandomStringUtilities.generate(8) :
options.username;
}
final String _username = options.username;
final ClientNetworkSessionFactory factory = ClientNetworkSessionFactory.factory()
.setAddress(host, port)
.setProtocol(
new MinecraftProtocol(
new GameProfile(
UUIDUtilities.getOfflineUUID(username),
username
),
null
)
);
if (_username == null) username = RandomStringUtilities.generate(8);
else username = _username;
if (this.session != null) factory.setPacketHandlerExecutor(this.session.getPacketHandlerExecutor());
Session session = new TcpClientSession(host, port, new MinecraftProtocol(username), null);
final ClientSession session = factory.create();
this.session = session;
// this replicates the minecraft behavior of not resolving SRV records.
// some servers check for this, so that's why i have it here
// this is still needed since MinecraftProtocol will use this flag to set the
// handshake intention to transfer, it will be set back to false at
// login success
session.setFlag(BuiltinFlags.CLIENT_TRANSFERRING, isTransferring);
session.setFlag(MinecraftConstants.FOLLOW_TRANSFERS, false); // we have our own transfer handler
session.setFlag(BuiltinFlags.ATTEMPT_SRV_RESOLVE, options.resolveSRV);
session.addListener(new SessionAdapter() {
// fard
@Override
public void packetReceived(Session session, Packet packet) {
for (SessionListener listener : listeners) {
listener.packetReceived(session, packet);
}
if (packet instanceof ClientboundLoginPacket) {
for (SessionListener listener : listeners) {
loggedIn = true;
listener.connected(new ConnectedEvent(session));
}
// this enables all the skin parts (by default they are ALL DISABLED
// which is why most bots when they use someone's skin they are just
// kinda broken)
final List<SkinPart> skinParts = new ArrayList<>();
skinParts.add(SkinPart.CAPE);
skinParts.add(SkinPart.JACKET);
skinParts.add(SkinPart.LEFT_SLEEVE);
skinParts.add(SkinPart.RIGHT_SLEEVE);
skinParts.add(SkinPart.LEFT_PANTS_LEG);
skinParts.add(SkinPart.RIGHT_PANTS_LEG);
skinParts.add(SkinPart.HAT);
// we also set other stuffs here
session.send(
new ServerboundClientInformationPacket(
ComponentUtilities.language.getOrDefault("language.code", "en-us"),
16,
ChatVisibility.FULL,
true,
skinParts,
HandPreference.RIGHT_HAND,
false,
true // should this be false lol so only real players are displayed
)
);
if (options.creayun) chat.send("/server creative");
} else if (packet instanceof ClientboundGameProfilePacket) packetReceived((ClientboundGameProfilePacket) packet);
}
public void packetReceived(ClientboundGameProfilePacket packet) {
profile = packet.getProfile();
}
@Override
public void packetSending(PacketSendingEvent packetSendingEvent) {
for (SessionListener listener : listeners) {
listener.packetSending(packetSendingEvent);
}
}
@Override
public void packetSent(Session session, Packet packet) {
for (SessionListener listener : listeners) {
listener.packetSent(session, packet);
}
}
@Override
public void packetError(PacketErrorEvent packetErrorEvent) {
for (SessionListener listener : listeners) {
listener.packetError(packetErrorEvent);
}
packetErrorEvent.setSuppress(true); // fix the ohio sus exploit
}
@Override
public void disconnecting(DisconnectingEvent disconnectingEvent) {
for (SessionListener listener : listeners) {
listener.disconnecting(disconnectingEvent);
}
}
@Override
public void disconnected(DisconnectedEvent disconnectedEvent) {
loggedIn = false;
final Throwable cause = disconnectedEvent.getCause();
if (printDisconnectedCause && cause != null) cause.printStackTrace();
// lazy fix #69420
if (cause instanceof OutOfMemoryError) System.exit(1);
int reconnectDelay = options.reconnectDelay;
final String stringMessage = ComponentUtilities.stringify(disconnectedEvent.getReason());
if (
stringMessage.equals("Wait 5 seconds before connecting, thanks! :)") ||
stringMessage.equals("You are logging in too fast, try again later.") ||
stringMessage.equals("Connection throttled! Please wait before reconnecting.")
) reconnectDelay = 1000 * (5 + 2); // 2 seconds extra delay just in case
executor.schedule(() -> reconnect(), reconnectDelay, TimeUnit.MILLISECONDS);
for (SessionListener listener : listeners) {
listener.disconnected(disconnectedEvent);
}
}
});
session.addListener(this);
session.connect(false);
}
@Override
public void packetReceived (final Session session, final Packet packet) {
this.listener.dispatch(listener -> listener.packetReceived(session, packet));
switch (packet) {
case final ClientboundLoginPacket t_packet -> packetReceived(t_packet);
case final ClientboundLoginFinishedPacket t_packet -> packetReceived(t_packet);
case final ClientboundCustomQueryPacket t_packet -> packetReceived(t_packet);
case final ClientboundCookieRequestPacket t_packet -> packetReceived(t_packet);
case final ClientboundTransferPacket t_packet -> packetReceived(t_packet);
case final ClientboundStoreCookiePacket t_packet -> packetReceived(t_packet);
case final ClientboundLoginCompressionPacket t_packet -> packetReceived(t_packet);
case final ClientboundCustomPayloadPacket t_packet -> packetReceived(t_packet);
default -> { }
}
}
private void packetReceived (final ClientboundLoginFinishedPacket packet) {
profile = packet.getProfile();
session.setFlag(BuiltinFlags.CLIENT_TRANSFERRING, false);
}
private void packetReceived (final ClientboundLoginPacket ignoredPacket) {
loggedIn = true;
loginTime = System.currentTimeMillis();
connectAttempts = 0;
this.listener.dispatch(listener -> listener.connected(new ConnectedEvent(session)));
session.send(ServerboundPlayerLoadedPacket.INSTANCE);
}
private void packetReceived (final ClientboundCustomQueryPacket packet) {
session.send(new ServerboundCustomQueryAnswerPacket(packet.getMessageId(), null));
}
private void packetReceived (final ClientboundCustomPayloadPacket packet) {
if (!packet.getChannel().asString().equals("minecraft:register")) return;
session.send(
new ServerboundCustomPayloadPacket(
Key.key("minecraft", "register"),
"\0".getBytes(StandardCharsets.UTF_8)
)
);
}
private void packetReceived (final ClientboundCookieRequestPacket packet) {
session.send(
new ServerboundCookieResponsePacket(
packet.getKey(),
cookies.get(packet.getKey())
)
);
}
private void packetReceived (final ClientboundStoreCookiePacket packet) {
cookies.put(packet.getKey(), packet.getPayload());
}
private void packetReceived (final ClientboundTransferPacket ignoredPacket) {
this.isTransferring = true;
session.disconnect(Component.translatable("disconnect.transfer"));
}
// MCProtocolLib devs forgot
// "Negative values will disable compression, meaning the packet format should remain in the uncompressed packet format."
// https://minecraft.wiki/w/Java_Edition_protocol#Set_Compression
// they actually implemented this, but at this commit:
// https://github.com/GeyserMC/MCProtocolLib/commit/f8460356db2b92fbf7cb506757fe8f87a011a1f7#diff-a9066adbcb6d5503f5edefe3ec95465cf755f1585e02b732a6fa907afe7c7177R67-L103
// they removed it, for some reason
private void packetReceived (final ClientboundLoginCompressionPacket packet) {
if (packet.getThreshold() < 0) {
session.setCompression(null);
}
}
@Override
public void packetSending (final PacketSendingEvent packetSendingEvent) {
this.listener.dispatch(listener -> listener.packetSending(packetSendingEvent));
}
@Override
public void packetSent (final Session session, final Packet packet) {
this.listener.dispatch(listener -> listener.packetSent(session, packet));
if (packet instanceof final ServerboundLoginAcknowledgedPacket t_packet) packetSent(t_packet);
}
private void packetSent (final ServerboundLoginAcknowledgedPacket ignoredPacket) {
// doesn't work without executing
session.getPacketHandlerExecutor().execute(() -> {
// for voicechat
session.send(new ServerboundCustomPayloadPacket(
Key.key("minecraft:brand"),
"\u0006fabric".getBytes(StandardCharsets.UTF_8)
));
// this enables all the skin parts (by default they are ALL DISABLED
// which is why most bots when they use someone's skin they are just
// kinda broken)
final List<SkinPart> skinParts = new ArrayList<>(Arrays.asList(SkinPart.VALUES));
// we also set other stuffs here
session.send(
new ServerboundClientInformationPacket(
ComponentUtilities.LANGUAGE.getOrDefault("language.code", "en_us"),
16,
ChatVisibility.FULL,
true,
skinParts,
HandPreference.RIGHT_HAND,
false,
false,
ParticleStatus.ALL
)
);
});
}
@Override
public void packetError (final PacketErrorEvent packetErrorEvent) {
this.listener.dispatch(listener -> listener.packetError(packetErrorEvent));
packetErrorEvent.setSuppress(true); // fixes the ohio sus exploit
}
@Override
public void disconnecting (final DisconnectingEvent disconnectingEvent) {
this.listener.dispatch(listener -> listener.disconnecting(disconnectingEvent));
}
@Override
public void disconnected (final DisconnectedEvent disconnectedEvent) {
loggedIn = false;
final Throwable cause = disconnectedEvent.getCause();
if (printDisconnectedCause && cause != null) logger.error(cause);
if (Main.stopping) return;
if (!isTransferring) cookies.clear();
if (isTransferring) {
// for now, it's going to transfer to the same server instead of
// other servers
reconnect(); // instantly reconnect
isTransferring = false;
} else {
final String stringMessage = ComponentUtilities.stringify(disconnectedEvent.getReason());
final long reconnectDelay;
if (NEEDS_DELAY_DISCONNECT_REASON.contains(stringMessage))
reconnectDelay = TimeUnit.SECONDS.toMillis(5);
else
reconnectDelay = options.reconnectDelay;
executor.schedule(this::reconnect, reconnectDelay, TimeUnit.MILLISECONDS);
}
this.listener.dispatch(listener -> listener.disconnected(disconnectedEvent));
}
public String getServerString () { return getServerString(false); }
public String getServerString (final boolean bypassHidden) {
return options.hidden && !bypassHidden ?
options.serverName :
host + ":" + port;
}
public void stop () {
session.disconnect("Received stop signal");
Main.bots.remove(this);
}
public void addListener (Listener listener) {
listeners.add(listener);
}
public static class Listener extends SessionAdapter {
public void connecting () {}
public void loadedPlugins () {}
@Override
public String toString () {
return "Bot{" +
"host='" + host + '\'' +
", port=" + port +
", username='" + username + '\'' +
", loggedIn=" + loggedIn +
'}';
}
}

View file

@ -1,9 +1,7 @@
package me.chayapak1.chomens_bot;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Configuration {
public List<String> prefixes;
@ -11,15 +9,15 @@ public class Configuration {
public String consoleCommandPrefix;
public Keys keys = new Keys();
public InternetCheck internetCheck = new InternetCheck();
public Backup backup = new Backup();
public Database database = new Database();
public ChomeNSMod chomeNSMod = new ChomeNSMod();
public String weatherApiKey;
public String bossBarNamespace = "chomens_bot";
public String namespace = "chomens_bot";
public Core core = new Core();
public Discord discord = new Discord();
@ -29,61 +27,55 @@ public class Configuration {
public Eval eval = new Eval();
public ColorPalette colorPalette = new ColorPalette();
public String ownerName = "chayapak"; // mabe mabe
public ImposterFormatChecker imposterFormatChecker = new ImposterFormatChecker();
public String consoleChatFormat = "{\"translate\":\"chat.type.text\",\"with\":[\"OWNER_NAME\",\"MESSAGE\"]}";
public OwnerAuthentication ownerAuthentication = new OwnerAuthentication();
public boolean announceClearChatUsername = false;
public boolean rainbowArmor = true;
public List<String> trusted = new ArrayList<>();
public SelfCare selfCare = new SelfCare();
public BotOption[] bots = new BotOption[]{};
public static class ImposterFormatChecker {
public boolean enabled = false;
public String key;
}
public BotOption[] bots = new BotOption[] {};
public static class OwnerAuthentication {
public boolean enabled = false;
public String key = "";
public int timeout = 6000;
}
public static class InternetCheck {
public boolean enabled = true;
public String address = "https://sus.red";
public int timeout = 10;
}
public static class Backup {
public boolean enabled = false;
public String address = "http://fard.sex/check";
public int interval = 1000;
public int failTimes = 2;
}
public static class Keys {
public String normalKey;
public String ownerKey;
public static class Database {
public boolean enabled = false;
public String address = "localhost";
public String username = "chomens_bot";
public String password = "123456";
}
public static class ChomeNSMod {
public boolean enabled = false;
public String password = "123456";
public List<String> players = new ArrayList<>();
}
public static class Core {
public Position start = new Position();
public Position end = new Position();
public int refillInterval = (60 * 5) * 1000; // 5 minutes
public String customName = "{\"text\":\"@\"}";
}
public static class Position {
public int x = 0;
public int y = 0;
public int z = 0;
}
public static class ColorPalette {
public String primary = "yellow";
public String secondary = "gold";
public String defaultColor = "white";
public String defaultColor = "gray";
public String username = "gold";
public String uuid = "aqua";
public String string = "aqua";
@ -92,25 +84,31 @@ public class Configuration {
}
public static class Discord {
public boolean enabled = true;
public boolean enabled = false;
public String prefix = "default!";
public String serverId;
public boolean enableDiscordHashing = true;
public String token;
public Map<String, String> servers = new HashMap<>();
public EmbedColors embedColors = new EmbedColors();
public String trustedRoleName = "Trusted";
public String adminRoleName = "Admin";
public String ownerRoleName = "Owner";
public String statusMessage = "Oh hi!";
public String inviteLink = "https://discord.gg/xdgCkUyaA4";
public static class EmbedColors {
public String normal = "#FFFF00";
public String error = "#FF0000";
}
}
public static class IRC {
public boolean enabled = true;
public boolean enabled = false;
public String prefix = "!";
public String host;
public int port;
public String name = "chomens-bot";
public String password = "";
public Map<String, String> servers = new HashMap<>();
}
public static class Music {
@ -122,36 +120,23 @@ public class Configuration {
}
}
public static class EmbedColors {
public String normal = "#FFFF00";
public String error = "#FF0000";
}
public static class Eval {
public String address = "ws://localhost:3069";
}
public static class SelfCare {
public int delay = 225;
public boolean op = true;
public boolean gamemode = true;
public boolean endCredits = true;
public boolean respawn = true;
public boolean vanish = true;
public boolean nickname = true;
public boolean god = true;
public boolean socialspy = true;
public boolean mute = true;
public boolean cspy = true;
public Icu icu = new Icu();
public static class Icu {
public boolean enabled = true;
public int positionPacketsPerSecond = 10;
}
public Prefix prefix = new Prefix();
public static class Prefix {
@ -159,6 +144,8 @@ public class Configuration {
public String prefix = "&8[&eChomeNS Bot&8]";
}
public boolean icu = true;
public boolean username = true;
}
@ -166,18 +153,40 @@ public class Configuration {
public String host;
public int port;
public String username;
public boolean creayun = false;
public String serverName;
public String discordChannelId;
public String ircChannel;
public boolean hidden = false;
public boolean useCore = true;
public boolean useCorePlaceBlock = false;
public boolean useChat = false;
public boolean coreCommandSpy = false;
public boolean useSNBTComponents = true;
public boolean coreCommandSpy = true;
public boolean logConnectionStatusMessages = true;
public boolean resolveSRV = true;
public int reconnectDelay = 2000;
public boolean removeNamespaces = false;
public int chatQueueDelay = 125;
public EssentialsMessages essentialsMessages = new EssentialsMessages();
public CoreRateLimit coreRateLimit = new CoreRateLimit();
public static class EssentialsMessages {
public String vanishEnable1 = "Vanish for %s: enabled";
public String vanishEnable2 = "You are now completely invisible to normal users, and hidden from in-game commands.";
public String vanishDisable = "Vanish for %s: disabled";
public String nickNameRemove = "You no longer have a nickname.";
public String nickNameSet = "Your nickname is now ";
public String godModeEnable = "God mode enabled.";
public String godModeDisable = "God mode disabled.";
public String socialSpyEnable = "SocialSpy for %s: enabled";
public String socialSpyDisable = "SocialSpy for %s: disabled";
public String muted = "You have been muted";
public String unmuted = "You have been unmuted.";
}
public static class CoreRateLimit {
public int limit = 0;
public int reset = 0;

View file

@ -1,41 +1,38 @@
package me.chayapak1.chomens_bot;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.chayapak1.chomens_bot.plugins.ConsolePlugin;
import me.chayapak1.chomens_bot.plugins.DiscordPlugin;
import me.chayapak1.chomens_bot.plugins.IRCPlugin;
import me.chayapak1.chomens_bot.plugins.LoggerPlugin;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.chayapak1.chomens_bot.plugins.*;
import me.chayapak1.chomens_bot.util.*;
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import net.kyori.adventure.text.Component;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static final List<Bot> bots = new ArrayList<>();
public static final Path stopReasonFilePath = Path.of("shutdown_reason.txt");
public static final ExecutorService executorService = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors(),
public static final List<Bot> bots = new ObjectArrayList<>();
public static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(
Math.max(1, Runtime.getRuntime().availableProcessors() / 2),
new ThreadFactoryBuilder().setNameFormat("ExecutorService #%d").build()
);
public static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(
Runtime.getRuntime().availableProcessors(),
public static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(
Math.max(1, Runtime.getRuntime().availableProcessors() / 2),
new ThreadFactoryBuilder().setNameFormat("ScheduledExecutorService #%d").build()
);
@ -43,13 +40,57 @@ public class Main {
private static boolean alreadyStarted = false;
private static boolean stopping = false;
public static boolean stopping = false;
private static final List<Thread> alreadyAddedThreads = new ArrayList<>();
private static int backupFailTimes = 0;
private static DiscordPlugin discord;
public static ConsolePlugin console;
public static DatabasePlugin database;
public static DiscordPlugin discord;
public static IRCPlugin irc;
public static void main(String[] args) throws IOException {
public static void main (final String[] args) throws IOException {
Locale.setDefault(Locale.ROOT);
loadConfig();
final Thread shutdownThread = new Thread(Main::handleShutdown, "ChomeNS Bot Shutdown Thread");
Runtime.getRuntime().addShutdownHook(shutdownThread);
if (!config.backup.enabled) {
initializeBots();
} else {
EXECUTOR.scheduleAtFixedRate(() -> {
boolean reachable;
try {
HttpUtilities.getRequest(new URI(config.backup.address).toURL());
reachable = true;
} catch (final Exception e) {
reachable = false;
}
if (!reachable && !alreadyStarted) {
backupFailTimes++;
if (backupFailTimes > config.backup.failTimes) {
LoggerUtilities.log("Main instance is down! Starting backup instance");
initializeBots();
}
} else if (reachable && alreadyStarted) {
LoggerUtilities.log("Main instance is back up! Now stopping");
// no need to reset backupFailTimes because we are stopping anyway
stop(0);
}
}, 0, config.backup.interval, TimeUnit.MILLISECONDS);
}
}
private static void loadConfig () throws IOException {
final Path configPath = Path.of("config.yml");
final Constructor constructor = new Constructor(Configuration.class, new LoaderOptions());
@ -57,151 +98,97 @@ public class Main {
if (!Files.exists(configPath)) {
// creates config file from default-config.yml
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("default-config.yml");
final InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("default-config.yml");
if (is == null) System.exit(1);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder stringBuilder = new StringBuilder();
final BufferedReader reader = new BufferedReader(new InputStreamReader(is));
final StringBuilder stringBuilder = new StringBuilder();
while (reader.ready()) {
char character = (char) reader.read();
final char character = (char) reader.read();
stringBuilder.append(character);
}
String defaultConfig = stringBuilder.toString();
final String defaultConfig = stringBuilder.toString();
// writes it
BufferedWriter configWriter = Files.newBufferedWriter(configPath);
final BufferedWriter configWriter = Files.newBufferedWriter(configPath);
configWriter.write(defaultConfig);
configWriter.close();
LoggerUtilities.info("config.yml file not found, so the default one was created");
LoggerUtilities.log("config.yml file was not found, so the default one was created. Please modify it to your needs.");
System.exit(1);
}
InputStream opt = Files.newInputStream(configPath);
BufferedReader reader = new BufferedReader(new InputStreamReader(opt));
final InputStream opt = Files.newInputStream(configPath);
final BufferedReader reader = new BufferedReader(new InputStreamReader(opt));
config = yaml.load(reader);
executor.scheduleAtFixedRate(() -> {
try {
checkInternet();
} catch (IOException e) {
e.printStackTrace();
}
}, 0, 1, TimeUnit.MINUTES);
executor.scheduleAtFixedRate(() -> {
final Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
final Thread.UncaughtExceptionHandler oldHandler = thread.getUncaughtExceptionHandler();
thread.setUncaughtExceptionHandler((_thread, throwable) -> {
if (!alreadyAddedThreads.contains(thread) && throwable instanceof OutOfMemoryError) System.exit(1);
alreadyAddedThreads.add(thread);
oldHandler.uncaughtException(_thread, throwable);
});
}
}, 0, 30, TimeUnit.SECONDS);
if (!config.backup.enabled) {
initializeBots();
return;
}
executor.scheduleAtFixedRate(() -> {
boolean reachable;
try {
HttpUtilities.getRequest(new URL(config.backup.address));
reachable = true;
} catch (Exception e) {
reachable = false;
}
if (!reachable && !alreadyStarted) {
LoggerUtilities.info("Main instance is down! Starting backup instance");
initializeBots();
} else if (reachable && alreadyStarted) {
System.exit(1);
}
}, 0, 1, TimeUnit.MINUTES);
}
public static void initializeBots() {
private static void initializeBots () {
alreadyStarted = true;
try {
if (config.database.enabled) database = new DatabasePlugin(config);
HashingUtilities.init();
final Configuration.BotOption[] botsOptions = config.bots;
for (Configuration.BotOption botOption : botsOptions) {
for (final Configuration.BotOption botOption : botsOptions) {
final Bot bot = new Bot(botOption, bots, config);
bots.add(bot);
}
// initialize util classes and plugins
PersistentDataUtilities.init();
ComponentUtilities.init();
new ConsolePlugin();
LoggerPlugin.init();
console = new ConsolePlugin(config);
if (config.discord.enabled) discord = new DiscordPlugin(config);
if (config.irc.enabled) new IRCPlugin(config);
if (config.irc.enabled) irc = new IRCPlugin(config);
EvalPlugin.connect(config.eval.address);
LoggerUtilities.info("Initialized all bots. Now connecting");
LoggerUtilities.log(I18nUtilities.get("initialized"));
for (Bot bot : bots) bot.connect();
} catch (Exception e) {
e.printStackTrace();
for (final Bot bot : bots) bot.connect();
} catch (final Exception e) {
LoggerUtilities.error(e);
System.exit(1);
}
}
private static void checkInternet () throws IOException {
if (!config.internetCheck.enabled) return;
private static void handleShutdown () {
String reason = null;
boolean reachable = false;
try {
final URL url = new URL(config.internetCheck.address);
final URLConnection connection = url.openConnection();
connection.connect();
reachable = true;
} catch (UnknownHostException ignored) {}
if (!reachable) {
LoggerUtilities.error("No internet, exiting");
System.exit(1);
if (Files.exists(stopReasonFilePath)) {
try {
reason = new String(Files.readAllBytes(stopReasonFilePath)).trim();
} catch (final IOException ignored) { }
}
stop(0, reason, false);
}
// most of these are stolen from HBot
public static void stop () {
public static void stop (final int exitCode) { stop(exitCode, null, null, true); }
public static void stop (final int exitCode, final String reason) { stop(exitCode, reason, null, true); }
public static void stop (final int exitCode, final String reason, final String type) { stop(exitCode, reason, type, true); }
public static void stop (final int exitCode, final String reason, final boolean callSystemExit) { stop(exitCode, reason, null, callSystemExit); }
public static void stop (final int exitCode, final String reason, final String type, final boolean callSystemExit) {
if (stopping) return;
else stopping = true;
stopping = true;
final String stoppingMessage = String.format(
I18nUtilities.get("info.stopping.generic"),
type != null ? type : I18nUtilities.get("info.stopping"),
reason != null ? reason : I18nUtilities.get("info.no_reason")
);
executor.shutdown();
LoggerUtilities.log(stoppingMessage);
PersistentDataUtilities.stop();
executorService.shutdown();
try {
final boolean ignoredExecutorDone = executor.awaitTermination(5, TimeUnit.SECONDS);
final boolean ignoredExecutorServiceDone = executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {}
ArrayList<Bot> copiedList;
final ArrayList<Bot> copiedList;
synchronized (bots) {
copiedList = new ArrayList<>(bots);
}
@ -209,42 +196,48 @@ public class Main {
final boolean ircEnabled = config.irc.enabled;
final boolean discordEnabled = config.discord.enabled;
if (ircEnabled) irc.quit(stoppingMessage);
final boolean[] stoppedDiscord = new boolean[copiedList.size()];
int botIndex = 0;
for (Bot bot : copiedList) {
final AtomicInteger botIndex = new AtomicInteger();
for (final Bot bot : copiedList) {
try {
if (discordEnabled) {
final String channelId = bot.discord.servers.get(bot.host + ":" + bot.port);
final String channelId = bot.options.discordChannelId;
final MessageCreateAction messageAction = bot.discord.sendMessageInstantly("Stopping..", channelId, false);
final MessageCreateAction messageAction = Main.discord.sendMessageInstantly(stoppingMessage, channelId, false);
final int currentIndex = botIndex.get();
final int finalBotIndex = botIndex;
messageAction.queue(
(message) -> stoppedDiscord[finalBotIndex] = true,
(error) -> stoppedDiscord[finalBotIndex] = true // should i also set this to true on fail?
(message) -> stoppedDiscord[currentIndex] = true,
(error) -> stoppedDiscord[currentIndex] = true // should i also set this to true on fail?
);
}
if (ircEnabled) bot.irc.quit("Stopping..");
bot.stop();
} catch (Exception ignored) {}
} catch (final Exception ignored) { }
botIndex++;
botIndex.getAndIncrement();
}
if (discord.jda != null) discord.jda.shutdown();
if (discordEnabled) {
for (int i = 0; i < 150; i++) {
try {
if (!ArrayUtilities.isAllTrue(stoppedDiscord)) Thread.sleep(50);
else break;
} catch (InterruptedException ignored) {}
} catch (final InterruptedException ignored) { }
}
if (discord != null && discord.jda != null) discord.jda.shutdown();
}
System.exit(0);
EXECUTOR.shutdown();
EXECUTOR_SERVICE.shutdown();
FileLoggerUtilities.stop();
if (database != null) database.stop();
if (callSystemExit) System.exit(exitCode);
}
}

View file

@ -1,49 +0,0 @@
package me.chayapak1.chomens_bot.chatParsers;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.chat.ChatParser;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.data.chat.PlayerMessage;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import net.kyori.adventure.text.Component;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CreayunChatParser implements ChatParser {
private final Bot bot;
// is parsing creayun chat using regex a good idea?
// mabe mabe mabe
// it doesn't use like translation or anything
private static final Pattern PATTERN = Pattern.compile("([^ ]*) » (.*)");
public CreayunChatParser (Bot bot) {
this.bot = bot;
}
@Override
public PlayerMessage parse (Component message) {
final String stringified = ComponentUtilities.stringify(message);
if (!bot.options.creayun) return null;
final Matcher matcher = PATTERN.matcher(stringified);
if (matcher.find()) {
final String displayName = matcher.group(1);
final String contents = matcher.group(2);
PlayerEntry sender = bot.players.getEntry(displayName);
if (sender == null) sender = new PlayerEntry(new GameProfile(UUIDUtilities.getOfflineUUID(displayName), displayName), GameMode.SURVIVAL, 0, Component.text(displayName), 0L, null, new byte[0], true);
return new PlayerMessage(sender, Component.text(displayName), Component.text(contents));
}
return null;
}
}

View file

@ -1,38 +1,37 @@
package me.chayapak1.chomens_bot.chatParsers;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.chat.ChatParser;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.data.chat.PlayerMessage;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import java.util.List;
import java.util.UUID;
public class KaboomChatParser implements ChatParser {
private final Bot bot;
public KaboomChatParser (Bot bot) {
public KaboomChatParser (final Bot bot) {
this.bot = bot;
}
@Override
public PlayerMessage parse (Component message) {
public PlayerMessage parse (final Component message) {
if (message instanceof TextComponent) return parse((TextComponent) message);
return null;
}
public PlayerMessage parse (TextComponent message) {
List<Component> children = message.children();
public PlayerMessage parse (final TextComponent message) {
final List<Component> children = message.children();
if (!message.content().isEmpty() || !message.style().isEmpty() || children.size() < 3) return null;
final Component prefix = children.get(0);
final Component prefix = children.getFirst();
Component displayName = Component.empty();
Component contents = Component.empty();
@ -45,17 +44,29 @@ public class KaboomChatParser implements ChatParser {
return null;
}
// final String stringifiedDisplayName = ComponentUtilities.stringify(displayName);
final String stringifiedDisplayName = ComponentUtilities.stringify(displayName);
PlayerEntry sender = bot.players.getEntry(Component.empty().append(prefix).append(displayName));
if (sender == null) sender = bot.players.getEntry(prefix.append(displayName)); // old
// if (sender == null) sender = new PlayerEntry(new GameProfile(UUIDUtilities.getOfflineUUID(stringifiedDisplayName), stringifiedDisplayName), GameMode.SURVIVAL, 0, displayName, 0L, null, new byte[0], true); // new and currently using
if (sender == null) return null;
if (sender == null) sender = bot.players.getEntry(prefix.append(displayName));
if (sender == null) sender = new PlayerEntry(
new GameProfile(
UUIDUtilities.getOfflineUUID(stringifiedDisplayName),
stringifiedDisplayName
),
GameMode.SURVIVAL,
0,
displayName,
0L,
null,
new byte[0],
true
);
return new PlayerMessage(sender, displayName, contents);
}
private boolean isSeperatorAt (List<Component> children, int start) {
private boolean isSeperatorAt (final List<Component> children, final int start) {
return (
children.get(start).equals(Component.text(":")) ||
children.get(start).equals(Component.text("§f:"))

View file

@ -1,52 +1,53 @@
package me.chayapak1.chomens_bot.chatParsers;
import it.unimi.dsi.fastutil.objects.ObjectList;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.chat.ChatParser;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.data.chat.PlayerMessage;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.TranslationArgument;
import net.kyori.adventure.text.event.HoverEvent;
import java.util.*;
import java.util.List;
import java.util.UUID;
public class MinecraftChatParser implements ChatParser {
private final Bot bot;
private static final List<String> keys = new ArrayList<>();
static {
keys.add("chat.type.text");
keys.add("chat.type.announcement");
keys.add("commands.message.display.incoming");
keys.add("chat.type.team.text");
keys.add("chat.type.emote");
}
private static final List<String> keys = ObjectList.of(
"chat.type.text",
"chat.type.announcement",
"commands.message.display.incoming",
"chat.type.team.text",
"chat.type.emote"
);
public MinecraftChatParser (Bot bot) {
public MinecraftChatParser (final Bot bot) {
this.bot = bot;
}
@Override
public PlayerMessage parse (Component message) {
public PlayerMessage parse (final Component message) {
if (message instanceof TranslatableComponent) return parse((TranslatableComponent) message);
return null;
}
public PlayerMessage parse (TranslatableComponent message) {
public PlayerMessage parse (final TranslatableComponent message) {
final List<TranslationArgument> args = message.arguments();
final String key = message.key();
if (args.size() < 2 || !keys.contains(key)) return null;
final Component senderComponent = args.get(0).asComponent();
final Component senderComponent = args.getFirst().asComponent();
final Component contents = args.get(1).asComponent();
PlayerEntry sender;
final PlayerEntry sender;
final HoverEvent<?> hoverEvent = senderComponent.hoverEvent();
if (hoverEvent != null && hoverEvent.action().equals(HoverEvent.Action.SHOW_ENTITY)) {
HoverEvent.ShowEntity entityInfo = (HoverEvent.ShowEntity) hoverEvent.value();
final HoverEvent.ShowEntity entityInfo = (HoverEvent.ShowEntity) hoverEvent.value();
final UUID senderUUID = entityInfo.id();
sender = bot.players.getEntry(senderUUID);
} else {

View file

@ -2,8 +2,8 @@ package me.chayapak1.chomens_bot.chatParsers;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.chat.ChatParser;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.data.chat.PlayerMessage;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
@ -17,28 +17,29 @@ import java.util.UUID;
public class U203aChatParser implements ChatParser {
private final Bot bot;
public U203aChatParser(Bot bot) {
public U203aChatParser (final Bot bot) {
this.bot = bot;
}
@Override
public PlayerMessage parse (Component message) {
public PlayerMessage parse (final Component message) {
if (message instanceof TranslatableComponent) return parse((TranslatableComponent) message);
return null;
}
// very similar to MinecraftChatParser
public PlayerMessage parse (TranslatableComponent message) {
public PlayerMessage parse (final TranslatableComponent message) {
final List<TranslationArgument> args = message.arguments();
if (args.size() < 3 || (!message.key().equals("[%s] %s %s") && !message.key().equals("%s %s %s"))) return null;
if (args.size() < 3 || (!message.key().equals("[%s] %s %s") && !message.key().equals("%s %s %s")))
return null;
final Component senderComponent = args.get(1).asComponent();
final Component contents = args.get(2).asComponent();
PlayerEntry sender;
final PlayerEntry sender;
final HoverEvent<?> hoverEvent = senderComponent.hoverEvent();
if (hoverEvent != null && hoverEvent.action().equals(HoverEvent.Action.SHOW_ENTITY)) {
HoverEvent.ShowEntity entityInfo = (HoverEvent.ShowEntity) hoverEvent.value();
final HoverEvent.ShowEntity entityInfo = (HoverEvent.ShowEntity) hoverEvent.value();
final UUID senderUUID = entityInfo.id();
sender = bot.players.getEntry(senderUUID);
} else {

View file

@ -0,0 +1,70 @@
package me.chayapak1.chomens_bot.chomeNSMod;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
// inspired from smp encryption plugin
// Author: ChatGPT
public class Encryptor {
private static final SecureRandom RANDOM = new SecureRandom();
private static final int SALT_LENGTH = 16;
private static final int IV_LENGTH = 16;
private static final int ITERATIONS = 65536;
private static final int KEY_LENGTH = 256;
public static byte[] encrypt (final byte[] data, final String password) throws Exception {
final byte[] salt = generateRandomBytes(SALT_LENGTH);
final SecretKey key = deriveKey(password, salt);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final byte[] iv = generateRandomBytes(IV_LENGTH);
final IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
final byte[] encrypted = cipher.doFinal(data);
final byte[] combined = new byte[salt.length + iv.length + encrypted.length];
System.arraycopy(salt, 0, combined, 0, salt.length);
System.arraycopy(iv, 0, combined, salt.length, iv.length);
System.arraycopy(encrypted, 0, combined, salt.length + iv.length, encrypted.length);
return combined;
}
public static byte[] decrypt (final byte[] combined, final String password) throws Exception {
final byte[] salt = new byte[SALT_LENGTH];
final byte[] iv = new byte[IV_LENGTH];
final byte[] encrypted = new byte[combined.length - SALT_LENGTH - IV_LENGTH];
System.arraycopy(combined, 0, salt, 0, SALT_LENGTH);
System.arraycopy(combined, SALT_LENGTH, iv, 0, IV_LENGTH);
System.arraycopy(combined, SALT_LENGTH + IV_LENGTH, encrypted, 0, encrypted.length);
final SecretKey key = deriveKey(password, salt);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
return cipher.doFinal(encrypted);
}
private static SecretKey deriveKey (final String password, final byte[] salt) throws Exception {
final SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
final KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
final SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), "AES");
}
private static byte[] generateRandomBytes (final int length) {
final byte[] bytes = new byte[length];
RANDOM.nextBytes(bytes);
return bytes;
}
}

View file

@ -0,0 +1,9 @@
package me.chayapak1.chomens_bot.chomeNSMod;
import io.netty.buffer.ByteBuf;
public interface Packet {
int getId ();
void serialize (ByteBuf buf);
}

View file

@ -0,0 +1,63 @@
package me.chayapak1.chomens_bot.chomeNSMod;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.chomeNSMod.clientboundPackets.ClientboundCoreOutputPacket;
import me.chayapak1.chomens_bot.chomeNSMod.serverboundPackets.ServerboundRunCommandPacket;
import me.chayapak1.chomens_bot.chomeNSMod.serverboundPackets.ServerboundRunCoreCommandPacket;
import me.chayapak1.chomens_bot.command.contexts.ChomeNSModCommandContext;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import net.kyori.adventure.text.Component;
import java.util.concurrent.CompletableFuture;
public class PacketHandler {
private final Bot bot;
public PacketHandler (final Bot bot) {
this.bot = bot;
}
public void handlePacket (final PlayerEntry player, final Packet packet) {
if (packet instanceof final ServerboundRunCoreCommandPacket t_packet) handlePacket(player, t_packet);
else if (packet instanceof final ServerboundRunCommandPacket t_packet) handlePacket(player, t_packet);
}
private void handlePacket (final PlayerEntry player, final ServerboundRunCoreCommandPacket packet) {
final CompletableFuture<Component> future = bot.core.runTracked(packet.command);
if (future == null) {
bot.chomeNSMod.send(
player,
new ClientboundCoreOutputPacket(
packet.runID,
Component.empty()
)
);
return;
}
future.thenApply(output -> {
bot.chomeNSMod.send(
player,
new ClientboundCoreOutputPacket(
packet.runID,
output
)
);
return null;
});
}
private void handlePacket (final PlayerEntry player, final ServerboundRunCommandPacket packet) {
final String input = packet.input; // the input is raw, no prefix included
final ChomeNSModCommandContext context = new ChomeNSModCommandContext(
bot,
player
);
bot.commandHandler.executeCommand(input, context);
}
}

View file

@ -0,0 +1,54 @@
package me.chayapak1.chomens_bot.chomeNSMod;
import io.netty.buffer.ByteBuf;
import me.chayapak1.chomens_bot.util.SNBTUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class Types {
public static UUID readUUID (final ByteBuf buf) {
final long mostSignificantBits = buf.readLong();
final long leastSignificantBits = buf.readLong();
return new UUID(mostSignificantBits, leastSignificantBits);
}
public static void writeUUID (final ByteBuf buf, final UUID uuid) {
buf.writeLong(uuid.getMostSignificantBits());
buf.writeLong(uuid.getLeastSignificantBits());
}
public static void writeString (final ByteBuf buf, final String string) {
final byte[] bytes = string.getBytes(StandardCharsets.UTF_8);
buf.writeInt(bytes.length);
buf.writeBytes(bytes);
}
public static String readString (final ByteBuf buf) {
final int length = buf.readInt();
final byte[] bytes = new byte[length];
buf.readBytes(bytes);
return new String(bytes, StandardCharsets.UTF_8);
}
public static Component readComponent (final ByteBuf buf) {
final String stringJSON = readString(buf);
try {
return GsonComponentSerializer.gson().deserialize(stringJSON);
} catch (final Exception e) {
return null;
}
}
public static void writeComponent (final ByteBuf buf, final Component component) {
final String stringJSON = SNBTUtilities.fromComponent(false, component);
writeString(buf, stringJSON);
}
}

View file

@ -0,0 +1,34 @@
package me.chayapak1.chomens_bot.chomeNSMod.clientboundPackets;
import io.netty.buffer.ByteBuf;
import me.chayapak1.chomens_bot.chomeNSMod.Packet;
import me.chayapak1.chomens_bot.chomeNSMod.Types;
import net.kyori.adventure.text.Component;
import java.util.UUID;
public class ClientboundCoreOutputPacket implements Packet {
public final UUID runID;
public final Component output;
public ClientboundCoreOutputPacket (final UUID runID, final Component output) {
this.runID = runID;
this.output = output;
}
public ClientboundCoreOutputPacket (final ByteBuf buf) {
this.runID = Types.readUUID(buf);
this.output = Types.readComponent(buf);
}
@Override
public int getId () {
return 1;
}
@Override
public void serialize (final ByteBuf buf) {
Types.writeUUID(buf, this.runID);
Types.writeComponent(buf, this.output);
}
}

View file

@ -0,0 +1,21 @@
package me.chayapak1.chomens_bot.chomeNSMod.clientboundPackets;
import io.netty.buffer.ByteBuf;
import me.chayapak1.chomens_bot.chomeNSMod.Packet;
public class ClientboundHandshakePacket implements Packet {
public ClientboundHandshakePacket () {
}
public ClientboundHandshakePacket (final ByteBuf buf) {
}
@Override
public int getId () {
return 0;
}
@Override
public void serialize (final ByteBuf buf) {
}
}

View file

@ -0,0 +1,28 @@
package me.chayapak1.chomens_bot.chomeNSMod.clientboundPackets;
import io.netty.buffer.ByteBuf;
import me.chayapak1.chomens_bot.chomeNSMod.Packet;
import me.chayapak1.chomens_bot.chomeNSMod.Types;
import net.kyori.adventure.text.Component;
public class ClientboundMessagePacket implements Packet {
public final Component message;
public ClientboundMessagePacket (final Component message) {
this.message = message;
}
public ClientboundMessagePacket (final ByteBuf buf) {
this.message = Types.readComponent(buf);
}
@Override
public int getId () {
return 2;
}
@Override
public void serialize (final ByteBuf buf) {
Types.writeComponent(buf, this.message);
}
}

View file

@ -0,0 +1,27 @@
package me.chayapak1.chomens_bot.chomeNSMod.serverboundPackets;
import io.netty.buffer.ByteBuf;
import me.chayapak1.chomens_bot.chomeNSMod.Packet;
import me.chayapak1.chomens_bot.chomeNSMod.Types;
public class ServerboundRunCommandPacket implements Packet {
public final String input;
public ServerboundRunCommandPacket (final String input) {
this.input = input;
}
public ServerboundRunCommandPacket (final ByteBuf buf) {
this.input = Types.readString(buf);
}
@Override
public int getId () {
return 2;
}
@Override
public void serialize (final ByteBuf buf) {
Types.writeString(buf, this.input);
}
}

View file

@ -0,0 +1,33 @@
package me.chayapak1.chomens_bot.chomeNSMod.serverboundPackets;
import io.netty.buffer.ByteBuf;
import me.chayapak1.chomens_bot.chomeNSMod.Packet;
import me.chayapak1.chomens_bot.chomeNSMod.Types;
import java.util.UUID;
public class ServerboundRunCoreCommandPacket implements Packet {
public final UUID runID;
public final String command;
public ServerboundRunCoreCommandPacket (final UUID runID, final String command) {
this.runID = runID;
this.command = command;
}
public ServerboundRunCoreCommandPacket (final ByteBuf buf) {
this.runID = Types.readUUID(buf);
this.command = Types.readString(buf);
}
@Override
public int getId () {
return 1;
}
@Override
public void serialize (final ByteBuf buf) {
Types.writeUUID(buf, this.runID);
Types.writeString(buf, this.command);
}
}

View file

@ -0,0 +1,22 @@
package me.chayapak1.chomens_bot.chomeNSMod.serverboundPackets;
import io.netty.buffer.ByteBuf;
import me.chayapak1.chomens_bot.chomeNSMod.Packet;
public class ServerboundSuccessfulHandshakePacket implements Packet {
public ServerboundSuccessfulHandshakePacket () {
}
public ServerboundSuccessfulHandshakePacket (final ByteBuf buf) {
}
@Override
public int getId () {
return 0;
}
@Override
public void serialize (final ByteBuf buf) {
}
}

View file

@ -0,0 +1,73 @@
package me.chayapak1.chomens_bot.chunk;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import me.chayapak1.chomens_bot.data.chunk.ChunkPos;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes;
import org.geysermc.mcprotocollib.protocol.data.game.chunk.ChunkSection;
// Author: hhhzzzsss
public class ChunkColumn {
public final ChunkPos pos;
public final ChunkSection[] chunks;
private final int minY;
public ChunkColumn (final ChunkPos chunkPos, final byte[] data, final int worldHeight, final int minY) {
this.pos = chunkPos;
this.minY = minY;
final int absoluteWorldHeight = Math.abs(worldHeight);
final int absoluteMinY = Math.abs(minY);
final ByteBuf in = Unpooled.wrappedBuffer(data);
final int numSections = -Math.floorDiv(-(absoluteWorldHeight + absoluteMinY), 16);
chunks = new ChunkSection[numSections];
for (int i = 0; i < numSections; i++) {
chunks[i] = MinecraftTypes.readChunkSection(in);
}
}
public int getBlock (final int x, final int y, final int z) {
if (chunks == null) return 0;
final int yIndex = (y - minY) >> 4;
if (yIndex >= chunks.length) return 0;
return chunks[yIndex].getBlock(x, y & 15, z);
}
public void setBlock (final int x, final int y, final int z, final int id) {
final int yIndex = (y - minY) >> 4;
if (yIndex >= chunks.length) return;
try {
if (chunks[yIndex] == null) {
chunks[yIndex] = new ChunkSection();
chunks[yIndex].setBlock(0, 0, 0, 0);
}
chunks[yIndex].setBlock(x, y & 15, z, id);
} catch (final Exception e) {
// bot.logger.error(
// Component.translatable(
// "Failed to set block at %s %s %s in chunk %s %s with state %s!",
//
// Component.text(x),
// Component.text(y),
// Component.text(z),
//
// Component.text(pos.x()),
// Component.text(pos.z()),
//
// Component.text(id)
// )
// );
// bot.logger.error(e);
}
}
}

View file

@ -1,30 +1,72 @@
package me.chayapak1.chomens_bot.command;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import net.kyori.adventure.text.Component;
import java.util.Arrays;
public abstract class Command {
public final String name;
public final String description;
public final String[] usages;
public String[] usages;
public final String[] aliases;
public final TrustLevel trustLevel;
public final boolean consoleOnly;
public ChatPacketType[] disallowedPacketTypes;
public Command (
String name,
String description,
String[] usages,
String[] aliases,
TrustLevel trustLevel,
boolean consoleOnly
final String name,
final String[] usages,
final String[] aliases,
final TrustLevel trustLevel
) {
this.name = name;
this.usages = usages;
this.aliases = aliases;
this.trustLevel = trustLevel;
this.consoleOnly = false;
}
public Command (
final String name,
final String[] usages,
final String[] aliases,
final TrustLevel trustLevel,
final boolean consoleOnly
) {
this.name = name;
this.description = description;
this.usages = usages;
this.aliases = aliases;
this.trustLevel = trustLevel;
this.consoleOnly = consoleOnly;
}
public Command (
final String name,
final String[] usages,
final String[] aliases,
final TrustLevel trustLevel,
final boolean consoleOnly,
final ChatPacketType[] disallowedPacketTypes
) {
this.name = name;
this.usages = usages;
this.aliases = aliases;
this.trustLevel = trustLevel;
this.consoleOnly = consoleOnly;
this.disallowedPacketTypes = disallowedPacketTypes;
}
public abstract Component execute (CommandContext context) throws Exception;
@Override
public String toString () {
return "Command{" +
"name='" + name + '\'' +
", usages=" + Arrays.toString(usages) +
", aliases=" + Arrays.toString(aliases) +
", trustLevel=" + trustLevel +
", consoleOnly=" + consoleOnly +
", disallowedPacketTypes=" + Arrays.toString(disallowedPacketTypes) +
'}';
}
}

View file

@ -1,15 +1,25 @@
package me.chayapak1.chomens_bot.command;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CommandContext {
public static final Component UNKNOWN_ARGUMENT_COMPONENT = Component.text("???").style(Style.style(TextDecoration.UNDERLINED));
public static final Component UNKNOWN_ARGUMENT_COMPONENT = Component
.text("???")
.style(Style.style(TextDecoration.UNDERLINED));
private static final Pattern FLAGS_PATTERN = Pattern
.compile("^\\s*(?:--|-)([^\\s0-9]\\S*)"); // modified from HBot
public final Bot bot;
@ -19,13 +29,15 @@ public class CommandContext {
public final boolean inGame;
public TrustLevel trustLevel = TrustLevel.PUBLIC;
public String commandName = null;
public String userInputCommandName = null;
public String[] fullArgs;
public String[] args;
public CommandContext(Bot bot, String prefix, PlayerEntry sender, boolean inGame) {
public CommandContext (final Bot bot, final String prefix, final PlayerEntry sender, final boolean inGame) {
this.bot = bot;
this.prefix = prefix;
this.sender = sender;
@ -33,20 +45,24 @@ public class CommandContext {
}
public Component displayName () { return Component.empty(); }
public void sendOutput (Component component) {}
public void sendOutput (final Component component) { }
// args parsing stuff
private int argsPosition = 0;
public String getString (boolean greedy, boolean required) throws CommandException { return getString(greedy, required, "string"); }
public String getString (boolean greedy, boolean required, boolean returnLowerCase) throws CommandException { return getString(greedy, returnLowerCase, required, "string"); }
private String getString (boolean greedy, boolean required, String type) throws CommandException { return getString(greedy, false, required, type); }
private String getString (boolean greedy, boolean returnLowerCase, boolean required, String type) throws CommandException {
public String getString (final boolean greedy, final boolean required) throws CommandException { return getString(greedy, required, "string"); }
public String getString (final boolean greedy, final boolean required, final boolean returnLowerCase) throws CommandException { return getString(greedy, returnLowerCase, required, "string"); }
private String getString (final boolean greedy, final boolean required, final String type) throws CommandException { return getString(greedy, false, required, type); }
private String getString (final boolean greedy, final boolean returnLowerCase, final boolean required, final String type) throws CommandException {
if (argsPosition >= args.length || args[argsPosition] == null) {
if (required) {
throw new CommandException(
Component.translatable(
"Expected %s at position %s (%s %s)",
"arguments_parsing.error.expected_string",
Component.text(type),
Component.text(argsPosition),
Component.text(prefix + userInputCommandName),
@ -55,7 +71,19 @@ public class CommandContext {
Component
.text(String.join(" ", args))
.append(Component.space())
.append(UNKNOWN_ARGUMENT_COMPONENT)
.append(UNKNOWN_ARGUMENT_COMPONENT),
inGame ?
Component
.space()
.append(
Component.translatable("[%s]")
.arguments(
Component
.translatable("arguments_parsing.hover.usages")
.clickEvent(ClickEvent.suggestCommand(prefix + "help " + this.commandName))
)
) :
Component.empty()
)
);
} else {
@ -63,52 +91,183 @@ public class CommandContext {
}
}
final String string = greedy ?
String.join(" ", Arrays.copyOfRange(args, argsPosition, args.length)) :
args[argsPosition];
final String greedyString = String.join(" ", Arrays.copyOfRange(args, argsPosition, args.length));
final StringBuilder string = new StringBuilder();
if (greedy) {
string.append(greedyString);
} else if (
greedyString.length() > 1 &&
(greedyString.startsWith("'") || greedyString.startsWith("\""))
) {
// parses arguments with quotes
final char quote = greedyString.charAt(0);
int pointer = 1; // skips quote
while (true) {
if (pointer >= greedyString.length()) {
if (greedyString.charAt(pointer - 1) != quote) {
throw new CommandException(
Component
.translatable("arguments_parsing.error.unterminated_quote")
.arguments(
Component.text(greedyString, bot.colorPalette.string),
Component.text(quote, NamedTextColor.YELLOW)
)
);
}
break;
}
final char character = greedyString.charAt(pointer);
pointer++;
if (character == ' ') {
argsPosition++;
}
if (character == '\\') {
if (pointer >= greedyString.length()) {
throw new CommandException(
Component
.translatable("arguments_parsing.error.unterminated_escape")
.arguments(
Component
.text(greedyString)
.color(bot.colorPalette.string)
)
);
}
final char nextCharacter = greedyString.charAt(pointer); // pointer is already incremented above
final char toAdd = switch (nextCharacter) {
case 'n' -> '\n';
case 't' -> '\t';
case 'r' -> '\r';
default -> nextCharacter;
};
string.append(toAdd);
pointer++;
} else if (character == quote) {
break;
} else {
string.append(character);
}
}
} else {
// else just get the current argument
string.append(args[argsPosition]);
}
argsPosition++;
return returnLowerCase ? string.toLowerCase() : string;
final String result = string.toString();
return returnLowerCase ? result.toLowerCase() : result;
}
public Integer getInteger (boolean required) throws CommandException {
public String getAction () throws CommandException {
return getString(false, true, true, "action");
}
public List<String> getFlags (final String... allowedFlags) throws CommandException { return getFlags(false, allowedFlags); }
public List<String> getFlags (final boolean returnLowerCase, final String... allowedFlags) throws CommandException {
final List<String> flags = new ArrayList<>();
String flag = getFlag(returnLowerCase, allowedFlags);
while (flag != null) {
flags.add(flag);
flag = getFlag(returnLowerCase, allowedFlags);
}
return flags;
}
private String getFlag (final boolean returnLowerCase, final String[] allowedFlagsArray) throws CommandException {
final List<String> allowedFlags = Arrays.asList(allowedFlagsArray);
final String string = getString(false, false, returnLowerCase);
if (string.isBlank()) return null;
final Matcher matcher = FLAGS_PATTERN.matcher(string);
if (matcher.find()) {
final String match = matcher.group(1);
if (allowedFlags.contains(match)) return match;
}
argsPosition--; // getString incremented argsPosition
return null;
}
public Integer getInteger (final boolean required) throws CommandException {
final String string = getString(false, required, "integer");
if (string.isEmpty()) return null;
try {
return Integer.parseInt(string);
} catch (NumberFormatException e) {
throw new CommandException(Component.text("Invalid integer"));
} catch (final NumberFormatException e) {
throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("integer")));
}
}
public Double getDouble (boolean required) throws CommandException {
public Long getLong (final boolean required) throws CommandException {
final String string = getString(false, required, "long");
if (string.isEmpty()) return null;
try {
return Long.parseLong(string);
} catch (final NumberFormatException e) {
throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("long")));
}
}
public Double getDouble (final boolean required, final boolean allowInfinite) throws CommandException {
final String string = getString(false, required, "double");
if (string.isEmpty()) return null;
try {
return Double.parseDouble(string);
} catch (NumberFormatException e) {
throw new CommandException(Component.text("Invalid double"));
final double parsedDouble = Double.parseDouble(string);
if (!Double.isFinite(parsedDouble) && !allowInfinite) throw new NumberFormatException();
else return parsedDouble;
} catch (final NumberFormatException e) {
throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("double")));
}
}
public Float getFloat (boolean required) throws CommandException {
public Float getFloat (final boolean required, final boolean allowInfinite) throws CommandException {
final String string = getString(false, required, "float");
if (string.isEmpty()) return null;
try {
return Float.parseFloat(string);
} catch (NumberFormatException e) {
throw new CommandException(Component.text("Invalid float"));
final float parsedFloat = Float.parseFloat(string);
if (!Float.isFinite(parsedFloat) && !allowInfinite) throw new NumberFormatException();
else return parsedFloat;
} catch (final NumberFormatException e) {
throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("float")));
}
}
public Boolean getBoolean (boolean required) throws CommandException {
public Boolean getBoolean (final boolean required) throws CommandException {
final String string = getString(false, required, "boolean");
if (string.isEmpty()) return null;
@ -116,24 +275,42 @@ public class CommandContext {
return switch (string) {
case "true" -> true;
case "false" -> false;
default -> throw new CommandException(Component.text("Invalid boolean"));
default -> throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("boolean")));
};
}
public <T extends Enum<T>> T getEnum (Class<T> enumClass) throws CommandException {
final String string = getString(false, true, enumClass.getSimpleName());
public <T extends Enum<T>> T getEnum (final boolean required, final Class<T> enumClass) throws CommandException {
final String string = getString(false, required, enumClass.getSimpleName());
if (string.isEmpty()) return null;
try {
return Enum.valueOf(enumClass, string.toUpperCase());
} catch (IllegalArgumentException | NullPointerException e) {
throw new CommandException(Component.text("Invalid enum"));
} catch (final IllegalArgumentException | NullPointerException e) {
final T[] values = enumClass.getEnumConstants();
throw new CommandException(
Component.translatable(
"arguments_parsing.error.invalid_enum",
Component.text(enumClass.getSimpleName()),
Component.text(Arrays.toString(values))
)
);
}
}
public void checkOverloadArgs (int maximumArgs) throws CommandException {
if (args.length > maximumArgs) throw new CommandException(
public void checkOverloadArgs (final int maximumArgs) throws CommandException {
final String joined = String.join(" ", args);
final String quotesReplaced = joined.replaceAll("([\"'])(?:\\.|(?!\1).)*\1", "i");
final int count = quotesReplaced.isBlank() ?
0 :
quotesReplaced.split("\\s+").length;
if (count > maximumArgs) throw new CommandException(
Component.translatable(
"Too many arguments, expected %s max",
"arguments_parsing.error.too_many_arguments",
Component.text(maximumArgs)
)
);

View file

@ -5,7 +5,7 @@ import net.kyori.adventure.text.Component;
public class CommandException extends Exception {
public final Component message;
public CommandException (Component message) {
public CommandException (final Component message) {
this.message = message;
}
}

View file

@ -0,0 +1,6 @@
package me.chayapak1.chomens_bot.command;
public final class CommonFlags {
public static final String IGNORE_CASE = "ignorecase";
public static final String REGEX = "regex";
}

View file

@ -1,26 +0,0 @@
package me.chayapak1.chomens_bot.command;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
public class ConsoleCommandContext extends CommandContext {
private final Bot bot;
public ConsoleCommandContext (Bot bot, String prefix) {
super(bot, prefix, bot.players.getBotEntry() /* real */, false);
this.bot = bot;
}
@Override
public void sendOutput (Component component) {
final String message = ComponentUtilities.stringifyAnsi(component);
bot.logger.info(message);
}
@Override
public Component displayName () {
return sender.displayName.color(NamedTextColor.YELLOW);
}
}

View file

@ -1,62 +0,0 @@
package me.chayapak1.chomens_bot.command;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.CodeBlockUtilities;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.kyori.adventure.text.Component;
import java.awt.*;
import java.util.UUID;
public class DiscordCommandContext extends CommandContext {
private final MessageReceivedEvent event;
private final Bot bot;
public DiscordCommandContext(Bot bot, String prefix, MessageReceivedEvent event) {
super(
bot,
prefix,
new PlayerEntry(
new GameProfile(
UUID.nameUUIDFromBytes(("OfflinePlayer:" + event.getAuthor().getName()).getBytes()),
event.getAuthor().getName()
),
GameMode.SURVIVAL,
-69420,
Component.text(event.getAuthor().getName()),
0L,
null,
new byte[0],
true
),
false
);
this.bot = bot;
this.event = event;
}
@Override
public void sendOutput (Component component) {
final String output = ComponentUtilities.stringifyAnsi(component);
final EmbedBuilder builder = new EmbedBuilder();
builder.setTitle("Output");
builder.setColor(Color.decode(bot.config.discord.embedColors.normal));
builder.setDescription("```ansi\n" + CodeBlockUtilities.escape(output.replace("\u001b[9", "\u001b[3")) + "\n```");
final MessageEmbed embed = builder.build();
event.getMessage().replyEmbeds(embed).queue();
}
@Override
public Component displayName () {
return Component.text(event.getAuthor().getName());
}
}

View file

@ -1,37 +0,0 @@
package me.chayapak1.chomens_bot.command;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.Bot;
import net.kyori.adventure.text.Component;
public class PlayerCommandContext extends CommandContext {
public final String playerName;
public final String selector;
private final Bot bot;
public PlayerCommandContext (Bot bot, String playerName, String prefix, String selector, PlayerEntry sender) {
super(bot, prefix, sender, true);
this.bot = bot;
this.playerName = playerName;
this.selector = selector;
}
@Override
public void sendOutput (Component message) {
bot.chat.tellraw(
Component.translatable(
"%s",
message,
Component.text("chomens_bot_command_output" + ((commandName != null) ? "_" + commandName : ""))
),
selector
);
}
@Override
public Component displayName () {
return sender.displayName;
}
}

View file

@ -1,7 +1,47 @@
package me.chayapak1.chomens_bot.command;
import me.chayapak1.chomens_bot.Configuration;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import net.dv8tion.jda.api.entities.Role;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.List;
import java.util.Map;
public enum TrustLevel {
PUBLIC,
TRUSTED,
OWNER
PUBLIC(0, Component.text(I18nUtilities.get("trust_level.public"), NamedTextColor.GREEN)),
TRUSTED(1, Component.text(I18nUtilities.get("trust_level.trusted"), NamedTextColor.RED)),
ADMIN(2, Component.text(I18nUtilities.get("trust_level.admin"), NamedTextColor.DARK_RED)),
OWNER(3, Component.text(I18nUtilities.get("trust_level.owner"), NamedTextColor.LIGHT_PURPLE));
public static final TrustLevel MAX = values()[values().length - 1];
public final int level;
public final Component component;
TrustLevel (final int level, final Component component) {
this.level = level;
this.component = component;
}
public static TrustLevel fromDiscordRoles (final List<Role> roles) {
if (Main.discord == null || Main.discord.options == null) return PUBLIC;
final Configuration.Discord options = Main.discord.options;
final Map<String, TrustLevel> roleToLevel = Map.of(
options.ownerRoleName.toLowerCase(), OWNER,
options.adminRoleName.toLowerCase(), ADMIN,
options.trustedRoleName.toLowerCase(), TRUSTED
);
for (final Role role : roles) {
final TrustLevel level = roleToLevel.get(role.getName().toLowerCase());
if (level != null) return level;
}
return PUBLIC;
}
}

View file

@ -0,0 +1,33 @@
package me.chayapak1.chomens_bot.command.contexts;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.chomeNSMod.clientboundPackets.ClientboundMessagePacket;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import net.kyori.adventure.text.Component;
public class ChomeNSModCommandContext extends CommandContext {
public ChomeNSModCommandContext (final Bot bot, final PlayerEntry sender) {
super(
bot,
".cbot ", // intentionally hardcoded
sender,
true
);
}
@Override
public void sendOutput (final Component component) {
final Component rendered = I18nUtilities.render(component);
bot.chomeNSMod.send(
sender,
new ClientboundMessagePacket(rendered)
);
}
@Override
public Component displayName () {
return sender.displayName;
}
}

View file

@ -0,0 +1,51 @@
package me.chayapak1.chomens_bot.command.contexts;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.data.logging.LogType;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
public class ConsoleCommandContext extends CommandContext {
private final Bot bot;
public ConsoleCommandContext (final Bot bot, final String prefix) {
super(
bot,
prefix,
bot.players.getBotEntry() != null ?
bot.players.getBotEntry() :
new PlayerEntry(
new GameProfile(
UUIDUtilities.getOfflineUUID(bot.username),
bot.username
),
GameMode.CREATIVE,
-69420,
Component.text(bot.username),
0,
null,
new byte[0],
true
),
false
);
this.bot = bot;
}
@Override
public void sendOutput (final Component component) {
final Component rendered = I18nUtilities.render(component);
bot.logger.log(LogType.COMMAND_OUTPUT, rendered, false);
}
@Override
public Component displayName () {
return sender.displayName.color(NamedTextColor.YELLOW);
}
}

View file

@ -0,0 +1,94 @@
package me.chayapak1.chomens_bot.command.contexts;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.CodeBlockUtilities;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.utils.FileUpload;
import net.kyori.adventure.text.Component;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import java.awt.*;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
public class DiscordCommandContext extends CommandContext {
public final Member member;
public final String name;
public final Consumer<FileUpload> replyFiles;
public final Consumer<MessageEmbed> replyEmbed;
private final Bot bot;
public DiscordCommandContext (
final Bot bot,
final String prefix,
final Member member,
final String name,
final Consumer<FileUpload> replyFiles,
final Consumer<MessageEmbed> replyEmbed
) {
super(
bot,
prefix,
new PlayerEntry(
new GameProfile(
UUIDUtilities.getOfflineUUID(name),
name
),
GameMode.SURVIVAL,
-69420,
Component.text(name),
0L,
null,
new byte[0],
true
),
false
);
this.bot = bot;
this.member = member;
this.name = name;
this.replyFiles = replyFiles;
this.replyEmbed = replyEmbed;
}
@Override
public void sendOutput (final Component component) {
final Component rendered = I18nUtilities.render(component);
String output = ComponentUtilities.stringifyDiscordAnsi(rendered);
if (output.length() > 2048) {
output = ComponentUtilities.stringify(rendered);
replyFiles.accept(
FileUpload.fromData(
output.getBytes(StandardCharsets.UTF_8),
String.format("output-%d.txt", System.currentTimeMillis())
)
);
} else {
final EmbedBuilder builder = new EmbedBuilder();
builder.setTitle("Output");
builder.setColor(Color.decode(bot.config.discord.embedColors.normal));
builder.setDescription("```ansi\n" + CodeBlockUtilities.escape(output) + "\n```");
final MessageEmbed embed = builder.build();
replyEmbed.accept(embed);
}
}
@Override
public Component displayName () {
return Component.text(name);
}
}

View file

@ -1,25 +1,27 @@
package me.chayapak1.chomens_bot.command;
package me.chayapak1.chomens_bot.command.contexts;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import net.kyori.adventure.text.Component;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import net.kyori.adventure.text.Component;
import java.util.UUID;
public class IRCCommandContext extends CommandContext {
private final Bot bot;
private final String nickName;
public IRCCommandContext (Bot bot, String prefix, String nickName) {
public IRCCommandContext (final Bot bot, final String prefix, final String nickName) {
super(
bot,
prefix,
new PlayerEntry(
new GameProfile(
UUID.nameUUIDFromBytes(("OfflinePlayer:" + nickName).getBytes()),
UUIDUtilities.getOfflineUUID(nickName),
nickName
),
GameMode.SURVIVAL,
@ -37,8 +39,9 @@ public class IRCCommandContext extends CommandContext {
}
@Override
public void sendOutput (Component component) {
bot.irc.sendMessage(bot, ComponentUtilities.stringify(component));
public void sendOutput (final Component component) {
final Component rendered = I18nUtilities.render(component);
Main.irc.sendMessage(bot, ComponentUtilities.stringifyAnsi(rendered));
}
@Override

View file

@ -0,0 +1,61 @@
package me.chayapak1.chomens_bot.command.contexts;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import net.kyori.adventure.text.Component;
public class PlayerCommandContext extends CommandContext {
public final String playerName;
public final String selector;
public final ChatPacketType packetType;
private final Bot bot;
public PlayerCommandContext (
final Bot bot,
final String playerName,
final String prefix,
final String selector,
final PlayerEntry sender,
final ChatPacketType packetType
) {
super(bot, prefix, sender, true);
this.bot = bot;
this.playerName = playerName;
this.selector = selector;
this.packetType = packetType;
}
@Override
public void sendOutput (final Component component) {
sendOutput(component, false);
}
public void sendOutput (final Component component, final boolean onlyToSender) {
final Component rendered = I18nUtilities.render(component);
final String selector = onlyToSender
? UUIDUtilities.selector(this.sender.profile.getId())
: this.selector;
bot.chat.tellraw(
Component.translatable(
"%s",
rendered,
Component.text("chomens_bot_command_output" + ((commandName != null) ? "_" + commandName : ""))
),
selector
);
}
@Override
public Component displayName () {
return sender.displayName;
}
}

View file

@ -0,0 +1,34 @@
package me.chayapak1.chomens_bot.command.contexts;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.CommandContext;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
public class RemoteCommandContext extends CommandContext {
public final Bot targetBot;
public final CommandContext source;
public RemoteCommandContext (final Bot targetBot, final CommandContext source) {
super(targetBot, source.prefix, source.sender, false);
this.targetBot = targetBot;
this.source = source;
}
@Override
public void sendOutput (final Component component) {
// we do not need rendering here, since that is handled by the source
source.sendOutput(
Component
.translatable(
"[%s] %s",
Component.text(targetBot.getServerString(), NamedTextColor.GRAY),
Component.empty()
.color(NamedTextColor.WHITE)
.append(component)
)
.color(NamedTextColor.DARK_GRAY)
);
}
}

View file

@ -0,0 +1,59 @@
package me.chayapak1.chomens_bot.commands;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import net.kyori.adventure.text.Component;
public class AuthCommand extends Command {
public AuthCommand () {
super(
"auth",
new String[] { "[TrustLevel]" },
new String[] {},
TrustLevel.TRUSTED
);
}
@Override
public Component execute (final CommandContext context) throws CommandException {
final boolean allowSettingOthers = context.trustLevel == TrustLevel.MAX;
if (!allowSettingOthers) context.checkOverloadArgs(1);
final Bot bot = context.bot;
TrustLevel trustLevel = context.getEnum(false, TrustLevel.class);
if (trustLevel == null) trustLevel = context.trustLevel;
if (trustLevel.level > context.trustLevel.level) throw new CommandException(
Component.translatable("commands.auth.error.privilege_escalate")
);
final String targetString = context.getString(true, false);
PlayerEntry target = null;
if (allowSettingOthers && !targetString.isEmpty()) target = bot.players.getEntry(targetString);
if (target == null) target = context.sender;
target.persistingData.authenticatedTrustLevel = trustLevel;
if (target.equals(context.sender)) {
return Component.translatable(
"commands.auth.self",
bot.colorPalette.defaultColor,
target.persistingData.authenticatedTrustLevel.component
);
} else {
return Component.translatable(
"commands.auth.others",
bot.colorPalette.defaultColor,
Component.text(target.profile.getName(), bot.colorPalette.username),
target.persistingData.authenticatedTrustLevel.component
);
}
}
}

View file

@ -5,7 +5,7 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.selfCares.essentials.VanishSelfCare;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -13,55 +13,58 @@ public class BotVisibilityCommand extends Command {
public BotVisibilityCommand () {
super(
"botvisibility",
"Changes the bot's visibility",
new String[] { "<true|false>", "<on|off>", "" },
new String[] { "botvis", "togglevis", "togglevisibility" },
TrustLevel.TRUSTED,
false
TrustLevel.TRUSTED
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
final String action = context.getString(false, false, false);
if (action.isEmpty()) {
final boolean visibility = bot.selfCare.visibility;
bot.selfCare.visibility = !visibility;
final VanishSelfCare vanish = bot.selfCare.find(VanishSelfCare.class);
final NamedTextColor greenOrGold = bot.selfCare.visibility ? NamedTextColor.GREEN : NamedTextColor.GOLD;
final String visibleOrInvisible = bot.selfCare.visibility ? "visible" : "invisible";
final String disableOrEnable = bot.selfCare.visibility ? "disable" : "enable";
bot.core.run("/essentials:vanish " + bot.profile.getName() + " " + disableOrEnable);
return Component.empty()
.append(Component.text("The bot's visibility is now "))
.append(Component.text(visibleOrInvisible).color(greenOrGold))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
if (action.isEmpty()) {
vanish.visible = !vanish.visible;
vanish.needsRunning = true;
final NamedTextColor greenOrGold = vanish.visible ? NamedTextColor.GREEN : NamedTextColor.GOLD;
final String visibleOrInvisible = vanish.visible ? "visible" : "invisible";
return Component.translatable(
"commands.botvisibility.message",
bot.colorPalette.defaultColor,
Component.translatable("commands.botvisibility." + visibleOrInvisible, greenOrGold)
);
} else {
switch (action) {
case "on", "true" -> {
bot.selfCare.visibility = true;
bot.core.run("/essentials:vanish " + bot.profile.getName() + " disable");
return Component.empty()
.append(Component.text("The bot's visibility is now "))
.append(Component.text("visible").color(NamedTextColor.GREEN))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
vanish.visible = true;
vanish.needsRunning = true;
return Component.translatable(
"commands.botvisibility.message",
bot.colorPalette.defaultColor,
Component.translatable("commands.botvisibility.visible", NamedTextColor.GREEN)
);
}
case "off", "false" -> {
bot.selfCare.visibility = false;
bot.core.run("/essentials:vanish " + bot.profile.getName() + " enable");
return Component.empty()
.append(Component.text("The bot's visibility is now "))
.append(Component.text("invisible").color(NamedTextColor.GOLD))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
default -> {
throw new CommandException(Component.text("Invalid action"));
vanish.visible = false;
vanish.needsRunning = true;
return Component.translatable(
"commands.botvisibility.message",
bot.colorPalette.defaultColor,
Component.translatable("commands.botvisibility.invisible", NamedTextColor.GOLD)
);
}
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
}
}

View file

@ -11,16 +11,14 @@ public class BruhifyCommand extends Command {
public BruhifyCommand () {
super(
"bruhify",
"RecycleBots bruhify but actionbar",
new String[] { "[message]" },
new String[] {},
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
bot.bruhify.bruhifyText = context.getString(true, false);

View file

@ -5,7 +5,9 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -15,16 +17,16 @@ public class ClearChatCommand extends Command {
public ClearChatCommand () {
super(
"clearchat",
"Clears the chat",
new String[] { "[player]" },
new String[] { "cc" },
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[] { ChatPacketType.DISGUISED }
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String name = context.getString(true, false);
@ -32,26 +34,32 @@ public class ClearChatCommand extends Command {
if (!name.isEmpty()) {
final PlayerEntry entry = bot.players.getEntry(name);
if (entry == null) throw new CommandException(Component.text("Invalid player name"));
if (entry == null)
throw new CommandException(Component.translatable("commands.generic.error.invalid_player"));
final UUID uuid = entry.profile.getId();
bot.chat.tellraw(
Component.empty()
.append(Component.text("\n".repeat(1000)))
.append(
Component.empty()
.append(Component.text("Your chat has been cleared by "))
.append(context.displayName())
.color(NamedTextColor.DARK_GREEN)
),
I18nUtilities.render(
Component.empty()
.append(Component.text("\n".repeat(1000)))
.append(
Component.translatable(
"commands.clearchat.specific",
NamedTextColor.DARK_GREEN,
context.displayName()
)
)
),
uuid
);
} else {
bot.chat.tellraw(
Component.empty()
.append(Component.text("\n".repeat(1000)))
.append(Component.text("The chat has been cleared").color(NamedTextColor.DARK_GREEN))
I18nUtilities.render(
Component.empty()
.append(Component.text("\n".repeat(1000)))
.append(Component.translatable("commands.clearchat.everyone", NamedTextColor.DARK_GREEN))
)
);
}

View file

@ -11,22 +11,23 @@ public class ClearChatQueueCommand extends Command {
public ClearChatQueueCommand () {
super(
"clearchatqueue",
"Clears the bots chat queue",
new String[] {},
new String[] { "ccq" },
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(0);
final Bot bot = context.bot;
bot.chat.clearQueue();
return null;
return Component.translatable(
"commands.clearchatqueue.output",
bot.colorPalette.defaultColor
);
}
}

View file

@ -5,12 +5,12 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.CommandLoop;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.data.cloop.CommandLoop;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.format.NamedTextColor;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
@ -18,34 +18,43 @@ public class CloopCommand extends Command {
public CloopCommand () {
super(
"cloop",
"Loop commands",
new String[] { "add <interval> <command>", "remove <index>", "clear", "list" },
new String[] { "add <interval> <ChronoUnit> <command>", "remove <index>", "clear", "list" },
new String[] { "commandloop" },
TrustLevel.TRUSTED,
false
TrustLevel.TRUSTED
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String action = context.getString(false, true, true);
final String action = context.getAction();
switch (action) {
case "add" -> {
int interval = context.getInteger(true);
long interval = context.getLong(true);
if (interval < 1) interval = 1;
final ChronoUnit unit = context.getEnum(true, ChronoUnit.class);
if (unit == ChronoUnit.NANOS && interval < 1000)
throw new CommandException(Component.translatable("commands.cloop.add.error.too_low_nanoseconds"));
final String command = context.getString(true, true);
bot.cloop.add(interval, command);
try {
bot.cloop.add(unit, interval, command);
} catch (final Exception e) {
throw new CommandException(Component.text(e.toString()));
}
return Component.translatable(
"Added %s with interval %s to the cloops",
Component.text(command).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component.text(interval).color(ColorUtilities.getColorByString(bot.config.colorPalette.number))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
"commands.cloop.add.output",
bot.colorPalette.defaultColor,
Component.text(command, bot.colorPalette.string),
Component.text(interval, bot.colorPalette.number),
Component.text(unit.toString(), bot.colorPalette.string)
);
}
case "remove" -> {
context.checkOverloadArgs(2);
@ -56,18 +65,19 @@ public class CloopCommand extends Command {
final CommandLoop cloop = bot.cloop.remove(index);
return Component.translatable(
"Removed cloop %s",
Component.text(cloop.command()).color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
} catch (IndexOutOfBoundsException | IllegalArgumentException | NullPointerException ignored) {
throw new CommandException(Component.text("Invalid index"));
"commands.cloop.remove.output",
bot.colorPalette.defaultColor,
Component.text(cloop.command(), bot.colorPalette.string)
);
} catch (final IndexOutOfBoundsException | IllegalArgumentException | NullPointerException ignored) {
throw new CommandException(Component.translatable("commands.generic.error.invalid_index"));
}
}
case "clear" -> {
context.checkOverloadArgs(1);
bot.cloop.clear();
return Component.text("Cleared all cloops").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.cloop.clear.output", bot.colorPalette.defaultColor);
}
case "list" -> {
context.checkOverloadArgs(1);
@ -75,29 +85,31 @@ public class CloopCommand extends Command {
final List<Component> cloopsComponent = new ArrayList<>();
int index = 0;
for (CommandLoop command : bot.cloop.loops) {
for (final CommandLoop command : bot.cloop.loops) {
cloopsComponent.add(
Component.translatable(
"%s %s (%s)",
Component.text(index).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
Component.text(command.command()).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component.text(command.interval()).color(ColorUtilities.getColorByString(bot.config.colorPalette.number))
).color(NamedTextColor.DARK_GRAY)
"%s %s (%s %s)",
NamedTextColor.DARK_GRAY,
Component.text(index, bot.colorPalette.number),
Component.text(command.command(), bot.colorPalette.string),
Component.text(command.interval(), bot.colorPalette.number),
Component.text(command.unit().toString(), bot.colorPalette.string)
)
);
index++;
}
return Component.empty()
.append(Component.text("Cloops ").color(NamedTextColor.GREEN))
.append(Component.text("(").color(NamedTextColor.DARK_GRAY))
.append(Component.text(bot.cloop.loops.size()).color(NamedTextColor.GRAY))
.append(Component.text(")").color(NamedTextColor.DARK_GRAY))
.append(Component.translatable("commands.cloop.list.cloops_text", NamedTextColor.GREEN))
.append(Component.text("(", NamedTextColor.DARK_GRAY))
.append(Component.text(bot.cloop.loops.size(), NamedTextColor.GRAY))
.append(Component.text(")", NamedTextColor.DARK_GRAY))
.append(Component.newline())
.append(
Component.join(JoinConfiguration.newlines(), cloopsComponent)
);
}
default -> throw new CommandException(Component.text("Invalid action"));
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
}
}

View file

@ -5,12 +5,17 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.cloudburstmc.math.vector.Vector3i;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
// based off of HBot's supercb command
public class CommandBlockCommand extends Command {
@ -20,8 +25,8 @@ public class CommandBlockCommand extends Command {
public CommandBlockCommand () {
super(
"cb",
"Executes a command in the command core and return its output",
new String[] {
"",
"<command>",
"..{username}..",
"..{uuid}..",
@ -29,21 +34,70 @@ public class CommandBlockCommand extends Command {
"..{uuid{regex}}.."
},
new String[] { "cmd", "commandblock", "run", "core" },
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
runCommand(bot, context, context.getString(true, true));
final String command = context.getString(true, false);
if (command.isEmpty()) {
return getInfo(bot);
}
try {
runCommand(bot, context, command, null);
} catch (final PatternSyntaxException e) {
throw new CommandException(Component.text(e.toString()));
}
return null;
}
private void runCommand (Bot bot, CommandContext context, String command) {
private Component getInfo (final Bot bot) {
final Vector3i from = bot.core.from;
final Vector3i to = bot.core.to;
final Vector3i block = bot.core.block;
final int layers = Math.max(1, to.getY() - from.getY());
final StringBuilder commandBuilder = new StringBuilder("/");
if (bot.serverFeatures.hasEssentials) commandBuilder.append("essentials:");
commandBuilder
.append("tp ")
.append(from.getX())
.append(" ")
.append(from.getY())
.append(" ")
.append(from.getZ());
final String command = commandBuilder.toString();
return Component.translatable(
"commands.cb.info.output",
bot.colorPalette.secondary,
Component.translatable(
"commands.cb.info.size",
bot.colorPalette.string,
Component.text(256 * layers)
),
Component.text(layers, bot.colorPalette.string),
Component.text(from.toString(), bot.colorPalette.string),
Component.text(to.toString(), bot.colorPalette.string),
Component.text(block.toString(), bot.colorPalette.string),
Component.text(bot.world.currentDimension, bot.colorPalette.string),
Component
.translatable("commands.cb.info.click_to_teleport", NamedTextColor.GREEN)
.hoverEvent(HoverEvent.showText(Component.text(command, bot.colorPalette.secondary)))
.clickEvent(ClickEvent.runCommand(command))
);
}
private void runCommand (final Bot bot, final CommandContext context, final String command, final PlayerEntry player) {
final Matcher userMatcher = USER_PATTERN.matcher(command);
final Matcher uuidMatcher = UUID_PATTERN.matcher(command);
@ -51,51 +105,57 @@ public class CommandBlockCommand extends Command {
final boolean uuidFound = uuidMatcher.find();
if (userFound || uuidFound) {
Pattern pattern;
final Pattern pattern;
if (userFound) pattern = Pattern.compile(userMatcher.group(1));
else pattern = Pattern.compile(uuidMatcher.group(1));
for (PlayerEntry entry : bot.players.list) {
final String username = entry.profile.getName();
final String uuid = entry.profile.getIdAsString();
synchronized (bot.players.list) {
for (final PlayerEntry entry : bot.players.list) {
final String username = entry.profile.getName();
final String uuid = entry.profile.getIdAsString();
if (!pattern.matcher(userFound ? username : uuid).matches()) continue;
if (!pattern.matcher(userFound ? username : uuid).matches()) continue;
String replacedCommand;
String replacedCommand;
if (userFound) replacedCommand = new StringBuilder(command).replace(userMatcher.start(), userMatcher.end(), username).toString();
else replacedCommand = new StringBuilder(command).replace(uuidMatcher.start(), uuidMatcher.end(), uuid).toString();
if (userFound)
replacedCommand = new StringBuilder(command).replace(userMatcher.start(), userMatcher.end(), username).toString();
else
replacedCommand = new StringBuilder(command).replace(uuidMatcher.start(), uuidMatcher.end(), uuid).toString();
replacedCommand = replacedCommand
.replace("{username}", username)
.replace("{uuid}", uuid);
replacedCommand = replacedCommand
.replace("{username}", username)
.replace("{uuid}", uuid);
if (
!replacedCommand.contains("{username}") &&
!replacedCommand.contains("{uuid}") &&
!USER_PATTERN.matcher(username).find() &&
!UUID_PATTERN.matcher(username).find()
) {
runCommand(bot, context, replacedCommand);
if (
!replacedCommand.contains("{username}") &&
!replacedCommand.contains("{uuid}") &&
!USER_PATTERN.matcher(username).find() &&
!UUID_PATTERN.matcher(username).find()
) {
runCommand(bot, context, replacedCommand, entry);
}
}
}
} else if (command.contains("{username}") || command.contains("{uuid}")) {
for (PlayerEntry entry : bot.players.list) {
final String username = entry.profile.getName();
final String uuid = entry.profile.getIdAsString();
synchronized (bot.players.list) {
for (final PlayerEntry entry : bot.players.list) {
final String username = entry.profile.getName();
final String uuid = entry.profile.getIdAsString();
final String replacedCommand = command
.replace("{username}", username)
.replace("{uuid}", uuid);
final String replacedCommand = command
.replace("{username}", username)
.replace("{uuid}", uuid);
if (
!replacedCommand.contains("{username}") &&
!replacedCommand.contains("{Uuuid}") &&
!USER_PATTERN.matcher(username).find() &&
!UUID_PATTERN.matcher(username).find()
) {
runCommand(bot, context, replacedCommand);
if (
!replacedCommand.contains("{username}") &&
!replacedCommand.contains("{uuid}") &&
!USER_PATTERN.matcher(username).find() &&
!UUID_PATTERN.matcher(username).find()
) {
runCommand(bot, context, replacedCommand, entry);
}
}
}
} else {
@ -103,8 +163,42 @@ public class CommandBlockCommand extends Command {
if (future == null) return;
future.thenApplyAsync(output -> {
context.sendOutput(output);
future.thenApply(output -> {
if (player == null) context.sendOutput(output);
else {
final Component component = Component
.translatable(
"[%s] %s",
Component
.text(player.profile.getName())
.color(NamedTextColor.GRAY)
.hoverEvent(
HoverEvent.showText(
Component
.text(player.profile.getName())
.append(Component.newline())
.append(
Component
.text(player.profile.getIdAsString())
.color(bot.colorPalette.uuid)
)
.append(Component.newline())
.append(Component.translatable("commands.generic.click_to_copy_username", NamedTextColor.GREEN))
.append(Component.newline())
.append(Component.translatable("commands.generic.shift_click_to_insert_uuid", NamedTextColor.GREEN))
)
)
.clickEvent(ClickEvent.copyToClipboard(player.profile.getName()))
.insertion(player.profile.getIdAsString()),
Component
.empty()
.append(output)
.color(NamedTextColor.WHITE)
)
.color(NamedTextColor.DARK_GRAY);
context.sendOutput(component);
}
return output;
});

View file

@ -1,11 +1,11 @@
package me.chayapak1.chomens_bot.commands;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -13,54 +13,83 @@ import java.util.ArrayList;
import java.util.List;
public class ConsoleCommand extends Command {
public ConsoleCommand() {
public ConsoleCommand () {
super(
"console",
"Controls stuff about console",
new String[] {
"server <server>",
"discord <message>",
"logtoconsole <true|false>",
"printdisconnectedreason <true|false>"
},
new String[] {},
new String[] { "csvr" },
TrustLevel.OWNER,
true
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String action = context.getString(false, true, true);
final String action = !context.userInputCommandName.equals(this.name)
? "server" // csvr alias (console server), like in the original chomens bot javascript
: context.getString(false, true, true);
switch (action) {
case "server" -> {
final List<String> servers = new ArrayList<>();
for (Bot eachBot : bot.bots) {
servers.add(eachBot.host + ":" + eachBot.port);
for (final Bot eachBot : bot.bots) {
servers.add(eachBot.getServerString(true));
}
final String server = context.getString(true, true);
for (Bot eachBot : bot.bots) {
if (server.equalsIgnoreCase("all")) {
eachBot.console.consoleServer = "all";
continue;
}
try {
// servers.find(server => server.toLowerCase().includes(args.join(' '))) in js i guess
eachBot.console.consoleServer = servers.stream()
.filter(eachServer -> eachServer.toLowerCase().contains(server))
.findFirst()
.orElse("all");
} catch (ArrayIndexOutOfBoundsException e) {
throw new CommandException(Component.text("Invalid server: " + server));
}
if (server.equalsIgnoreCase("all")) {
Main.console.consoleServer = "all";
return Component.translatable(
"commands.console.server.set",
bot.colorPalette.defaultColor,
Component.translatable("commands.console.server.all_servers")
);
}
try {
// servers.find(server => server.toLowerCase().includes(args.join(' '))) in js i guess
Main.console.consoleServer = servers.stream()
.filter(eachServer -> eachServer.toLowerCase().contains(server))
.findFirst()
.orElse("all");
return Component.translatable(
"commands.console.server.set",
bot.colorPalette.defaultColor,
Component.text(Main.console.consoleServer)
);
} catch (final ArrayIndexOutOfBoundsException e) {
throw new CommandException(
Component.translatable(
"commands.console.server.error.invalid_server",
Component.text(server)
)
);
}
}
case "discord" -> {
if (Main.discord == null || Main.discord.jda == null) {
throw new CommandException(Component.translatable("commands.generic.error.discord_disabled"));
}
final String channelId = context.bot.options.discordChannelId;
if (channelId == null) return null;
final String message = context.getString(true, true);
Main.discord.sendMessageInstantly(message, channelId, true);
return null;
}
case "logtoconsole" -> {
context.checkOverloadArgs(2);
@ -70,9 +99,12 @@ public class ConsoleCommand extends Command {
bot.logger.logToConsole = bool;
return Component.translatable(
"Logging to console is now %s",
bool ? Component.text("enabled").color(NamedTextColor.GREEN) : Component.text("disabled").color(NamedTextColor.RED)
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
"commands.console.logtoconsole.set",
bot.colorPalette.defaultColor,
bool
? Component.translatable("commands.generic.enabled", NamedTextColor.GREEN)
: Component.translatable("commands.generic.disabled", NamedTextColor.RED)
);
}
case "printdisconnectedreason" -> {
context.checkOverloadArgs(2);
@ -82,9 +114,12 @@ public class ConsoleCommand extends Command {
bot.printDisconnectedCause = bool;
return Component.translatable(
"Printing the disconnected cause is now %s",
bool ? Component.text("enabled").color(NamedTextColor.GREEN) : Component.text("disabled").color(NamedTextColor.RED)
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
"commands.console.printdisconnectedreason.set",
bot.colorPalette.defaultColor,
bool
? Component.translatable("commands.generic.enabled", NamedTextColor.GREEN)
: Component.translatable("commands.generic.disabled", NamedTextColor.RED)
);
}
}

View file

@ -11,16 +11,14 @@ public class CowsayCommand extends Command {
public CowsayCommand () {
super(
"cowsay",
"Moo",
new String[] { "<message>" },
new String[] {},
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final String message = context.getString(true, true);
final CowExecutor cowExecutor = new CowExecutor();

View file

@ -5,22 +5,23 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import net.kyori.adventure.text.Component;
public class EchoCommand extends Command {
public EchoCommand () {
super(
"echo",
"Makes the bot say a message",
new String[] { "<message>" },
new String[] { "say" },
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[] { ChatPacketType.DISGUISED }
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
bot.chat.send(context.getString(true, true));

View file

@ -5,27 +5,28 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import net.kyori.adventure.text.Component;
public class EndCommand extends Command {
public EndCommand () {
super(
"end",
"End/Reconnects the bot",
new String[] { "" },
new String[] { "reconnect", "restart" },
TrustLevel.TRUSTED,
false
new String[] { "reconnect" },
TrustLevel.TRUSTED
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(0);
final Bot bot = context.bot;
bot.session.disconnect("End command");
bot.executorService.execute(
() -> bot.session.disconnect(I18nUtilities.get("commands.end.disconnect_reason"))
);
return null;
}

View file

@ -5,8 +5,9 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.data.eval.EvalOutput;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.plugins.EvalPlugin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -16,21 +17,21 @@ public class EvalCommand extends Command {
public EvalCommand () {
super(
"eval",
"Evaluate JavaScript codes on a Node.JS container running @n8n/vm2",
new String[] { "run <code>", "reset" },
new String[] {},
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[]{ ChatPacketType.DISGUISED }
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
if (!bot.eval.connected) throw new CommandException(Component.text("Eval server is not online"));
if (!EvalPlugin.connected) throw new CommandException(Component.translatable("commands.eval.error.offline"));
final String action = context.getString(false, true);
final String action = context.getAction();
switch (action) {
case "run" -> {
@ -38,19 +39,22 @@ public class EvalCommand extends Command {
final CompletableFuture<EvalOutput> future = bot.eval.run(command);
future.thenApplyAsync(output -> {
if (output.isError()) context.sendOutput(Component.text(output.output()).color(NamedTextColor.RED));
else context.sendOutput(Component.text(output.output()));
// it returns null when the eval server isn't online, even though we have already checked,
// i'm just fixing the warning here
if (future == null) return null;
return output;
future.thenApply(result -> {
if (result.isError()) context.sendOutput(Component.text(result.output(), NamedTextColor.RED));
else context.sendOutput(Component.text(result.output()));
return result;
});
}
case "reset" -> {
bot.eval.reset();
return Component.text("Reset the eval worker").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
EvalPlugin.reset();
return Component.translatable("commands.eval.reset", bot.colorPalette.defaultColor);
}
default -> throw new CommandException(Component.text("Invalid action"));
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
return null;

View file

@ -1,96 +1,124 @@
package me.chayapak1.chomens_bot.commands;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.FilteredPlayer;
import me.chayapak1.chomens_bot.plugins.FilterPlugin;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.command.*;
import me.chayapak1.chomens_bot.data.filter.PlayerFilter;
import me.chayapak1.chomens_bot.plugins.DatabasePlugin;
import me.chayapak1.chomens_bot.plugins.PlayerFilterPlugin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class FilterCommand extends Command {
public FilterCommand () {
super(
"filter",
"Filter players",
new String[] {
"add <player>",
"-ignorecase add <player>",
"-regex add <player>",
"-ignorecase -regex add <player>",
"add <player> [reason]",
"-ignorecase add <player> [reason]",
"-regex add <player> [reason]",
"remove <index>",
"clear",
"list"
},
new String[] { "filterplayer", "ban", "blacklist" },
TrustLevel.OWNER,
false
TrustLevel.ADMIN
);
}
// most of these codes are from cloop and greplog
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
boolean ignoreCase = false;
boolean regex = false;
final List<String> flags = context.getFlags(true, CommonFlags.IGNORE_CASE, CommonFlags.REGEX);
String action = context.getString(false, true);
final boolean ignoreCase = flags.contains(CommonFlags.IGNORE_CASE);
final boolean regex = flags.contains(CommonFlags.REGEX);
// run 2 times. for example `*filter -ignorecase -regex add test` will be both accepted
for (int i = 0; i < 2; i++) {
if (action.equals("-ignorecase")) {
ignoreCase = true;
action = context.getString(false, true);
} else if (action.equals("-regex")) {
regex = true;
action = context.getString(false, true);
}
}
final Gson gson = new Gson();
final String action = context.getString(false, true, true);
switch (action) {
case "add" -> {
final String player = context.getString(true, true);
final String player = context.getString(false, true);
final String reason = context.getString(true, false);
bot.filter.add(player, regex, ignoreCase);
return Component.translatable(
"Added %s to the filters",
Component.text(player).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
if (
PlayerFilterPlugin.localList.stream()
.map(PlayerFilter::playerName)
.toList()
.contains(player)
) {
throw new CommandException(
Component.translatable(
"commands.filter.add.error.already_exists",
Component.text(player)
)
);
}
if (regex) {
// try validating the regex
try {
Pattern.compile(player);
} catch (final PatternSyntaxException e) {
throw new CommandException(
Component.translatable(
"commands.filter.error.invalid_regex",
Component.text(e.toString())
)
);
}
}
DatabasePlugin.EXECUTOR_SERVICE.execute(() -> bot.playerFilter.add(player, reason, regex, ignoreCase));
if (reason.isEmpty()) {
return Component.translatable(
"commands.filter.add.no_reason",
bot.colorPalette.defaultColor,
Component.text(player, bot.colorPalette.username)
);
} else {
return Component.translatable(
"commands.filter.add.reason",
bot.colorPalette.defaultColor,
Component.text(player, bot.colorPalette.username),
Component.text(reason, bot.colorPalette.string)
);
}
}
case "remove" -> {
context.checkOverloadArgs(2);
try {
final int index = context.getInteger(true);
final int index = context.getInteger(true);
final FilteredPlayer removed = bot.filter.remove(index);
try {
final PlayerFilter player = PlayerFilterPlugin.localList.get(index);
if (player == null) throw new IllegalArgumentException();
DatabasePlugin.EXECUTOR_SERVICE.execute(() -> bot.playerFilter.remove(player.playerName()));
return Component.translatable(
"Removed %s from the filters",
Component.text(removed.playerName).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
} catch (IndexOutOfBoundsException | IllegalArgumentException | NullPointerException ignored) {
throw new CommandException(Component.text("Invalid index"));
"commands.filter.remove.output",
bot.colorPalette.defaultColor,
Component.text(player.playerName(), bot.colorPalette.username)
);
} catch (final IndexOutOfBoundsException | IllegalArgumentException | NullPointerException e) {
throw new CommandException(Component.translatable("commands.generic.error.invalid_index"));
}
}
case "clear" -> {
context.checkOverloadArgs(1);
bot.filter.clear();
return Component.text("Cleared the filter").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
DatabasePlugin.EXECUTOR_SERVICE.execute(bot.playerFilter::clear);
return Component.translatable("commands.filter.clear.output", bot.colorPalette.defaultColor);
}
case "list" -> {
context.checkOverloadArgs(1);
@ -98,45 +126,66 @@ public class FilterCommand extends Command {
final List<Component> filtersComponents = new ArrayList<>();
int index = 0;
for (JsonElement playerElement : FilterPlugin.filteredPlayers) {
final FilteredPlayer player = gson.fromJson(playerElement, FilteredPlayer.class);
for (final PlayerFilter player : PlayerFilterPlugin.localList) {
Component options = Component.empty().color(NamedTextColor.DARK_GRAY);
if (player.ignoreCase || player.regex) {
if (player.ignoreCase() || player.regex()) {
final List<Component> args = new ArrayList<>();
if (player.ignoreCase) args.add(Component.text("ignore case"));
if (player.regex) args.add(Component.text("regex"));
if (player.ignoreCase()) args.add(Component.translatable("commands.filter.list.ignore_case"));
if (player.regex()) args.add(Component.translatable("commands.filter.list.regex"));
options = options.append(Component.text("("));
options = options.append(Component.join(JoinConfiguration.commas(true), args).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)));
options = options.append(Component.text(")"));
options = options
.append(Component.text("("))
.append(
Component
.join(
JoinConfiguration.commas(true),
args
)
.color(bot.colorPalette.string)
)
.append(Component.text(")"))
.append(Component.space());
}
if (!player.reason().isEmpty()) {
options = options
.append(Component.text("("))
.append(
Component.translatable(
"commands.filter.list.reason",
NamedTextColor.GRAY,
Component.text(player.reason(), bot.colorPalette.string)
)
)
.append(Component.text(")"));
}
filtersComponents.add(
Component.translatable(
"%s %s %s",
Component.text(index).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
Component.text(player.playerName).color(ColorUtilities.getColorByString(bot.config.colorPalette.username)),
NamedTextColor.DARK_GRAY,
Component.text(index, bot.colorPalette.number),
Component.text(player.playerName(), bot.colorPalette.username),
options
).color(NamedTextColor.DARK_GRAY)
)
);
index++;
}
return Component.empty()
.append(Component.text("Filtered players ").color(NamedTextColor.GREEN))
.append(Component.text("(").color(NamedTextColor.DARK_GRAY))
.append(Component.text(FilterPlugin.filteredPlayers.size()).color(NamedTextColor.GRAY))
.append(Component.text(")").color(NamedTextColor.DARK_GRAY))
.append(Component.translatable("commands.filter.list.filtered_players_text", NamedTextColor.GREEN))
.append(Component.text("(", NamedTextColor.DARK_GRAY))
.append(Component.text(PlayerFilterPlugin.localList.size(), NamedTextColor.GRAY))
.append(Component.text(")", NamedTextColor.DARK_GRAY))
.append(Component.newline())
.append(
Component.join(JoinConfiguration.newlines(), filtersComponents)
);
}
default -> throw new CommandException(Component.text("Invalid action"));
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
}
}

View file

@ -1,91 +1,122 @@
package me.chayapak1.chomens_bot.commands;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.fasterxml.jackson.databind.JsonNode;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.plugins.PlayersPersistentDataPlugin;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.plugins.DatabasePlugin;
import net.kyori.adventure.text.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import java.util.Optional;
public class FindAltsCommand extends Command {
public FindAltsCommand() {
// we allow both, since the flag used to be `allserver`
private static final String ALL_SERVER_FLAG = "allserver";
private static final String ALL_SERVERS_FLAG = "allservers";
private static final int LIMIT = 200;
public FindAltsCommand () {
super(
"findalts",
"Finds players with the same IP address",
new String[] { "<player>", "<ip>" },
new String[] { "-allservers <player|ip>", "<player|ip>" },
new String[] { "alts", "sameip" },
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[] { ChatPacketType.DISGUISED }
);
}
@Override
public Component execute(CommandContext context) throws Exception {
public Component execute (final CommandContext context) throws Exception {
final Bot bot = context.bot;
if (Main.database == null)
throw new CommandException(Component.translatable("commands.generic.error.database_disabled"));
Main.database.checkOverloaded();
final List<String> flags = context.getFlags(true, ALL_SERVER_FLAG, ALL_SERVERS_FLAG);
final boolean allServer = flags.contains(ALL_SERVER_FLAG) || flags.contains(ALL_SERVERS_FLAG);
final String player = context.getString(true, true);
final PlayerEntry playerEntry = bot.players.getEntry(player);
DatabasePlugin.EXECUTOR_SERVICE.execute(() -> {
final PlayerEntry playerInTheServer = bot.players.getEntry(player);
if (playerEntry == null) return handle(bot, player, true, player);
else {
final CompletableFuture<String> future = bot.players.getPlayerIP(playerEntry);
final String ipFromUsername;
if (future == null) return null;
if (playerInTheServer == null || playerInTheServer.persistingData.ip == null)
ipFromUsername = bot.playersDatabase.getPlayerIP(player);
else ipFromUsername = playerInTheServer.persistingData.ip;
future.thenApplyAsync(targetIP -> {
context.sendOutput(handle(bot, targetIP, false, player));
return targetIP;
});
}
if (ipFromUsername == null) {
context.sendOutput(handle(bot, player, player, allServer));
} else {
context.sendOutput(handle(bot, ipFromUsername, player, allServer));
}
});
return null;
}
private Component handle (Bot bot, String targetIP, boolean argumentIsIP, String player) {
final Stream<String> matches = PlayersPersistentDataPlugin.playersObject.deepCopy().entrySet() // is calling deepCopy necessary?
.stream()
.filter(
entry -> {
final JsonObject ipsObject = entry
.getValue().getAsJsonObject().getAsJsonObject("ips");
private Component handle (final Bot bot, final String targetIP, final String player, final boolean allServer) {
final Map<String, JsonNode> altsMap = bot.playersDatabase.findPlayerAlts(targetIP, allServer, LIMIT);
if (ipsObject == null) return false;
final Component playerComponent = Component.text(player, bot.colorPalette.username);
final JsonElement currentServerIP = ipsObject.get(bot.host + ":" + bot.port);
if (currentServerIP == null) return false;
return currentServerIP.getAsString().equals(targetIP);
}
)
.map(Map.Entry::getKey);
final boolean isIP = targetIP.equals(player);
Component component = Component
.translatable("Possible alts for the %s %s:")
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
.translatable("commands.findalts.output", bot.colorPalette.defaultColor)
.arguments(
Component.text(argumentIsIP ? "IP" : "player"),
Component.text(player).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
Component.translatable(isIP ? "commands.findalts.ip" : "commands.findalts.player"),
isIP ?
playerComponent :
Component.translatable(
"%s (%s)",
playerComponent,
Component
.text(targetIP)
.color(bot.colorPalette.number)
)
)
.appendNewline();
final List<String> sorted = altsMap.entrySet().stream()
.limit(200) // only find 200 alts because more than this is simply too many
.sorted((a, b) -> {
final JsonNode aTimeNode = Optional.ofNullable(a.getValue().get("lastSeen"))
.map(node -> node.get("time"))
.orElse(null);
final JsonNode bTimeNode = Optional.ofNullable(b.getValue().get("lastSeen"))
.map(node -> node.get("time"))
.orElse(null);
if (aTimeNode == null && bTimeNode == null) return 0;
if (aTimeNode == null) return 1;
if (bTimeNode == null) return -1;
return Long.compare(bTimeNode.asLong(), aTimeNode.asLong());
})
.map(Map.Entry::getKey)
.toList();
int i = 0;
for (String name : matches.toList()) {
for (final String username : sorted) {
component = component
.append(
Component
.text(name)
.color((i++ & 1) == 0 ? ColorUtilities.getColorByString(bot.config.colorPalette.primary) : ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
.text(username)
.color((i++ & 1) == 0 ? bot.colorPalette.primary : bot.colorPalette.secondary)
)
.appendSpace();
}

View file

@ -1,86 +1,65 @@
package me.chayapak1.chomens_bot.commands;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.List;
public class GrepLogCommand extends Command {
private Thread thread;
public GrepLogCommand() {
public GrepLogCommand () {
super(
"greplog",
"Queries the bot's logs",
new String[] { "<input>", "...-ignorecase...", "...-regex...", "stop" },
new String[] { "<input>", "-ignorecase ...", "-regex ...", "stop" },
new String[] { "logquery", "findlog" },
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
if (bot.discord.jda == null) throw new CommandException(Component.text("The bot's Discord integration has to be enabled to use the command."));
boolean ignoreCase = false;
boolean regex = false;
String firstInput = context.getString(false, true);
// run 2 times. for example `*greplog -ignorecase -regex test` will be both accepted
for (int i = 0; i < 2; i++) {
if (firstInput.equals("-ignorecase")) {
ignoreCase = true;
firstInput = context.getString(false, true);
} else if (firstInput.equals("-regex")) {
regex = true;
firstInput = context.getString(false, true);
}
if (Main.discord == null || Main.discord.jda == null) {
throw new CommandException(Component.translatable("commands.generic.error.discord_disabled"));
}
// interesting code
final String input = (firstInput + " " + context.getString(true, false)).trim();
final List<String> flags = context.getFlags(true, CommonFlags.IGNORE_CASE, CommonFlags.REGEX);
if (input.equals("stop")) {
if (thread == null) throw new CommandException(Component.text("There is no query process running"));
final boolean ignoreCase = flags.contains(CommonFlags.IGNORE_CASE);
final boolean regex = flags.contains(CommonFlags.REGEX);
final String input = context.getString(true, true);
if (input.equalsIgnoreCase("stop")) {
if (thread == null) throw new CommandException(Component.translatable("commands.greplog.error.not_running"));
bot.grepLog.running = false;
bot.grepLog.pattern = null;
thread.interrupt(); // ? should i interrupt it this way?
thread = null;
return Component.text("Stopped querying the logs").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.greplog.stopped", bot.colorPalette.defaultColor);
}
if (thread != null) throw new CommandException(Component.text("Another query is already running"));
if (thread != null) throw new CommandException(Component.translatable("commands.greplog.error.already_running"));
context.sendOutput(
Component
.translatable("Started querying the logs for %s")
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
.translatable("commands.greplog.started", bot.colorPalette.defaultColor)
.arguments(
Component
.text(input)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
Component.text(input, bot.colorPalette.string)
)
);
final boolean finalIgnoreCase = ignoreCase;
final boolean finalRegex = regex;
thread = new Thread(() -> {
try {
bot.grepLog.search(context, input, finalIgnoreCase, finalRegex);
} catch (CommandException e) {
bot.grepLog.search(context, input, ignoreCase, regex);
} catch (final CommandException e) {
context.sendOutput(e.message.color(NamedTextColor.RED));
}

View file

@ -1,18 +1,19 @@
package me.chayapak1.chomens_bot.commands;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.command.contexts.ConsoleCommandContext;
import me.chayapak1.chomens_bot.plugins.CommandHandlerPlugin;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -21,133 +22,163 @@ public class HelpCommand extends Command {
public HelpCommand () {
super(
"help",
"Shows a command list or usage for a command",
new String[] { "[command]" },
new String[] { "heko", "cmds", "commands" },
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
private CommandContext context;
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
this.context = context;
final String commandName = context.getString(false, false);
if (commandName.isEmpty()) {
return sendCommandList();
if (commandName.isBlank()) {
return getCommandList(context);
} else {
return sendUsages(context, commandName);
return getUsages(context, commandName);
}
}
public Component sendCommandList () throws CommandException {
final List<Component> list = new ArrayList<>();
list.addAll(getCommandListByTrustLevel(TrustLevel.PUBLIC));
list.addAll(getCommandListByTrustLevel(TrustLevel.TRUSTED));
list.addAll(getCommandListByTrustLevel(TrustLevel.OWNER));
public Component getCommandList (final CommandContext context) {
final List<Component> list = new ObjectArrayList<>();
for (final TrustLevel level : TrustLevel.values()) {
list.addAll(getCommandListByTrustLevel(context, level));
}
final Component trustLevels = Component.join(
JoinConfiguration.spaces(),
Arrays.stream(TrustLevel.values())
.map(level -> level.component)
.toList()
);
return Component.empty()
.append(Component.text("Commands ").color(NamedTextColor.GRAY))
.append(Component.text("(").color(NamedTextColor.DARK_GRAY))
.append(Component.text(list.size()).color(NamedTextColor.GREEN))
.append(Component.text(") ").color(NamedTextColor.DARK_GRAY))
.append(Component.text("(").color(NamedTextColor.DARK_GRAY))
.append(Component.text("Public ").color(NamedTextColor.GREEN))
.append(Component.text("Trusted ").color(NamedTextColor.RED))
.append(Component.text("Owner").color(NamedTextColor.DARK_RED))
.append(Component.text(") - ").color(NamedTextColor.DARK_GRAY))
.append(Component.join(JoinConfiguration.separator(Component.space()), list));
.append(Component.translatable("commands.help.commands_text", NamedTextColor.GRAY))
.append(Component.text("(", NamedTextColor.DARK_GRAY))
.append(Component.text(list.size(), NamedTextColor.GREEN))
.append(Component.text(") ", NamedTextColor.DARK_GRAY))
.append(Component.text("(", NamedTextColor.DARK_GRAY))
.append(Component.translatable("%s", trustLevels))
.append(Component.text(") - ", NamedTextColor.DARK_GRAY))
.append(Component.join(JoinConfiguration.separator(Component.space()), list));
}
public List<Component> getCommandListByTrustLevel(TrustLevel trustLevel) throws CommandException {
final List<Component> list = new ArrayList<>();
public List<Component> getCommandListByTrustLevel (final CommandContext context, final TrustLevel trustLevel) {
final Bot bot = context.bot;
List<String> commandNames = new ArrayList<>();
final List<Component> list = new ObjectArrayList<>();
for (Command command : CommandHandlerPlugin.commands) {
if (command.trustLevel != trustLevel || command.consoleOnly) continue;
final List<String> commandNames = new ObjectArrayList<>();
for (final Command command : CommandHandlerPlugin.COMMANDS) {
if (command.trustLevel != trustLevel || (command.consoleOnly && !(context instanceof ConsoleCommandContext)))
continue;
commandNames.add(command.name);
}
Collections.sort(commandNames);
for (String name : commandNames) {
for (final String name : commandNames) {
final String clickSuggestion = context.prefix + name; // *command
final String insertionSuggestion = context.prefix + this.name + " " + name; // *help <command>
list.add(
Component
.text(name)
.color(getColorByTrustLevel(trustLevel))
.color(trustLevel.component.color())
.clickEvent(
ClickEvent.suggestCommand(clickSuggestion)
)
.insertion(insertionSuggestion)
.hoverEvent(
HoverEvent.showText(
sendUsages(context, name)
Component.empty()
.color(NamedTextColor.GREEN)
.append(
Component.translatable(
"commands.help.hover.click_to_command",
Component.text(clickSuggestion, bot.colorPalette.string)
)
)
.append(Component.newline())
.append(
Component.translatable(
"commands.help.hover.shift_click_to_help_command",
Component.text(insertionSuggestion, bot.colorPalette.string)
)
)
)
)
// there are too many commands and having hover being the usages will make the command length > 32767 :(
/* .hoverEvent(
HoverEvent.showText(
getUsages(context, name)
)
) */
);
}
return list;
}
public NamedTextColor getColorByTrustLevel (TrustLevel trustLevel) {
return switch (trustLevel) {
case PUBLIC -> NamedTextColor.GREEN;
case TRUSTED -> NamedTextColor.RED;
case OWNER -> NamedTextColor.DARK_RED;
};
}
public Component sendUsages (CommandContext context, String commandName) throws CommandException {
public Component getUsages (final CommandContext context, final String commandName) throws CommandException {
final Bot bot = context.bot;
final String prefix = context.prefix;
for (Command command : CommandHandlerPlugin.commands) {
if (!command.name.equalsIgnoreCase(commandName) && !Arrays.stream(command.aliases).toList().contains(commandName.toLowerCase())) continue;
for (final Command command : CommandHandlerPlugin.COMMANDS) {
if (
!command.name.equalsIgnoreCase(commandName) &&
!Arrays.stream(command.aliases).toList().contains(commandName.toLowerCase())
) continue;
final String actualCommandName = command.name.toLowerCase();
final List<Component> usages = new ArrayList<>();
final List<Component> usages = new ObjectArrayList<>();
usages.add(
Component.empty()
.append(Component.text(prefix + actualCommandName).color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary)))
.append(Component.text(
(command.aliases.length > 0 && !command.aliases[0].isEmpty()) ?
" (" + String.join(", ", command.aliases) + ")" :
""
).color(NamedTextColor.WHITE))
.append(Component.text(" - " + command.description)).color(NamedTextColor.GRAY)
.color(NamedTextColor.GRAY)
.append(Component.text(prefix + actualCommandName, bot.colorPalette.secondary))
.append(
Component
.text(
(command.aliases.length > 0 && !command.aliases[0].isEmpty()) ?
" (" + String.join(", ", command.aliases) + ")" :
"",
NamedTextColor.WHITE
)
)
.append(Component.text(" - "))
.append(
Component.translatable(
String.format(
"commands.%s.description",
actualCommandName
)
)
)
);
usages.add(
Component.empty()
.append(Component.text("Trust level: ").color(NamedTextColor.GREEN))
.append(Component.text(command.trustLevel.name()).color(NamedTextColor.YELLOW))
.append(Component.translatable("commands.help.trust_level", NamedTextColor.GREEN))
.append(
command.trustLevel.component
.append(Component.text(" - "))
.append(Component.text(command.trustLevel.level))
)
);
for (String usage : command.usages) {
for (final String usage : command.usages) {
Component usageComponent = Component.empty()
.append(Component.text(prefix + actualCommandName).color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary)))
.append(Component.text(prefix + actualCommandName, bot.colorPalette.secondary))
.append(Component.text(" "));
if (command.trustLevel == TrustLevel.TRUSTED) {
usageComponent = usageComponent
.append(Component.text("<hash>"))
.append(Component.space())
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string));
} else if (command.trustLevel == TrustLevel.OWNER) {
usageComponent = usageComponent
.append(Component.text("<ownerHash>"))
.append(Component.space())
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string));
}
usageComponent = usageComponent.append(Component.text(usage).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)));
usageComponent = usageComponent.append(Component.text(usage, bot.colorPalette.string));
usages.add(usageComponent);
}
@ -155,6 +186,6 @@ public class HelpCommand extends Command {
return Component.join(JoinConfiguration.separator(Component.newline()), usages);
}
throw new CommandException(Component.text("Unknown command"));
throw new CommandException(Component.translatable("commands.help.error.unknown_command"));
}
}

View file

@ -1,75 +1,99 @@
package me.chayapak1.chomens_bot.commands;
import com.google.gson.JsonElement;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.plugins.DatabasePlugin;
import me.chayapak1.chomens_bot.plugins.IPFilterPlugin;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class IPFilterCommand extends Command {
public IPFilterCommand() {
public IPFilterCommand () {
super(
"ipfilter",
"Filter IPs",
new String[] {
"add <ip>",
"add <ip> [reason]",
"remove <index>",
"clear",
"list"
},
new String[] { "filterip", "banip", "ipban" },
TrustLevel.OWNER,
false
TrustLevel.ADMIN
);
}
// most of these codes are from cloop and greplog
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String action = context.getString(false, true, true);
final String action = context.getAction();
switch (action) {
case "add" -> {
final String ip = context.getString(true, true);
final String ip = context.getString(false, true);
final String reason = context.getString(true, false);
bot.ipFilter.add(ip);
return Component.translatable(
"Added %s to the filters",
Component.text(ip).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
if (IPFilterPlugin.localList.containsKey(ip)) {
throw new CommandException(
Component.translatable(
"commands.ipfilter.add.error.already_exists",
Component.text(ip)
)
);
}
DatabasePlugin.EXECUTOR_SERVICE.execute(() -> bot.ipFilter.add(ip, reason));
if (reason.isEmpty()) {
return Component.translatable(
"commands.filter.add.no_reason",
bot.colorPalette.defaultColor,
Component.text(ip, bot.colorPalette.username)
);
} else {
return Component.translatable(
"commands.filter.add.reason",
bot.colorPalette.defaultColor,
Component.text(ip, bot.colorPalette.username),
Component.text(reason, bot.colorPalette.string)
);
}
}
case "remove" -> {
context.checkOverloadArgs(2);
try {
final int index = context.getInteger(true);
final int index = context.getInteger(true);
final String removed = bot.ipFilter.remove(index);
try {
final String targetIP = new ArrayList<>(IPFilterPlugin.localList.keySet()).get(index);
if (targetIP == null) throw new IllegalArgumentException();
DatabasePlugin.EXECUTOR_SERVICE.execute(() -> bot.ipFilter.remove(targetIP));
return Component.translatable(
"Removed %s from the filters",
Component.text(removed).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
} catch (IndexOutOfBoundsException | IllegalArgumentException | NullPointerException ignored) {
throw new CommandException(Component.text("Invalid index"));
"commands.ipfilter.remove.output",
bot.colorPalette.defaultColor,
Component.text(targetIP, bot.colorPalette.username)
);
} catch (final IndexOutOfBoundsException | IllegalArgumentException | NullPointerException e) {
throw new CommandException(Component.translatable("commands.generic.error.invalid_index"));
}
}
case "clear" -> {
context.checkOverloadArgs(1);
bot.ipFilter.clear();
return Component.text("Cleared the filter").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
DatabasePlugin.EXECUTOR_SERVICE.execute(bot.ipFilter::clear);
return Component.translatable("commands.ipfilter.clear.output", bot.colorPalette.defaultColor);
}
case "list" -> {
context.checkOverloadArgs(1);
@ -77,31 +101,49 @@ public class IPFilterCommand extends Command {
final List<Component> filtersComponents = new ArrayList<>();
int index = 0;
for (JsonElement playerElement : IPFilterPlugin.filteredIPs) {
for (final Map.Entry<String, String> entry : IPFilterPlugin.localList.entrySet()) {
final String ip = entry.getKey();
final String reason = entry.getValue();
Component reasonComponent = Component.empty().color(NamedTextColor.DARK_GRAY);
if (!reason.isEmpty()) {
reasonComponent = reasonComponent
.append(Component.text("("))
.append(
Component.translatable(
"commands.ipfilter.list.reason",
NamedTextColor.GRAY,
Component.text(reason, bot.colorPalette.string)
)
)
.append(Component.text(")"));
}
filtersComponents.add(
Component.translatable(
"%s %s",
Component.text(index).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
Component.text(playerElement.getAsString()).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(NamedTextColor.DARK_GRAY)
"%s %s %s",
NamedTextColor.DARK_GRAY,
Component.text(index, bot.colorPalette.number),
Component.text(ip, bot.colorPalette.username),
reasonComponent
)
);
index++;
}
return Component.empty()
.append(Component.text("Filtered IPs ").color(NamedTextColor.GREEN))
.append(Component.text("(").color(NamedTextColor.DARK_GRAY))
.append(Component.text(IPFilterPlugin.filteredIPs.size()).color(NamedTextColor.GRAY))
.append(Component.text(")").color(NamedTextColor.DARK_GRAY))
.append(Component.translatable("commands.ipfilter.list.filtered_ips_text", NamedTextColor.GREEN))
.append(Component.text("(", NamedTextColor.DARK_GRAY))
.append(Component.text(IPFilterPlugin.localList.size(), NamedTextColor.GRAY))
.append(Component.text(")", NamedTextColor.DARK_GRAY))
.append(Component.newline())
.append(
Component.join(JoinConfiguration.newlines(), filtersComponents)
);
}
default -> {
throw new CommandException(Component.text("Invalid action"));
}
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
}
}

View file

@ -5,7 +5,8 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.util.StringUtilities;
import me.chayapak1.chomens_bot.util.TimeUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
@ -15,6 +16,7 @@ import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
@ -23,56 +25,69 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Optional;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class InfoCommand extends Command {
public static final String ORIGINAL_REPOSITORY_URL = "https://code.chipmunk.land/ChomeNS/chomens-bot-java";
public static final Properties BUILD_INFO = new Properties();
static {
try (final InputStream input = ClassLoader.getSystemClassLoader().getResourceAsStream("application.properties")) {
BUILD_INFO.load(input);
} catch (final IOException ignored) { }
}
public InfoCommand () {
super(
"info",
"Shows an info about various things",
new String[] {
"",
"<creator>",
"<discord>",
"<server>",
"<botuser>",
"<uptime>"
"creator",
"discord",
"server",
"botuser",
"botlogintime",
"uptime"
},
new String[] {},
TrustLevel.PUBLIC,
false
new String[] { "creator", "discord", "botuser", "botlogintime", "uptime" },
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
final String action = context.getString(false, false, true);
final String action = !context.userInputCommandName.equalsIgnoreCase(this.name) // if the input command is not `info`
? context.userInputCommandName.toLowerCase() // use that as the action (e.g. "discord", "creator")
: context.getString(false, false, true); // else just take the argument of `info`
switch (action) {
case "creator" -> {
return Component.empty()
.append(Component.text("ChomeNS Bot ").color(ColorUtilities.getColorByString(bot.config.colorPalette.primary)))
.append(Component.text("is created by ").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor)))
.append(Component.text("chayapak").color(ColorUtilities.getColorByString(bot.config.colorPalette.ownerName)));
return Component.translatable(
"commands.info.creator.output",
bot.colorPalette.defaultColor,
Component.text("ChomeNS Bot", bot.colorPalette.primary),
Component.text("chayapak", bot.colorPalette.ownerName)
);
}
case "discord" -> {
final String link = bot.config.discord.inviteLink;
return Component.empty()
.append(Component.text("The Discord invite is ").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor)))
.append(
Component
.text(link)
.clickEvent(ClickEvent.openUrl(link))
.color(NamedTextColor.BLUE)
);
return Component.translatable(
"commands.info.discord.output",
bot.colorPalette.defaultColor,
Component
.text(link, NamedTextColor.BLUE)
.clickEvent(ClickEvent.openUrl(link))
);
}
case "server" -> {
// totallynotskidded from extras' serverinfo
@ -84,10 +99,10 @@ public class InfoCommand extends Command {
final StringBuilder builder = new StringBuilder();
try {
RandomAccessFile file = new RandomAccessFile("/proc/cpuinfo", "r");
FileChannel channel = file.getChannel();
final RandomAccessFile file = new RandomAccessFile("/proc/cpuinfo", "r");
final FileChannel channel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); // 1 MB buffer
final ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); // 1 MB buffer
long bytesRead = channel.read(buffer);
while (bytesRead != -1) {
@ -103,50 +118,50 @@ public class InfoCommand extends Command {
channel.close();
file.close();
} catch (IOException ignored) {}
} catch (final IOException ignored) { }
final TextColor color = ColorUtilities.getColorByString(bot.config.colorPalette.string);
final TextColor color = bot.colorPalette.string;
final String[] lines = builder.toString().split("\n");
final Optional<String> modelName = Arrays.stream(lines)
.filter(line -> line.startsWith("model name"))
.findFirst();
final Component cpuModel = modelName
.map(s -> Component.text(s.split("\t: ")[1]).color(color))
.orElseGet(() -> Component.text("N/A").color(color));
.map(s -> Component.text(s.split("\t: ")[1], color))
.orElseGet(() -> Component.text("N/A", color));
InetAddress localHost = null;
try {
localHost = InetAddress.getLocalHost();
} catch (UnknownHostException ignored) {}
} catch (final UnknownHostException ignored) { }
component = Component.translatable(
"""
Hostname: %s
Working directory: %s
OS architecture: %s
OS version: %s
OS name: %s
CPU cores: %s
CPU model: %s
Threads: %s
Heap memory usage: %s""",
Component.text(localHost == null ? "N/A" : localHost.getHostName()).color(color),
Component.text(System.getProperty("user.dir")).color(color),
Component.text(os.getArch()).color(color),
Component.text(os.getVersion()).color(color),
Component.text(os.getName()).color(color),
Component.text(String.valueOf(Runtime.getRuntime().availableProcessors())).color(color),
"commands.info.server.output",
bot.colorPalette.secondary,
Component.text(localHost == null ? "N/A" : localHost.getHostName(), color),
Component.text(System.getProperty("user.dir"), color),
Component.text(os.getArch(), color),
Component.text(os.getVersion(), color),
Component.text(os.getName(), color),
Component.text(String.valueOf(Runtime.getRuntime().availableProcessors()), color),
cpuModel,
Component.text(String.valueOf(Thread.activeCount())).color(color),
Component.text(String.valueOf(Thread.activeCount()), color),
Component
.translatable(
"%s MB / %s MB",
color,
Component.text(heapUsage.getUsed() / 1024L / 1024L),
Component.text(heapUsage.getMax() / 1024L / 1024L)
).color(color)
).color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary));
),
Component
.translatable(
"%s MB / %s MB",
color,
Component.text((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024L / 1024L),
Component.text(Runtime.getRuntime().totalMemory() / 1024L / 1024L)
)
);
return component;
}
@ -155,34 +170,53 @@ public class InfoCommand extends Command {
final String uuid = bot.profile.getIdAsString();
return Component.translatable(
"The bot's username is: %s and the UUID is: %s",
"commands.info.botuser.output",
bot.colorPalette.defaultColor,
Component
.text(username)
.text(username, bot.colorPalette.username)
.hoverEvent(
HoverEvent.showText(
Component
.text("Click here to copy the username to your clipboard")
.color(NamedTextColor.GREEN)
Component.translatable(
"commands.generic.click_to_copy_username",
NamedTextColor.GREEN
)
)
)
.clickEvent(
ClickEvent.copyToClipboard(username)
)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.username)),
.clickEvent(ClickEvent.copyToClipboard(username)),
Component
.text(uuid)
.text(uuid, bot.colorPalette.uuid)
.hoverEvent(
HoverEvent.showText(
Component
.text("Click here to copy the UUID to your clipboard")
.color(NamedTextColor.GREEN)
Component.translatable(
"commands.generic.click_to_copy_uuid",
NamedTextColor.GREEN
)
)
)
.clickEvent(
ClickEvent.copyToClipboard(uuid)
)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.uuid))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
.clickEvent(ClickEvent.copyToClipboard(uuid))
);
}
case "botlogintime" -> {
final long loginTime = bot.loginTime;
final ZoneId zoneId = ZoneId.of("UTC");
final String formattedLoginTime = TimeUtilities.formatTime(
loginTime,
"MMMM d, yyyy, hh:mm:ss a Z",
zoneId
);
final DateFormat timeSinceFormatter = new SimpleDateFormat("HH 'hours' mm 'minutes' ss 'seconds'");
timeSinceFormatter.setTimeZone(TimeZone.getTimeZone(zoneId)); // very important
final String formattedTimeSince = timeSinceFormatter.format(new Date(System.currentTimeMillis() - loginTime));
return Component.translatable(
"commands.info.botlogintime.output",
bot.colorPalette.defaultColor,
Component.text(formattedLoginTime, bot.colorPalette.string),
Component.text(formattedTimeSince, bot.colorPalette.string)
);
}
case "uptime" -> {
final long uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000;
@ -193,40 +227,83 @@ public class InfoCommand extends Command {
final long seconds = TimeUnit.SECONDS.toSeconds(uptime) - (TimeUnit.SECONDS.toMinutes(uptime) * 60);
return Component.translatable(
"The bots uptime is: %s",
"commands.info.uptime.output",
bot.colorPalette.defaultColor,
Component.translatable(
"%s days, %s hours, %s minutes, %s seconds",
"%s %s, %s %s, %s %s, %s %s",
NamedTextColor.GREEN,
Component.text(days),
Component.text(StringUtilities.addPlural(days, "day")),
Component.text(hours),
Component.text(StringUtilities.addPlural(hours, "hour")),
Component.text(minutes),
Component.text(seconds)
).color(NamedTextColor.GREEN)
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
Component.text(StringUtilities.addPlural(minutes, "minute")),
Component.text(seconds),
Component.text(StringUtilities.addPlural(seconds, "second"))
)
);
}
default -> {
return Component.empty()
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
.append(Component.text("ChomeNS Bot").color(NamedTextColor.YELLOW))
.append(Component.space())
return Component
.translatable(
"commands.info.default.main_output",
bot.colorPalette.defaultColor,
Component.text("ChomeNS Bot", NamedTextColor.YELLOW),
Component
.text("Kaboom")
.style(
Style.style()
.color(NamedTextColor.GRAY)
.decorate(TextDecoration.BOLD)
)
)
.append(Component.newline())
.append(
Component
.translatable(
"is an open-source utility bot and a moderation bot made for " +
"the %s Minecraft server (and its clones) " +
"but also works on vanilla Minecraft servers. " +
"It was originally made for fun but " +
"I got addicted and made it a full blown bot."
"commands.info.default.original_repository",
bot.colorPalette.defaultColor,
Component
.text(ORIGINAL_REPOSITORY_URL)
.color(bot.colorPalette.string)
.clickEvent(
ClickEvent.openUrl(ORIGINAL_REPOSITORY_URL)
)
)
.arguments(Component.text("Kaboom").style(Style.style().color(NamedTextColor.GRAY).decorate(TextDecoration.BOLD)))
)
.append(Component.newline())
.append(Component.text("Original repository: "))
.append(
Component
.text(ORIGINAL_REPOSITORY_URL)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
.clickEvent(
ClickEvent.openUrl(ORIGINAL_REPOSITORY_URL)
.translatable(
"commands.info.default.compiled_at",
bot.colorPalette.defaultColor,
Component
.text(BUILD_INFO.getProperty("build.date", "unknown"))
.color(bot.colorPalette.string)
)
)
.append(
Component
.translatable(
"commands.info.default.git_commit",
bot.colorPalette.defaultColor,
Component
.text(BUILD_INFO.getProperty("build.git.commit.hash", "unknown"))
.color(bot.colorPalette.string),
Component
.text(BUILD_INFO.getProperty("build.git.commit.count", "unknown"))
.color(bot.colorPalette.number)
)
)
.append(Component.newline())
.append(
Component
.translatable(
"commands.info.default.build",
bot.colorPalette.defaultColor,
Component
.text(BUILD_INFO.getProperty("build.number", "unknown"))
.color(bot.colorPalette.number)
)
);
}

View file

@ -5,40 +5,47 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.data.exploitMethods.Kick;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import net.kyori.adventure.text.Component;
import java.util.Arrays;
import java.util.UUID;
public class KickCommand extends Command {
public KickCommand () {
super(
"kick",
"Kicks a player",
new String[] { "<player>" },
new String[] {},
TrustLevel.TRUSTED,
false
new String[] {},
TrustLevel.TRUSTED
);
final String allMethods = String.join(" | ", Arrays.stream(Kick.values()).map(Enum::name).toList());
this.usages = new String[] { "<" + allMethods + "> <player>" };
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final Kick method = context.getEnum(true, Kick.class);
final PlayerEntry entry = bot.players.getEntry(context.getString(true, true));
if (entry == null) throw new CommandException(Component.text("Invalid player name"));
if (entry == null) throw new CommandException(Component.translatable("commands.generic.error.invalid_player"));
final String name = entry.profile.getName();
final UUID uuid = entry.profile.getId();
bot.exploits.kick(uuid);
bot.exploits.kick(method, uuid);
return Component.empty()
.append(Component.text("Kicking player "))
.append(Component.text(name).color(ColorUtilities.getColorByString(bot.config.colorPalette.username)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.kick.output",
bot.colorPalette.defaultColor,
Component.text(name, bot.colorPalette.username),
Component.text(method.toString(), bot.colorPalette.string)
);
}
}

View file

@ -5,8 +5,7 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.event.ClickEvent;
@ -20,16 +19,14 @@ public class ListCommand extends Command {
public ListCommand () {
super(
"list",
"Lists all players in the server (including vanished)",
new String[] {},
new String[] {},
TrustLevel.PUBLIC,
false
new String[] { "players" },
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(0);
final Bot bot = context.bot;
@ -38,45 +35,114 @@ public class ListCommand extends Command {
final List<Component> playersComponent = new ArrayList<>();
for (PlayerEntry entry : list) {
for (final PlayerEntry entry : list) {
if (entry == null) continue;
// chayapak
// b58cac19-066b-307b-97b1-d6e19ed08d7c
//
// Usernames: foo, bar, baz or No other usernames associated
// Vanished: false
// Latency: 32
// Game Mode: CREATIVE
// IP Address: 127.0.0.1
//
// Click to copy the username to your clipboard
// Shift+Click to insert the UUID into your chat box
final Component hoverEvent = Component
.text(entry.profile.getName())
.append(Component.newline())
.append(Component.text(entry.profile.getIdAsString(), bot.colorPalette.uuid))
.append(Component.newline())
.append(Component.newline())
.append(
entry.persistingData.usernames.isEmpty()
? Component.translatable(
"commands.list.no_other_usernames",
NamedTextColor.GRAY
)
: Component.translatable(
"commands.list.with_usernames",
bot.colorPalette.secondary,
Component
.join(
JoinConfiguration.commas(true),
entry.persistingData.usernames
.stream()
.map(Component::text)
.toList()
)
.color(bot.colorPalette.string)
)
)
.append(Component.newline())
.append(
Component.translatable(
"commands.list.vanished",
bot.colorPalette.secondary,
Component.text(!entry.persistingData.listed, bot.colorPalette.string)
)
)
.append(Component.newline())
.append(
Component.translatable(
"commands.list.latency",
bot.colorPalette.secondary,
Component
.text(entry.latency, bot.colorPalette.string) // using number color palette will not blend in (GOLD)
.append(Component.text("ms"))
)
)
.append(Component.newline())
.append(
Component.translatable(
"commands.list.game_mode",
bot.colorPalette.secondary,
Component.text(entry.gamemode.name(), bot.colorPalette.string)
)
)
.append(Component.newline())
.append(
Component.translatable(
"commands.list.ip_address",
bot.colorPalette.secondary,
Component.text(
entry.persistingData.ip == null
? "N/A"
: entry.persistingData.ip,
bot.colorPalette.string
)
)
)
.append(Component.newline())
.append(Component.newline())
.append(Component.translatable("commands.generic.click_to_copy_username", NamedTextColor.GREEN))
.append(Component.newline())
.append(Component.translatable("commands.generic.shift_click_to_insert_uuid", NamedTextColor.GREEN));
playersComponent.add(
Component.translatable(
"%s %s",
entry.displayName == null ?
Component.text(entry.profile.getName()).color(ColorUtilities.getColorByString(bot.config.colorPalette.username)) :
entry.displayName
.hoverEvent(
HoverEvent.showText(
Component
.text(entry.profile.getName())
.append(Component.newline())
.append(Component.text("Click here to copy the username to your clipboard").color(NamedTextColor.GREEN))
)
)
.clickEvent(
ClickEvent.copyToClipboard(entry.profile.getName())
)
.color(NamedTextColor.WHITE),
Component
.text(entry.profile.getIdAsString())
.hoverEvent(
HoverEvent.showText(
Component.text("Click here to copy the UUID to your clipboard").color(NamedTextColor.GREEN)
)
)
.clickEvent(
ClickEvent.copyToClipboard(entry.profile.getIdAsString())
)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.uuid))
).color(NamedTextColor.DARK_GRAY)
Component
.translatable(
"%s",
entry.displayName == null ?
Component.text(entry.profile.getName()) :
entry.displayName
)
.hoverEvent(
HoverEvent.showText(hoverEvent)
)
.clickEvent(
ClickEvent.copyToClipboard(entry.profile.getName())
)
.insertion(entry.profile.getIdAsString())
);
}
return Component.empty()
.append(Component.text("Players ").color(NamedTextColor.GREEN))
.append(Component.text("(").color(NamedTextColor.DARK_GRAY))
.append(Component.text(list.size()).color(NamedTextColor.GRAY))
.append(Component.text(")").color(NamedTextColor.DARK_GRAY))
.append(Component.translatable("commands.list.players_text", NamedTextColor.GREEN))
.append(Component.text("(", NamedTextColor.DARK_GRAY))
.append(Component.text(list.size(), NamedTextColor.GRAY))
.append(Component.text(")", NamedTextColor.DARK_GRAY))
.append(Component.newline())
.append(
Component.join(JoinConfiguration.newlines(), playersComponent)

View file

@ -1,30 +1,23 @@
package me.chayapak1.chomens_bot.commands;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.Mail;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.plugins.MailPlugin;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.PersistentDataUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import me.chayapak1.chomens_bot.data.mail.Mail;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.plugins.DatabasePlugin;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import me.chayapak1.chomens_bot.util.TimeUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@ -33,212 +26,164 @@ public class MailCommand extends Command {
public MailCommand () {
super(
"mail",
"Sends a mail",
new String[] { "send <player> <message>", "sendselecteditem <player>", "read" },
new String[] {},
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
if (Main.database == null)
throw new CommandException(Component.translatable("commands.generic.error.database_disabled"));
Main.database.checkOverloaded();
final PlayerEntry sender = context.sender;
final Gson gson = new Gson();
// kinda messy ngl
final String action = context.getString(false, true, true);
final String action = context.getAction();
switch (action) {
case "send" -> {
int senderMailsSentTotal = 0;
for (JsonElement mailElement : MailPlugin.mails) {
final Mail mail = gson.fromJson(mailElement, Mail.class);
case "send" -> DatabasePlugin.EXECUTOR_SERVICE.execute(() -> {
try {
bot.mail.send(
new Mail(
sender.profile.getName(),
context.getString(false, true),
Instant.now().toEpochMilli(),
bot.getServerString(),
context.getString(true, true)
)
);
if (mail.sentBy == null) continue;
if (!mail.sentBy.equals(sender.profile.getName())) continue;
senderMailsSentTotal++;
context.sendOutput(Component.translatable("commands.mail.sent", bot.colorPalette.defaultColor));
} catch (final CommandException e) {
context.sendOutput(e.message.colorIfAbsent(NamedTextColor.RED));
}
if (senderMailsSentTotal > 256) {
throw new CommandException(Component.text("You are sending too many mails!"));
}
bot.mail.send(
new Mail(
sender.profile.getName(),
context.getString(false, true),
Instant.now().toEpochMilli(),
bot.host + ":" + bot.port,
context.getString(true, true)
)
);
return Component.text("Mail sent!").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
});
case "sendselecteditem" -> {
context.checkOverloadArgs(2);
int senderMailsSentTotal = 0;
for (JsonElement mailElement : MailPlugin.mails) {
final Mail mail = gson.fromJson(mailElement, Mail.class);
if (!mail.sentTo.equals(sender.profile.getName())) continue;
senderMailsSentTotal++;
}
if (senderMailsSentTotal > 256) {
throw new CommandException(Component.text("You are sending too many mails!"));
}
final CompletableFuture<Component> future = bot.core.runTracked(
"minecraft:data get entity " +
UUIDUtilities.selector(sender.profile.getId()) +
" SelectedItem.components.minecraft:custom_data.message"
final CompletableFuture<String> future = bot.query.entity(
context.sender.profile.getIdAsString(),
"SelectedItem.components.minecraft:custom_data.message"
);
if (future == null) {
throw new CommandException(Component.text("There was an error while sending your mail"));
}
future.thenApplyAsync(output -> {
future.thenApply(output -> {
try {
final List<Component> children = output.children();
if (
!children.isEmpty() &&
children.get(0).children().isEmpty() &&
((TranslatableComponent) children.get(0)).key()
.equals("arguments.nbtpath.nothing_found")
) {
context.sendOutput(Component.text("Player has no `message` NBT tag in their selected item's minecraft:custom_data").color(NamedTextColor.RED));
return output;
if (output.isEmpty()) {
throw new CommandException(Component.translatable("commands.mail.sendselecteditem.error.no_item_nbt"));
}
final Component actualOutputComponent = ((TranslatableComponent) output).arguments().get(1).asComponent();
DatabasePlugin.EXECUTOR_SERVICE.execute(() -> {
try {
bot.mail.send(
new Mail(
sender.profile.getName(),
context.getString(true, true),
Instant.now().toEpochMilli(),
bot.getServerString(),
output
)
);
final String value = ComponentUtilities.stringify(actualOutputComponent);
if (!value.startsWith("\"") && !value.endsWith("\"") && !value.startsWith("'") && !value.endsWith("'")) {
context.sendOutput(Component.text("`message` NBT is not a string").color(NamedTextColor.RED));
return output;
}
bot.mail.send(
new Mail(
sender.profile.getName(),
context.getString(true, true),
Instant.now().toEpochMilli(),
bot.host + ":" + bot.port,
value.substring(1).substring(0, value.length() - 2)
)
);
} catch (CommandException e) {
context.sendOutput(e.message.color(NamedTextColor.RED));
return output;
context.sendOutput(Component.translatable("commands.mail.sent", bot.colorPalette.defaultColor));
} catch (final CommandException e) {
context.sendOutput(e.message.colorIfAbsent(NamedTextColor.RED));
}
});
} catch (final CommandException e) {
context.sendOutput(e.message.colorIfAbsent(NamedTextColor.RED));
return null;
}
context.sendOutput(
Component.text("Mail sent!").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
);
return output;
});
}
case "read" -> {
context.checkOverloadArgs(1);
// TODO: use less for loops?
DatabasePlugin.EXECUTOR_SERVICE.execute(() -> {
final List<Mail> mails = bot.mail.list();
int senderMailSize = 0;
for (JsonElement mailElement : MailPlugin.mails) {
final Mail mail = gson.fromJson(mailElement, Mail.class);
int senderMailSize = 0;
for (final Mail mail : mails) {
if (!mail.sentTo().equals(sender.profile.getName())) continue;
senderMailSize++;
}
if (!mail.sentTo.equals(sender.profile.getName())) continue;
senderMailSize++;
}
if (senderMailSize == 0) {
context.sendOutput(Component.translatable("commands.mail.read.no_new_mails", NamedTextColor.RED));
return;
}
if (senderMailSize == 0) {
throw new CommandException(Component.text("You have no new mails"));
}
final int tempFinalSenderMailSize = senderMailSize;
final List<Component> mailsComponent = new ArrayList<>();
final List<Component> mailsComponent = new ArrayList<>();
int count = 1;
for (final Mail mail : mails) {
if (!mail.sentTo().equals(sender.profile.getName())) continue;
int i = 1;
for (JsonElement mailElement : MailPlugin.mails) {
final Mail mail = gson.fromJson(mailElement, Mail.class);
final String formattedTime = TimeUtilities.formatTime(
mail.timeSent(),
"MMMM d, yyyy, hh:mm:ss a Z",
ZoneId.of("UTC")
);
if (!mail.sentTo.equals(sender.profile.getName())) continue;
mailsComponent.add(
Component.translatable(
"commands.mail.read.mail_contents",
NamedTextColor.GREEN,
Component.text(count, bot.colorPalette.number),
Component.text("-", NamedTextColor.DARK_GRAY),
final Instant instant = Instant.ofEpochMilli(mail.timeSent);
final ZoneId zoneId = ZoneId.systemDefault();
final OffsetDateTime localDateTime = OffsetDateTime.ofInstant(instant, zoneId);
Component.text(mail.sentBy(), bot.colorPalette.username),
Component
.translatable("commands.mail.read.hover_more_info", NamedTextColor.GREEN)
.hoverEvent(
HoverEvent.showText(
Component.translatable(
"commands.mail.read.hover_info",
NamedTextColor.GREEN,
Component.text(formattedTime, bot.colorPalette.string),
Component.text(mail.server(), bot.colorPalette.string)
)
)
),
Component.text(mail.contents(), NamedTextColor.WHITE)
)
);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy, hh:mm:ss a Z");
final String formattedTime = localDateTime.format(formatter);
count++;
}
mailsComponent.add(
Component.translatable(
"""
%s %s Sent by: %s %s
Contents:
%s""",
Component.text(i).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
Component.text("-").color(NamedTextColor.DARK_GRAY),
final Component component = Component.empty()
.append(Component.translatable("commands.mail.read.mails_text", NamedTextColor.GREEN))
.append(Component.text("(", NamedTextColor.DARK_GRAY))
.append(Component.text(tempFinalSenderMailSize, NamedTextColor.GRAY))
.append(Component.text(")", NamedTextColor.DARK_GRAY))
.append(Component.newline())
.append(Component.join(JoinConfiguration.newlines(), mailsComponent));
Component.text(mail.sentBy).color(ColorUtilities.getColorByString(bot.config.colorPalette.username)),
Component
.text("[Hover here for more info]")
.color(NamedTextColor.GREEN)
.hoverEvent(
HoverEvent.showText(
Component.translatable(
"""
Time sent: %s
Server: %s""",
Component.text(formattedTime).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component.text(mail.server).color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
).color(NamedTextColor.GREEN)
)
),
Component.text(mail.contents).color(NamedTextColor.WHITE)
).color(NamedTextColor.GREEN)
);
final Component renderedComponent = I18nUtilities.render(component);
i++;
}
if (context.inGame) {
bot.chat.tellraw(
renderedComponent,
context.sender.profile.getId()
);
} else {
context.sendOutput(renderedComponent);
}
final Component component = Component.empty()
.append(Component.text("Mails ").color(NamedTextColor.GREEN))
.append(Component.text("(").color(NamedTextColor.DARK_GRAY))
.append(Component.text(senderMailSize).color(NamedTextColor.GRAY))
.append(Component.text(")").color(NamedTextColor.DARK_GRAY))
.append(Component.newline())
.append(Component.join(JoinConfiguration.newlines(), mailsComponent));
if (context.inGame) {
bot.chat.tellraw(
component,
context.sender.profile.getId()
);
} else {
context.sendOutput(component);
}
for (JsonElement mailElement : MailPlugin.mails.deepCopy()) {
final Mail mail = gson.fromJson(mailElement, Mail.class);
if (mail.sentTo.equals(sender.profile.getName())) MailPlugin.mails.remove(mailElement);
}
PersistentDataUtilities.put("mails", MailPlugin.mails);
bot.mail.clear(sender.profile.getName());
});
}
default -> context.sendOutput(Component.text("Invalid action").color(NamedTextColor.RED));
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
return null;

View file

@ -1,46 +1,56 @@
package me.chayapak1.chomens_bot.commands;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.command.contexts.ConsoleCommandContext;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.data.listener.Listener;
import me.chayapak1.chomens_bot.plugins.MusicPlayerPlugin;
import me.chayapak1.chomens_bot.song.Instrument;
import me.chayapak1.chomens_bot.song.Loop;
import me.chayapak1.chomens_bot.song.Note;
import me.chayapak1.chomens_bot.song.Song;
import me.chayapak1.chomens_bot.util.*;
import me.chayapak1.chomens_bot.util.Ascii85;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import me.chayapak1.chomens_bot.util.PathUtilities;
import me.chayapak1.chomens_bot.util.TimestampUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.cloudburstmc.math.vector.Vector3d;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.file.*;
import java.util.ArrayList;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static me.chayapak1.chomens_bot.util.StringUtilities.isNotNullAndNotBlank;
public class MusicCommand extends Command {
private Path root;
public class MusicCommand extends Command implements Listener {
private static final Path ROOT = MusicPlayerPlugin.SONG_DIR;
private static final AtomicInteger commandsPerSecond = new AtomicInteger();
public MusicCommand () {
super(
"music",
"Plays music",
new String[] {
"play <song|URL>",
"playitem",
"stop",
"loop <current|all|off>",
"list [directory]",
@ -50,6 +60,7 @@ public class MusicCommand extends Command {
"goto <timestamp>",
"pitch <pitch>",
"speed <speed>",
"volume <volume modifier>",
"amplify <amplification>",
"noteinstrument <instrument>",
"pause",
@ -58,187 +69,164 @@ public class MusicCommand extends Command {
},
new String[] { "song" },
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[] { ChatPacketType.DISGUISED }
);
Main.EXECUTOR.scheduleAtFixedRate(() -> commandsPerSecond.set(0), 0, 1, TimeUnit.SECONDS);
}
@Override
public Component execute(CommandContext context) throws CommandException {
if (context.bot.music.locked) throw new CommandException(Component.text("Managing music is currently locked"));
public Component execute (final CommandContext context) throws CommandException {
// denis check
if (commandsPerSecond.get() > 3) return null;
else commandsPerSecond.getAndIncrement();
final String action = context.getString(false, true, true);
if (context.bot.music.locked && !(context instanceof ConsoleCommandContext))
throw new CommandException(Component.translatable("commands.music.error.locked"));
final String action = context.getAction();
root = MusicPlayerPlugin.SONG_DIR;
return switch (action) {
case "play", "playurl", "playnbs", "playnbsurl" -> play(context);
case "playfromitem", "playitem" -> playFromItem(context);
case "playsongplayer" -> playSongPlayer(context);
case "playfromitem", "playitem", "playsongplayer" -> playFromItem(context);
case "stop" -> stop(context);
case "loop" -> loop(context);
case "list" -> list(context);
case "skip" -> skip(context);
case "nowplaying" -> nowplaying(context);
case "nowplaying" -> nowPlaying(context);
case "queue" -> queue(context);
case "goto" -> goTo(context);
case "pitch" -> pitch(context);
case "speed" -> speed(context);
case "volume" -> volume(context);
case "amplify" -> amplify(context);
case "noteinstrument" -> noteInstrument(context);
case "pause", "resume" -> pause(context);
case "info" -> info(context);
case "testsong" -> testSong(context);
default -> Component.text("Invalid action").color(NamedTextColor.RED);
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
};
}
public Component play (CommandContext context) throws CommandException {
public Component play (final CommandContext context) throws CommandException {
final MusicPlayerPlugin player = context.bot.music;
if (player.loaderThread != null) throw new CommandException(Component.text("Already loading a song"));
if (player.loaderThread != null) throw new CommandException(Component.translatable("commands.music.play.error.already_loading"));
final String stringPath = context.getString(true, true);
final Path path;
String _path;
Path path;
try {
_path = context.getString(true, true);
path = Path.of(ROOT.toString(), stringPath);
// if (_path.isBlank()) throw new CommandException(Component.text("No song specified"));
path = Path.of(root.toString(), _path);
if (path.toString().contains("http")) player.loadSong(new URI(_path).toURL(), context.sender);
if (path.toString().contains("http")) player.loadSong(new URI(stringPath).toURL(), context);
else {
// among us protection!!!11
if (!path.normalize().startsWith(root.toString())) throw new CommandException(Component.text("no"));
if (!path.normalize().startsWith(ROOT.toString())) throw new CommandException(Component.text("no"));
// ignore my ohio code for autocomplete
final String separator = FileSystems.getDefault().getSeparator();
if (_path.contains(separator) && !_path.isEmpty()) {
final String[] pathSplitted = _path.split(separator);
if (stringPath.contains(separator) && !stringPath.isEmpty()) {
final String[] splitPath = stringPath.split(separator);
final List<String> pathSplittedClone = new ArrayList<>(Arrays.stream(pathSplitted.clone()).toList());
pathSplittedClone.remove(pathSplittedClone.size() - 1);
final List<String> splitPathClone = new ObjectArrayList<>(Arrays.stream(splitPath).toList());
splitPathClone.removeLast();
final Path realPath = Path.of(root.toString(), String.join(separator, pathSplittedClone));
final Path realPath = Path.of(ROOT.toString(), String.join(separator, splitPathClone));
try (DirectoryStream<Path> stream = Files.newDirectoryStream(realPath)) {
final List<Path> songsPaths = new ArrayList<>();
for (Path eachPath : stream) songsPaths.add(eachPath);
try (final DirectoryStream<Path> stream = Files.newDirectoryStream(realPath)) {
final List<Path> songsPaths = new ObjectArrayList<>();
for (final Path eachPath : stream) songsPaths.add(eachPath);
PathUtilities.sort(songsPaths);
final List<String> songs = new ArrayList<>();
for (Path eachPath : songsPaths) songs.add(eachPath.getFileName().toString());
final List<String> songs = new ObjectArrayList<>();
for (final Path eachPath : songsPaths) songs.add(eachPath.getFileName().toString());
final String lowerCaseFile = pathSplitted[pathSplitted.length - 1].toLowerCase();
final String lowerCaseFile = splitPath[splitPath.length - 1].toLowerCase();
final String[] matchedArray = songs.stream()
.filter(song -> song.equalsIgnoreCase(lowerCaseFile) || song.toLowerCase().contains(lowerCaseFile))
.toArray(String[]::new);
if (matchedArray.length == 0) throw new CommandException(Component.text("Song not found"));
if (matchedArray.length == 0) throw new CommandException(Component.translatable("commands.music.error.song_not_found"));
final String file = matchedArray[0];
player.loadSong(Path.of(realPath.toString(), file), context.sender);
} catch (CommandException e) {
throw e;
} catch (NoSuchFileException e) {
throw new CommandException(Component.text("Directory does not exist"));
} catch (Exception e) {
e.printStackTrace();
player.loadSong(Path.of(realPath.toString(), file), context);
} catch (final NoSuchFileException e) {
throw new CommandException(Component.translatable("commands.music.error.no_directory"));
}
} else {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
final List<Path> songsPaths = new ArrayList<>();
for (Path eachPath : stream) songsPaths.add(eachPath);
try (final DirectoryStream<Path> stream = Files.newDirectoryStream(ROOT)) {
final List<Path> songsPaths = new ObjectArrayList<>();
for (final Path eachPath : stream) songsPaths.add(eachPath);
PathUtilities.sort(songsPaths);
final List<String> songs = new ArrayList<>();
for (Path eachPath : songsPaths) songs.add(eachPath.getFileName().toString());
final List<String> songs = new ObjectArrayList<>();
for (final Path eachPath : songsPaths) songs.add(eachPath.getFileName().toString());
final String[] matchedArray = songs.stream()
.filter(song -> song.equalsIgnoreCase(_path) || song.toLowerCase().contains(_path.toLowerCase()))
.filter(song -> song.equalsIgnoreCase(stringPath) || song.toLowerCase().contains(stringPath.toLowerCase()))
.toArray(String[]::new);
if (matchedArray.length == 0) throw new CommandException(Component.text("Song not found"));
if (matchedArray.length == 0) throw new CommandException(Component.translatable("commands.music.error.song_not_found"));
final String file = matchedArray[0];
player.loadSong(Path.of(root.toString(), file), context.sender);
} catch (CommandException e) {
throw e;
} catch (NoSuchFileException e) {
player.loadSong(Path.of(ROOT.toString(), file), context);
} catch (final NoSuchFileException e) {
throw new CommandException(Component.text("this will never happen ok??"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (MalformedURLException e) {
throw new CommandException(Component.text("Invalid URL"));
} catch (IndexOutOfBoundsException e) {
throw new CommandException(Component.text("Song not found"));
} catch (CommandException e) {
} catch (final MalformedURLException e) {
throw new CommandException(Component.translatable("commands.music.error.invalid_url"));
} catch (final IndexOutOfBoundsException e) {
throw new CommandException(Component.translatable("commands.music.error.song_not_found"));
} catch (final CommandException e) {
throw e;
} catch (Exception e) {
} catch (final Exception e) {
throw new CommandException(Component.text(e.toString()));
}
return null;
}
public Component playFromItem (CommandContext context) throws CommandException {
public Component playFromItem (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
// mail command lol
final Bot bot = context.bot;
final CompletableFuture<Component> future = bot.core.runTracked(
"minecraft:data get entity " +
UUIDUtilities.selector(context.sender.profile.getId()) +
" SelectedItem.tag.SongItemData.SongData"
final CompletableFuture<String> future = bot.query.entity(
context.sender.profile.getIdAsString(),
"SelectedItem.components.minecraft:custom_data.SongItemData.SongData"
);
if (future == null) {
throw new CommandException(Component.text("There was an error while getting your data"));
}
future.thenApplyAsync(output -> {
final List<Component> children = output.children();
if (
!children.isEmpty() &&
children.get(0).children().isEmpty() &&
((TranslatableComponent) children.get(0)).key()
.equals("arguments.nbtpath.nothing_found")
) {
context.sendOutput(Component.text("Player has no `SongItemData.SongData` NBT tag in their selected item").color(NamedTextColor.RED));
return output;
}
final Component actualOutputComponent = ((TranslatableComponent) output).arguments().get(1).asComponent();
final String value = ComponentUtilities.stringify(actualOutputComponent);
if (!value.startsWith("\"") && !value.endsWith("\"") && !value.startsWith("'") && !value.endsWith("'")) {
context.sendOutput(Component.text("`data` NBT is not a string").color(NamedTextColor.RED));
return output;
future.thenApply(output -> {
if (output.isEmpty()) {
context.sendOutput(Component.translatable("commands.music.playitem.error.no_item_nbt", NamedTextColor.RED));
return null;
}
try {
bot.music.loadSong(
Base64.getDecoder().decode(
value
.substring(1)
.substring(0, value.length() - 2)
),
context.sender
Base64.getDecoder().decode(output),
context
);
} catch (IllegalArgumentException e) {
context.sendOutput(Component.text("Invalid base64 in the selected item").color(NamedTextColor.RED));
} catch (final IllegalArgumentException e) {
try {
bot.music.loadSong(
Ascii85.decode(output),
context
);
} catch (final IllegalArgumentException e2) {
context.sendOutput(Component.translatable("commands.music.playitem.invalid_data", NamedTextColor.RED));
}
}
return output;
@ -247,103 +235,57 @@ public class MusicCommand extends Command {
return null;
}
public Component playSongPlayer (CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
// dupe codes ??
final Bot bot = context.bot;
final CompletableFuture<Component> future = bot.core.runTracked(
"minecraft:data get entity " +
UUIDUtilities.selector(context.sender.profile.getId()) +
" SelectedItem.components.minecraft:custom_data.SongItemData.SongData"
);
if (future == null) {
throw new CommandException(Component.text("There was an error while getting your data"));
}
future.thenApplyAsync(output -> {
final List<Component> children = output.children();
if (
!children.isEmpty() &&
!children.get(0).children().isEmpty() &&
((TranslatableComponent) children.get(0).children().get(0))
.key()
.equals("arguments.nbtpath.nothing_found")
) {
context.sendOutput(Component.text("Player has no SongItemData -> SongData NBT tag in their selected item's minecraft:custom_data").color(NamedTextColor.RED));
return output;
}
final String value = ComponentUtilities.stringify(((TranslatableComponent) children.get(0)).arguments().get(1).asComponent());
if (!value.startsWith("\"") && !value.endsWith("\"") && !value.startsWith("'") && !value.endsWith("'")) {
context.sendOutput(Component.text("NBT is not a string").color(NamedTextColor.RED));
return output;
}
try {
bot.music.loadSong(
Base64.getDecoder().decode(
value
.substring(1)
.substring(0, value.length() - 2)
),
context.sender
);
} catch (IllegalArgumentException e) {
context.sendOutput(Component.text("Invalid song data in the selected item").color(NamedTextColor.RED));
}
return output;
});
return null;
}
public Component stop (CommandContext context) throws CommandException {
public Component stop (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
bot.music.stopPlaying();
bot.music.songQueue.clear();
bot.music.loaderThread = null;
return Component.text("Cleared the song queue").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.music.stop", bot.colorPalette.defaultColor);
}
public Component loop (CommandContext context) throws CommandException {
public Component loop (final CommandContext context) throws CommandException {
context.checkOverloadArgs(2);
final Bot bot = context.bot;
final Loop loop = context.getEnum(Loop.class);
final Loop loop = context.getEnum(true, Loop.class);
bot.music.loop = loop;
switch (loop) {
case OFF -> {
return Component.empty()
.append(Component.text("Looping is now "))
.append(Component.text("disabled").color(NamedTextColor.RED))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.music.loop.off",
bot.colorPalette.defaultColor,
Component.translatable("commands.music.loop.off.disabled", NamedTextColor.RED)
);
}
case CURRENT -> {
return Component.empty()
.append(Component.text("Now looping "))
.append(Component.text(bot.music.currentSong.name).color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
if (bot.music.currentSong != null) {
return Component.translatable(
"commands.music.loop.current.with_song",
bot.colorPalette.defaultColor,
Component.text(bot.music.currentSong.name, bot.colorPalette.secondary)
);
} else {
return Component.translatable(
"commands.music.loop.current.without_song",
bot.colorPalette.defaultColor
);
}
}
case ALL -> {
return Component.text("Now looping every song").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.music.loop.all", bot.colorPalette.defaultColor);
}
default -> throw new CommandException(Component.text("Invalid action"));
}
return null;
}
public Component list (CommandContext context) throws CommandException {
public Component list (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String prefix = context.prefix;
@ -351,31 +293,31 @@ public class MusicCommand extends Command {
final String stringPathIfExists = context.getString(true, false);
final Path path = Path.of(
root.toString(),
ROOT.toString(),
stringPathIfExists
);
if (!path.normalize().startsWith(root.toString())) throw new CommandException(Component.text("no"));
if (!path.normalize().startsWith(ROOT.toString())) throw new CommandException(Component.text("no"));
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
final List<Path> paths = new ArrayList<>();
for (Path eachPath : stream) paths.add(eachPath);
try (final DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
final List<Path> paths = new ObjectArrayList<>();
for (final Path eachPath : stream) paths.add(eachPath);
PathUtilities.sort(paths);
final List<Component> fullList = new ArrayList<>();
final List<Component> fullList = new ObjectArrayList<>();
int i = 0;
for (Path eachPath : paths) {
for (final Path eachPath : paths) {
final boolean isDirectory = Files.isDirectory(eachPath);
Path location;
try {
location = path;
} catch (IllegalArgumentException e) {
} catch (final IllegalArgumentException e) {
location = Paths.get(""); // wtf mabe
}
final String joinedPath = location.equals(root) ?
final String joinedPath = location.equals(ROOT) ?
eachPath.getFileName().toString() :
Paths.get(
location.getFileName().toString(),
@ -384,7 +326,7 @@ public class MusicCommand extends Command {
fullList.add(
Component
.text(eachPath.getFileName().toString(), (i++ & 1) == 0 ? ColorUtilities.getColorByString(bot.config.colorPalette.primary) : ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
.text(eachPath.getFileName().toString(), (i++ & 1) == 0 ? bot.colorPalette.primary : bot.colorPalette.secondary)
.clickEvent(
ClickEvent.suggestCommand(
prefix +
@ -402,7 +344,7 @@ public class MusicCommand extends Command {
while (index <= fullList.size()) {
// we MUST make a new copy of the list else everything will fard..,.
List<Component> list = new ArrayList<>(fullList).subList(index, Math.min(index + eachSize, fullList.size()));
final List<Component> list = new ObjectArrayList<>(fullList).subList(index, Math.min(index + eachSize, fullList.size()));
final Component component = Component.join(JoinConfiguration.separator(Component.space()), list);
context.sendOutput(component);
@ -410,68 +352,73 @@ public class MusicCommand extends Command {
index += eachSize;
list.clear();
}
} catch (NoSuchFileException e) {
throw new CommandException(Component.text("Directory doesn't exist"));
} catch (IOException e) {
e.printStackTrace();
} catch (final IOException e) {
throw new CommandException(Component.translatable("commands.music.error.no_directory"));
}
return null;
}
public Component skip (CommandContext context) throws CommandException {
public Component skip (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
final MusicPlayerPlugin music = bot.music;
if (music.currentSong == null) throw new CommandException(Component.text("No song is currently playing"));
if (music.currentSong == null) throw new CommandException(Component.translatable("commands.music.error.not_playing"));
context.sendOutput(
Component.empty()
.append(Component.text("Skipping "))
.append(Component.text(music.currentSong.name).color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
);
final String name = music.currentSong.name;
music.skip();
return null;
return Component.translatable(
"commands.music.skip",
bot.colorPalette.defaultColor,
Component.text(name, bot.colorPalette.secondary)
);
}
public Component nowplaying (CommandContext context) throws CommandException {
public Component nowPlaying (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
final Song song = bot.music.currentSong;
if (song == null) throw new CommandException(Component.text("No song is currently playing"));
if (song == null) throw new CommandException(Component.translatable("commands.music.error.not_playing"));
return Component.empty()
.append(Component.text("Now playing "))
.append(Component.text(song.name).color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.music.nowplaying",
bot.colorPalette.defaultColor,
Component.text(song.name, bot.colorPalette.secondary)
);
}
public Component queue (CommandContext context) throws CommandException {
public Component queue (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
final List<Song> queue = bot.music.songQueue;
final List<Component> queueWithNames = new ArrayList<>();
final List<Component> queueWithNames = new ObjectArrayList<>();
int i = 0;
for (Song song : queue) {
for (final Song song : queue) {
queueWithNames.add(
Component.text(song.name).color((i++ & 1) == 0 ? ColorUtilities.getColorByString(bot.config.colorPalette.primary) : ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
Component.text(
song.name,
(i++ & 1) == 0
? bot.colorPalette.primary
: bot.colorPalette.secondary
)
);
}
return Component.empty()
.append(Component.text("Queue: ").color(NamedTextColor.GREEN))
.append(Component.join(JoinConfiguration.separator(Component.text(", ")), queueWithNames));
return Component.translatable(
"commands.music.queue",
NamedTextColor.GREEN,
Component.join(JoinConfiguration.commas(true), queueWithNames)
);
}
// lazy fix for java using "goto" as keyword real
public Component goTo (CommandContext context) throws CommandException {
public Component goTo (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final Song currentSong = bot.music.currentSong;
@ -479,132 +426,167 @@ public class MusicCommand extends Command {
final long timestamp = TimestampUtilities.parseTimestamp(input);
if (currentSong == null) throw new CommandException(Component.text("No song is currently playing"));
if (currentSong == null) throw new CommandException(Component.translatable("commands.music.error.not_playing"));
if (timestamp < 0 || timestamp > currentSong.length) throw new CommandException(Component.text("Invalid timestamp"));
if (timestamp < 0 || timestamp > currentSong.length / bot.music.speed)
throw new CommandException(Component.translatable("commands.music.goto.error.invalid_timestamp"));
currentSong.setTime(timestamp);
currentSong.setTime(timestamp / bot.music.speed);
return Component
.text("Set the time to ")
.append(Component.text(input).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.music.goto",
bot.colorPalette.defaultColor,
Component.text(input, bot.colorPalette.number)
);
}
public Component pitch (CommandContext context) throws CommandException {
public Component pitch (final CommandContext context) throws CommandException {
context.checkOverloadArgs(2);
final Bot bot = context.bot;
final float pitch = context.getFloat(true);
final float pitch = context.getFloat(true, false);
bot.music.pitch = pitch;
return Component.empty()
.append(Component.text("Set the pitch to "))
.append(Component.text(pitch).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.music.pitch",
bot.colorPalette.defaultColor,
Component.text(pitch, bot.colorPalette.number)
);
}
public Component speed (CommandContext context) throws CommandException {
public Component speed (final CommandContext context) throws CommandException {
context.checkOverloadArgs(2);
final Bot bot = context.bot;
final Song currentSong = bot.music.currentSong;
final float speed = context.getFloat(true);
final double speed = context.getDouble(true, false);
if (speed > 5) throw new CommandException(Component.text("Too fast!"));
if (speed > 5) throw new CommandException(Component.translatable("commands.music.speed.error.too_fast"));
else if (speed < 0) throw new CommandException(Component.translatable("commands.music.speed.error.negative"));
long oldTime = -1;
double oldTime = -1;
if (currentSong != null) oldTime = currentSong.time;
if (currentSong != null) oldTime = currentSong.time / speed;
bot.music.speed = speed;
if (currentSong != null) currentSong.setTime(oldTime);
return Component.empty()
.append(Component.text("Set the speed to "))
.append(Component.text(speed).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.music.speed",
bot.colorPalette.defaultColor,
Component.text(speed, bot.colorPalette.number)
);
}
public Component amplify(CommandContext context) throws CommandException {
public Component volume (final CommandContext context) throws CommandException {
context.checkOverloadArgs(2);
final Bot bot = context.bot;
final float volume = context.getFloat(true, false);
bot.music.volume = volume;
return Component.translatable(
"commands.music.volume",
bot.colorPalette.defaultColor,
Component.text(volume, bot.colorPalette.number)
);
}
public Component amplify (final CommandContext context) throws CommandException {
context.checkOverloadArgs(2);
final Bot bot = context.bot;
final int amplify = context.getInteger(true);
if (amplify > 8) throw new CommandException(Component.text("Too big value"));
if (amplify > 8) throw new CommandException(Component.translatable("commands.music.amplify.error.too_big_value"));
else if (amplify < 0) throw new CommandException(Component.translatable("commands.music.amplify.error.negative"));
bot.music.amplify = amplify;
return Component.empty()
.append(Component.text("Set the amplification to "))
.append(Component.text(amplify).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.music.amplify",
bot.colorPalette.defaultColor,
Component.text(amplify, bot.colorPalette.number)
);
}
public Component noteInstrument (CommandContext context) throws CommandException {
public Component noteInstrument (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String instrument = context.getString(true, true);
bot.music.instrument = instrument;
if (!instrument.equals("off")) {
return Component.empty()
.append(Component.text("Set the instrument for every note to "))
.append(Component.text(instrument).color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
if (instrument.equalsIgnoreCase("off")) {
return Component.translatable("commands.music.noteinstrument.off", bot.colorPalette.defaultColor);
} else {
return Component.text("Every notes are now using its instrument").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.music.noteinstrument.set",
bot.colorPalette.defaultColor,
Component.text(instrument)
);
}
}
public Component pause (CommandContext context) throws CommandException {
public Component pause (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
final Song currentSong = bot.music.currentSong;
if (currentSong == null) throw new CommandException(Component.text("No song is currently playing"));
if (currentSong == null) throw new CommandException(Component.translatable("commands.music.error.not_playing"));
if (currentSong.paused) {
currentSong.play();
return Component.text("Resumed the current song").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.music.resumed", bot.colorPalette.defaultColor);
} else {
currentSong.pause();
return Component.text("Paused the current song").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.music.paused", bot.colorPalette.defaultColor);
}
}
public Component info (CommandContext context) throws CommandException {
public Component info (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
final Song currentSong = bot.music.currentSong;
if (currentSong == null) throw new CommandException(Component.text("No song is currently playing"));
if (currentSong == null) throw new CommandException(Component.translatable("commands.music.error.not_playing"));
final List<Component> components = new ArrayList<>();
final List<Component> components = new ObjectArrayList<>();
final TextColor keyColor = ColorUtilities.getColorByString(bot.config.colorPalette.secondary);
final TextColor valueColor = ColorUtilities.getColorByString(bot.config.colorPalette.string);
final TextColor keyColor = bot.colorPalette.secondary;
final TextColor valueColor = bot.colorPalette.string;
if (isNotNullAndNotBlank(currentSong.name)) components.add(Component.translatable("Title/Filename: %s", Component.text(currentSong.name).color(valueColor)).color(keyColor));
if (isNotNullAndNotBlank(currentSong.requester)) components.add(Component.translatable("Requester: %s", Component.text(currentSong.requester).color(valueColor)).color(keyColor));
if (isNotNullAndNotBlank(currentSong.songAuthor)) components.add(Component.translatable("Author: %s", Component.text(currentSong.songAuthor).color(valueColor)).color(keyColor));
if (isNotNullAndNotBlank(currentSong.songOriginalAuthor)) components.add(Component.translatable("Original author: %s", Component.text(currentSong.songOriginalAuthor).color(valueColor)).color(keyColor));
if (isNotNullAndNotBlank(currentSong.tracks)) components.add(Component.translatable("Tracks: %s", Component.text(currentSong.tracks).color(valueColor)).color(keyColor));
if (isNotNullAndNotBlank(currentSong.songDescription)) components.add(Component.translatable("Description: %s", Component.text(currentSong.songDescription).color(valueColor)).color(keyColor));
final DecimalFormat formatter = new DecimalFormat("#,###");
final String formattedNotesCount = formatter.format(currentSong.size());
if (isNotNullAndNotBlank(currentSong.name))
components.add(Component.translatable("commands.music.info.title", keyColor, Component.text(currentSong.name, valueColor)));
if (currentSong.context != null && isNotNullAndNotBlank(currentSong.context.sender.profile.getName()))
components.add(Component.translatable("commands.music.info.requester", keyColor, Component.text(currentSong.context.sender.profile.getName(), valueColor)));
if (isNotNullAndNotBlank(currentSong.songAuthor))
components.add(Component.translatable("commands.music.info.author", keyColor, Component.text(currentSong.songAuthor, valueColor)));
if (isNotNullAndNotBlank(currentSong.songOriginalAuthor))
components.add(Component.translatable("commands.music.info.original_author", keyColor, Component.text(currentSong.songOriginalAuthor, valueColor)));
if (isNotNullAndNotBlank(currentSong.tracks))
components.add(Component.translatable("commands.music.info.tracks", keyColor, Component.text(currentSong.tracks, valueColor)));
components.add(Component.translatable("commands.music.info.notes", keyColor, Component.text(formattedNotesCount, valueColor)));
if (isNotNullAndNotBlank(currentSong.songDescription))
components.add(Component.translatable("commands.music.info.description", keyColor, Component.text(currentSong.songDescription, valueColor)));
return Component.join(JoinConfiguration.newlines(), components);
}
public Component testSong (CommandContext context) throws CommandException {
public Component testSong (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
@ -612,14 +594,16 @@ public class MusicCommand extends Command {
final Song song = new Song(
"test_song",
bot,
"Test Song",
I18nUtilities.get("commands.music.testsong.title"),
"chayapak",
"hhhzzzsss",
"SongPlayer's test song ported to ChomeNS Bot",
I18nUtilities.get("commands.music.testsong.description"),
null,
false
);
song.context = context;
int instrumentId = 0;
int j = 0;
for (int i = 0; i < 400; i++) {
@ -632,8 +616,8 @@ public class MusicCommand extends Command {
j,
1,
i * 50,
-1,
100
Vector3d.ZERO,
false
)
);
@ -648,6 +632,6 @@ public class MusicCommand extends Command {
bot.music.songQueue.add(song);
return Component.text("Test song has been added to the song queue").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.music.testsong.output", bot.colorPalette.defaultColor);
}
}

View file

@ -0,0 +1,68 @@
package me.chayapak1.chomens_bot.commands;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.command.contexts.RemoteCommandContext;
import net.kyori.adventure.text.Component;
import java.util.ArrayList;
import java.util.List;
public class NetCommandCommand extends Command {
public NetCommandCommand () {
super(
"netcmd",
new String[] { "<servers separated by a comma> <command>" },
new String[] { "networkcommand", "irccommand", "remotecommand" },
TrustLevel.TRUSTED
);
}
@Override
public Component execute (final CommandContext context) throws CommandException {
final String rawServers = context.getString(false, true, true);
final List<Bot> allBots = context.bot.bots;
final List<Bot> bots;
if (rawServers.equals("all")) {
bots = allBots;
} else {
bots = new ArrayList<>();
final String[] servers = rawServers.split(",");
for (final Bot bot : allBots) {
for (final String server : servers) {
if (
server.isBlank()
|| !bot.getServerString(true)
.toLowerCase()
.trim()
.contains(server.toLowerCase())
) continue;
bots.add(bot);
}
}
}
if (bots.isEmpty()) throw new CommandException(Component.translatable("commands.netcmd.error.no_servers_found"));
final String command = context.getString(true, true);
for (final Bot bot : bots) {
if (!bot.loggedIn) continue;
final CommandContext remoteContext = new RemoteCommandContext(bot, context);
context.bot.commandHandler.executeCommand(command, remoteContext);
}
return null;
}
}

View file

@ -5,6 +5,9 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.util.ChatMessageUtilities;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
@ -16,44 +19,67 @@ public class NetMessageCommand extends Command {
public NetMessageCommand () {
super(
"netmsg",
"Broadcasts a message to all of the servers that the bot is connected",
new String[] { "<message>" },
new String[] { "networkmessage", "irc" },
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[]{ ChatPacketType.SYSTEM, ChatPacketType.DISGUISED }
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final List<Bot> bots = bot.bots;
final String hostAndPort = bot.host + ":" + bot.port;
final String originServerName = bot.getServerString();
final String originServerAddress = bot.getServerString(false);
Component serverNameComponent = Component
.text(originServerName)
.color(NamedTextColor.GRAY);
if (!bot.options.hidden) serverNameComponent = serverNameComponent
.clickEvent(ClickEvent.copyToClipboard(originServerAddress))
.hoverEvent(
HoverEvent.showText(
Component.empty()
.append(Component.text(originServerAddress, NamedTextColor.GRAY))
.append(Component.newline())
.append(Component.translatable("commands.netmsg.hover.copy_server_to_clipboard", NamedTextColor.GREEN))
)
);
final String rawMessage = context.getString(true, true);
final Component stylizedMessage = ChatMessageUtilities.applyChatMessageStyling(rawMessage);
final Component component = Component.translatable(
"[%s]%s%s%s %s",
Component
.text(hostAndPort)
NamedTextColor.DARK_GRAY,
serverNameComponent,
Component.space(),
context.sender.displayName == null ?
Component.text(context.sender.profile.getName(), NamedTextColor.GRAY) :
context.sender.displayName.color(NamedTextColor.GRAY),
Component.space(),
Component.empty()
.append(stylizedMessage)
.color(NamedTextColor.GRAY)
.clickEvent(ClickEvent.copyToClipboard(hostAndPort))
.clickEvent(ClickEvent.copyToClipboard(rawMessage))
.hoverEvent(
HoverEvent.showText(
Component.empty()
.append(Component.text(hostAndPort).color(NamedTextColor.GRAY))
.append(Component.newline())
.append(Component.text("Click here to copy the server host and port to your clipboard").color(NamedTextColor.GREEN))
Component.translatable(
"commands.generic.click_to_copy_message",
NamedTextColor.GREEN
)
)
),
Component.text(" "),
context.sender.displayName == null ? Component.text(context.sender.profile.getName()).color(NamedTextColor.GRAY) : context.sender.displayName.color(NamedTextColor.GRAY),
Component.text(" "),
Component.text(context.getString(true, true)).color(NamedTextColor.GRAY)
).color(NamedTextColor.DARK_GRAY);
)
);
for (Bot eachBot : bots) {
for (final Bot eachBot : bots) {
if (!eachBot.loggedIn) continue;
eachBot.chat.tellraw(component);
eachBot.chat.tellraw(I18nUtilities.render(component));
}
return null;

View file

@ -1,39 +0,0 @@
package me.chayapak1.chomens_bot.commands;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
public class PCrashCommand extends Command {
public PCrashCommand () {
super(
"pcrash",
"Crashes a player using particle",
new String[] { "<player>" },
new String[] { "particlecrash" },
TrustLevel.TRUSTED,
false
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
final Bot bot = context.bot;
final PlayerEntry player = bot.players.getEntry(context.getString(true, true));
if (player == null) throw new CommandException(Component.text("Invalid player name"));
bot.exploits.pcrash(player.profile.getId());
return Component.translatable(
"Attempting to crash %s",
Component.text(player.profile.getName()).color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
}

View file

@ -5,44 +5,79 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.command.contexts.PlayerCommandContext;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.MathUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
public class RandomTeleportCommand extends Command {
public RandomTeleportCommand () {
super(
"rtp",
"Randomly teleports you",
new String[] {},
new String[] { "randomteleport" },
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[] { ChatPacketType.DISGUISED }
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(0);
final Bot bot = context.bot;
final PlayerEntry sender = context.sender;
final int positionX = MathUtilities.between(-1_000_000, 1_000_000);
final int positionZ = MathUtilities.between(-1_000_000, 1_000_000);
final String stringPosition = positionX + " 100 " + positionZ; // is hardcoding the y to 100 a great idea?
final String position = String.format(
"%d %d %d",
MathUtilities.between(-1_000_000, 1_000_000),
100,
MathUtilities.between(-1_000_000, 1_000_000)
);
bot.core.run("essentials:teleport " + sender.profile.getIdAsString() + " " + stringPosition);
bot.core.run(
String.format(
"essentials:teleport %s %s",
sender.profile.getIdAsString(),
position
)
);
return Component.empty()
.append(Component.text("Teleporting "))
.append(Component.text(sender.profile.getName()).color(ColorUtilities.getColorByString(bot.config.colorPalette.username)))
.append(Component.text(" to "))
.append(Component.text(stringPosition).color(NamedTextColor.GREEN))
.append(Component.text("..."))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
if (context instanceof final PlayerCommandContext playerContext) {
playerContext.sendOutput(
Component.translatable(
"commands.rtp.teleporting_no_location",
bot.colorPalette.defaultColor,
Component.text(sender.profile.getName(), bot.colorPalette.username)
)
);
playerContext.sendOutput(
Component.translatable(
"commands.rtp.to_sender",
Style.style()
.color(NamedTextColor.GRAY)
.decorate(TextDecoration.ITALIC)
.build(),
Component.text(position, NamedTextColor.GREEN)
),
true
);
return null;
} else {
return Component.translatable(
"commands.rtp.teleporting_location",
bot.colorPalette.defaultColor,
Component.text(sender.profile.getName(), bot.colorPalette.username),
Component.text(position, NamedTextColor.GREEN)
);
}
}
}

View file

@ -11,16 +11,14 @@ public class RefillCoreCommand extends Command {
public RefillCoreCommand () {
super(
"refillcore",
"Refills and resets the bots command core",
new String[] {},
new String[] { "rc" },
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(0);
final Bot bot = context.bot;
@ -28,6 +26,9 @@ public class RefillCoreCommand extends Command {
bot.core.reset();
bot.core.refill();
return null;
return Component.translatable(
"commands.refillcore.output",
bot.colorPalette.defaultColor
);
}
}

View file

@ -0,0 +1,32 @@
package me.chayapak1.chomens_bot.commands;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.I18nUtilities;
import net.kyori.adventure.text.Component;
public class RestartCommand extends Command {
public RestartCommand () {
super(
"restart",
new String[] { "[reason]" },
new String[] {},
TrustLevel.OWNER
);
}
@Override
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String reason = context.getString(true, false);
Main.stop(12, reason.isEmpty() ? null : reason, I18nUtilities.get("info.restarting"));
return Component.translatable("commands.restart.output", bot.colorPalette.defaultColor);
}
}

View file

@ -5,29 +5,27 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import org.cloudburstmc.math.vector.Vector3i;
// this command uses english
public class ScreenshareCommand extends Command {
public ScreenshareCommand () {
super(
"screenshare",
"Shares my screen",
new String[] {
"<start> <x> <y> <z>",
"<stop>",
"<setres> <width> <height>",
"<setfps> <fps>"
"start <x> <y> <z>",
"stop",
"setres <width> <height>",
"setfps <fps>"
},
new String[] {},
TrustLevel.TRUSTED,
false
TrustLevel.TRUSTED
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String action = context.getString(false, true, true);
@ -45,7 +43,7 @@ public class ScreenshareCommand extends Command {
return Component
.text("Started screen sharing")
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
.color(bot.colorPalette.defaultColor);
}
case "stop" -> {
context.checkOverloadArgs(1);
@ -54,7 +52,7 @@ public class ScreenshareCommand extends Command {
return Component
.text("Stopped screen sharing")
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
.color(bot.colorPalette.defaultColor);
}
case "setres" -> {
context.checkOverloadArgs(3);
@ -69,8 +67,8 @@ public class ScreenshareCommand extends Command {
return Component
.text("Set the resolution to ")
.append(Component.text(width + "x" + height).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
.append(Component.text(width + "x" + height, bot.colorPalette.string))
.color(bot.colorPalette.defaultColor);
}
case "setfps" -> {
context.checkOverloadArgs(2);
@ -81,14 +79,12 @@ public class ScreenshareCommand extends Command {
return Component
.text("Set the FPS to ")
.append(Component.text(fps).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
default -> {
throw new CommandException(Component.text("Invalid action"));
.append(Component.text(fps, bot.colorPalette.number))
.color(bot.colorPalette.defaultColor);
}
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
} catch (NumberFormatException e) {
} catch (final NumberFormatException e) {
throw new CommandException(Component.text("Invalid integer"));
}
}

View file

@ -1,20 +1,20 @@
package me.chayapak1.chomens_bot.commands;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.plugins.PlayersPersistentDataPlugin;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.plugins.DatabasePlugin;
import me.chayapak1.chomens_bot.util.TimeUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.format.NamedTextColor;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
@ -22,66 +22,84 @@ public class SeenCommand extends Command {
public SeenCommand () {
super(
"seen",
"Shows the last seen of a player",
new String[] { "<player>" },
new String[] { "lastseen" },
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws Exception {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
if (Main.database == null)
throw new CommandException(Component.translatable("commands.generic.error.database_disabled"));
Main.database.checkOverloaded();
final String player = context.getString(true, true);
boolean online = false;
final List<Component> onlineComponents = new ArrayList<>();
for (Bot eachBot : bot.bots) {
for (final Bot eachBot : bot.bots) {
if (eachBot.players.getEntry(player) != null) {
online = true;
onlineComponents.add(
Component.empty()
.append(Component.text(player))
.append(Component.text(" is currently online on "))
.append(Component.text(eachBot.host + ":" + eachBot.port))
.color(NamedTextColor.RED)
Component.translatable(
"commands.seen.error.currently_online",
NamedTextColor.RED,
Component.text(player),
Component.text(eachBot.getServerString())
)
);
}
}
if (online) return Component.join(JoinConfiguration.newlines(), onlineComponents);
final JsonElement playerElement = PlayersPersistentDataPlugin.playersObject.get(player);
if (playerElement == null) throw new CommandException(Component.translatable(
"%s was never seen",
Component.text(player)
));
DatabasePlugin.EXECUTOR_SERVICE.execute(() -> {
try {
final JsonNode playerElement = bot.playersDatabase.getPlayerData(player);
if (playerElement == null) throw new CommandException(Component.translatable(
"commands.seen.error.never_seen",
Component.text(player)
));
final JsonObject lastSeen = playerElement.getAsJsonObject().get("lastSeen").getAsJsonObject();
final ObjectNode lastSeen = (ObjectNode) playerElement.get("lastSeen");
final JsonElement time = lastSeen.get("time");
if (lastSeen == null || lastSeen.isNull())
throw new CommandException(Component.translatable("commands.seen.error.no_last_seen_entry"));
if (time == null) throw new CommandException(Component.text("This player does not have the `lastSeen.time` entry in the database for some reason."));
final JsonNode time = lastSeen.get("time");
final Instant instant = Instant.ofEpochMilli(time.getAsLong());
final ZoneId zoneId = ZoneId.of("UTC"); // should i be doing this?
final OffsetDateTime localDateTime = OffsetDateTime.ofInstant(instant, zoneId);
if (time == null || time.isNull())
throw new CommandException(Component.translatable("commands.seen.error.no_time_entry"));
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE, MMMM d, yyyy, hh:mm:ss a Z");
final String formattedTime = localDateTime.format(formatter);
final String formattedTime = TimeUtilities.formatTime(
time.asLong(),
"EEEE, MMMM d, yyyy, hh:mm:ss a Z",
ZoneId.of("UTC")
);
final String server = lastSeen.get("server").getAsString();
final String server = lastSeen.get("server").asText();
return Component.translatable(
"%s was last seen at %s on %s",
Component.text(player).color(ColorUtilities.getColorByString(bot.config.colorPalette.username)),
Component.text(formattedTime).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component.text(server).color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
context.sendOutput(
Component.translatable(
"commands.seen.output",
bot.colorPalette.defaultColor,
Component.text(player, bot.colorPalette.username),
Component.text(formattedTime, bot.colorPalette.string),
Component.text(server, bot.colorPalette.string)
)
);
} catch (final CommandException e) {
context.sendOutput(e.message.color(NamedTextColor.RED));
}
});
return null;
}
}

View file

@ -7,45 +7,116 @@ import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import org.luaj.vm2.lib.jse.JsePlatform;
import party.iroiro.luajava.Lua;
import party.iroiro.luajava.lua54.Lua54;
import party.iroiro.luajava.value.LuaValue;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;
public class ServerEvalCommand extends Command {
public Lua lua;
public ServerEvalCommand () {
super(
"servereval",
"Evaluate codes using LuaJ",
new String[] { "<code>" },
new String[] { "reset", "<code>" },
new String[] {},
TrustLevel.OWNER,
false
TrustLevel.OWNER
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
bot.executorService.submit(() -> {
final String code = context.getString(true, true);
if (code.equalsIgnoreCase("reset")) {
if (lua != null) lua.close();
lua = null;
return Component.translatable("commands.servereval.reset", bot.colorPalette.defaultColor);
}
bot.executorService.execute(() -> {
try {
final Globals globals = JsePlatform.standardGlobals();
if (lua == null) lua = new Lua54();
globals.set("bot", CoerceJavaToLua.coerce(bot));
globals.set("class", CoerceJavaToLua.coerce(Class.class));
globals.set("context", CoerceJavaToLua.coerce(context));
lua.openLibraries();
LuaValue chunk = globals.load(context.getString(true, true));
lua.set("lua", lua);
lua.set("bot", bot);
lua.set("context", context);
lua.set("shell", new Shell());
final LuaValue output = chunk.call();
final LuaValue[] values = lua.eval(code);
context.sendOutput(Component.text(output.toString()).color(NamedTextColor.GREEN));
} catch (Exception e) {
context.sendOutput(Component.text(e.toString()).color(NamedTextColor.RED));
final StringBuilder output = new StringBuilder();
if (values.length != 1) {
output.append('[');
int i = 1;
for (final LuaValue value : values) {
output.append(getString(value));
if (i++ != values.length) output.append(", ");
}
output.append(']');
} else {
output.append(getString(values[0]));
}
context.sendOutput(Component.text(output.toString(), NamedTextColor.GREEN));
} catch (final Exception e) {
context.sendOutput(Component.text(e.toString(), NamedTextColor.RED));
}
});
return null;
}
private String getString (final LuaValue luaValue) {
final Object javaObject = luaValue.toJavaObject();
if (javaObject == null) return luaValue.toString();
else return javaObject.toString();
}
@SuppressWarnings("unused") // we actually use it in lua itself :)
public static final class Shell {
public String execute (final String[] command) throws Exception {
final ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command(command);
final Process process = processBuilder.start();
final StringBuilder output = new StringBuilder();
final BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
int character;
while ((character = stdoutReader.read()) != -1) {
final char[] chars = Character.toChars(character);
final String string = new String(chars);
output.append(string);
}
final BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((character = stderrReader.read()) != -1) {
final char[] chars = Character.toChars(character);
final String string = new String(chars);
output.append("[STDERR] ").append(string);
}
process.waitFor(10, TimeUnit.SECONDS);
return output.toString();
}
}
}

View file

@ -6,29 +6,26 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
public class StopCommand extends Command {
public StopCommand () {
super(
"stop",
"Gracefully stops the bot",
new String[] { "" },
new String[] { "[reason]" },
new String[] {},
TrustLevel.OWNER,
false
TrustLevel.OWNER
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
context.checkOverloadArgs(0);
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
Main.stop();
final String reason = context.getString(true, false);
return Component.text("Stopping").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
Main.stop(0, reason.isEmpty() ? null : reason);
return Component.translatable("commands.stop.output", bot.colorPalette.defaultColor);
}
}

View file

@ -5,7 +5,6 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -13,16 +12,14 @@ public class TPSBarCommand extends Command {
public TPSBarCommand () {
super(
"tpsbar",
"Shows the server's TPS using Minecraft Bossbar",
new String[] { "<on|off>" },
new String[] { "tps" },
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
context.checkOverloadArgs(1);
final Bot bot = context.bot;
@ -32,21 +29,21 @@ public class TPSBarCommand extends Command {
switch (action) {
case "on" -> {
bot.tps.on();
return Component.empty()
.append(Component.text("TPSBar is now "))
.append(Component.text("enabled").color(NamedTextColor.GREEN))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable(
"commands.tpsbar.output",
bot.colorPalette.defaultColor,
Component.translatable("commands.generic.enabled", NamedTextColor.GREEN)
);
}
case "off" -> {
bot.tps.off();
return Component.empty()
.append(Component.text("TPSBar is now "))
.append(Component.text("disabled").color(NamedTextColor.RED))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
default -> {
throw new CommandException(Component.text("Invalid action"));
return Component.translatable(
"commands.tpsbar.output",
bot.colorPalette.defaultColor,
Component.translatable("commands.generic.disabled", NamedTextColor.RED)
);
}
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
}
}

View file

@ -11,22 +11,21 @@ public class TestCommand extends Command {
public TestCommand () {
super(
"test",
"Tests if the bot is working",
new String[] { "[args]" },
new String[] {},
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
return Component.translatable(
"Hello, World! Username: %s, Sender UUID: %s, Prefix: %s, Args: %s",
"commands.test.output",
NamedTextColor.GREEN,
Component.text(context.sender.profile.getName()),
Component.text(context.sender.profile.getIdAsString()),
Component.text(context.prefix),
Component.text(context.getString(true, false))
).color(NamedTextColor.GREEN);
);
}
}

View file

@ -5,47 +5,45 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.util.TimeUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class TimeCommand extends Command {
public TimeCommand () {
super(
"time",
"Shows the date and time for the specified timezone",
new String[] { "<timezone>" },
new String[] { "dateandtime", "date" },
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
try {
final Bot bot = context.bot;
final String timezone = context.getString(true, true);
final ZoneId zoneId = ZoneId.of(timezone);
final ZonedDateTime zonedDateTime = ZonedDateTime.now(zoneId);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE, MMMM d, yyyy, hh:mm:ss a");
final String formattedTime = zonedDateTime.format(formatter);
final String formattedTime = TimeUtilities.formatTime(
Instant.now().toEpochMilli(),
"EEEE, MMMM d, yyyy, hh:mm:ss a",
ZoneId.of(timezone)
);
return Component.translatable(
"The current time for %s is: %s",
Component.text(timezone).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component.text(formattedTime).color(NamedTextColor.GREEN)
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
} catch (DateTimeException e) {
throw new CommandException(Component.text("Invalid timezone (case-sensitive)"));
"commands.time.output",
bot.colorPalette.defaultColor,
Component.text(timezone, bot.colorPalette.string),
Component.text(formattedTime, NamedTextColor.GREEN)
);
} catch (final DateTimeException e) {
throw new CommandException(Component.translatable("commands.time.error.invalid_timezone"));
}
}
}

View file

@ -1,36 +1,47 @@
package me.chayapak1.chomens_bot.commands;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.ObjectMapper;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.util.HttpUtilities;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
// stolen from vencord :D
// https://github.com/Vendicated/Vencord/blob/1fa6181f7e6526e1bf2e85260d9051000916c47c/src/plugins/translate/utils.ts
public class TranslateCommand extends Command {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final String TRANSLATE_URL = "https://translate-pa.googleapis.com/v1/translate?" +
"params.client=gtx&" +
"dataTypes=TRANSLATION&" +
"key=AIzaSyDLEeFI5OtFBwYBIoK_jj5m32rZK5CkCXA&" + // some google API key :eyes:
"query.sourceLanguage=%s&" +
"query.targetLanguage=%s&" +
"query.text=%s";
public TranslateCommand () {
super(
"translate",
"Translates a message using Google Translate",
new String[] { "<fromLanguage> <toLanguage> <message>" },
new String[] { "<from> <to> <message>" },
new String[] {},
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[] { ChatPacketType.SYSTEM, ChatPacketType.DISGUISED }
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String from = context.getString(false, true);
@ -38,47 +49,58 @@ public class TranslateCommand extends Command {
final String message = context.getString(true, true);
final Gson gson = new Gson();
bot.executorService.submit(() -> {
bot.executorService.execute(() -> {
try {
final URL url = new URL("https://translate.google.com/translate_a/single?client=at&dt=t&dt=rm&dj=1");
final String jsonOutput = HttpUtilities.postRequest(
url,
"application/x-www-form-urlencoded;charset=utf-8",
final URL url = new URI(
String.format(
"sl=%s&tl=%s&q=%s",
from,
to,
URLEncoder.encode(
message,
StandardCharsets.UTF_8
)
TRANSLATE_URL,
URLEncoder.encode(from, StandardCharsets.UTF_8),
URLEncoder.encode(to, StandardCharsets.UTF_8),
URLEncoder.encode(message, StandardCharsets.UTF_8)
)
);
).toURL();
final JsonObject jsonObject = gson.fromJson(jsonOutput, JsonObject.class);
final JsonArray sentences = jsonObject.getAsJsonArray("sentences");
final JsonObject translation = sentences.get(0).getAsJsonObject();
final String output = translation.get("trans").getAsString();
final Result result = OBJECT_MAPPER.readValue(url, Result.class);
context.sendOutput(
Component
.translatable(
"Result: %s",
Component.text(output).color(NamedTextColor.GREEN)
)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
.translatable(
"commands.translate.result",
bot.colorPalette.secondary,
Component.text(result.translation(), NamedTextColor.GREEN)
)
);
} catch (Exception e) {
context.sendOutput(Component.text(e.toString()).color(NamedTextColor.RED));
} catch (final Exception e) {
context.sendOutput(Component.text(e.toString(), NamedTextColor.RED));
}
});
return null;
}
/*
$ curl "https://translate-pa.googleapis.com/v1/translate?params.client=gtx&dataTypes=TRANSLATION&key=AIzaSyDLEeFI5OtFBwYBIoK_jj5m32rZK5CkCXA&query.sourceLanguage=auto&query.targetLanguage=ja&query.text=hello"
{
"translation": "こんにちは",
"detectedLanguages": {
"srclangs": [
"en"
],
"extendedSrclangs": [
"en"
]
},
"sourceLanguage": "en"
}
*/
private record Result(
@JsonValue String translation,
@JsonValue String sourceLanguage,
@JsonValue DetectedLanguages detectedLanguages
) {
private record DetectedLanguages(
@JsonValue List<String> srclangs,
@JsonValue List<String> extendedSrclangs
) { }
}
}

View file

@ -5,8 +5,7 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
@ -17,16 +16,14 @@ public class UUIDCommand extends Command {
public UUIDCommand () {
super(
"uuid",
"Shows your UUID or other player's UUID",
new String[] { "[username]" },
new String[] {},
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String player = context.getString(true, false);
@ -34,8 +31,8 @@ public class UUIDCommand extends Command {
if (!player.isEmpty()) {
final PlayerEntry entry = bot.players.getEntry(player);
String name;
String uuid;
final String name;
final String uuid;
if (entry == null) {
name = player;
@ -46,39 +43,45 @@ public class UUIDCommand extends Command {
}
return Component.translatable(
"%s's UUID: %s",
"commands.uuid.other",
NamedTextColor.GREEN,
Component.text(name),
Component
.text(uuid)
.text(uuid, bot.colorPalette.uuid)
.hoverEvent(
HoverEvent.showText(
Component.text("Click here to copy the UUID to your clipboard").color(NamedTextColor.GREEN)
Component.translatable(
"commands.generic.click_to_copy_uuid",
NamedTextColor.GREEN
)
)
)
.clickEvent(
ClickEvent.copyToClipboard(uuid)
)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.uuid))
).color(NamedTextColor.GREEN);
);
} else {
final PlayerEntry entry = context.sender;
final String uuid = entry.profile.getIdAsString();
return Component.translatable(
"Your UUID: %s",
"commands.uuid.self",
NamedTextColor.GREEN,
Component
.text(uuid)
.text(uuid, bot.colorPalette.uuid)
.hoverEvent(
HoverEvent.showText(
Component.text("Click here to copy the UUID to your clipboard").color(NamedTextColor.GREEN)
Component.translatable(
"commands.generic.click_to_copy_uuid",
NamedTextColor.GREEN
)
)
)
.clickEvent(
ClickEvent.copyToClipboard(uuid)
)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.uuid))
).color(NamedTextColor.GREEN);
);
}
}
}

View file

@ -6,9 +6,12 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.*;
import me.chayapak1.chomens_bot.command.*;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.command.contexts.DiscordCommandContext;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.util.HttpUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
@ -17,30 +20,32 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class UrbanCommand extends Command {
public int requestsPerSecond = 0;
public final AtomicInteger requestsPerSecond = new AtomicInteger();
public UrbanCommand () {
super(
"urban",
"Urban Dictionary in Minecraft",
new String[] { "<term>" },
new String[] {},
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[]{ ChatPacketType.DISGUISED }
);
Main.executor.scheduleAtFixedRate(() -> requestsPerSecond = 0, 0, 1, TimeUnit.SECONDS);
Main.EXECUTOR.scheduleAtFixedRate(() -> requestsPerSecond.set(0), 0, 1, TimeUnit.SECONDS);
}
public Component execute (CommandContext context) throws CommandException {
if (requestsPerSecond > 3) throw new CommandException(Component.text("Too many requests"));
public Component execute (final CommandContext context) throws CommandException {
if (requestsPerSecond.get() > 3) throw new CommandException(Component.translatable("commands.urban.error.too_many_requests"));
final Bot bot = context.bot;
@ -50,12 +55,12 @@ public class UrbanCommand extends Command {
final Gson gson = new Gson();
bot.executorService.submit(() -> {
bot.executorService.execute(() -> {
try {
final URL url = new URL(
final URL url = new URI(
"https://api.urbandictionary.com/v0/define?term=" +
URLEncoder.encode(term, StandardCharsets.UTF_8)
);
).toURL();
final String jsonOutput = HttpUtilities.getRequest(url);
@ -63,19 +68,19 @@ public class UrbanCommand extends Command {
final JsonArray list = jsonObject.getAsJsonArray("list");
if (list.isEmpty()) context.sendOutput(Component.text("No results found").color(NamedTextColor.RED));
if (list.isEmpty()) context.sendOutput(Component.translatable("commands.urban.error.no_results", NamedTextColor.RED));
Component discordComponent = Component.text("*Showing only 3 results because Discord*").append(Component.newline());
Component discordComponent = Component.translatable("commands.urban.discord_warning").append(Component.newline());
int count = 0;
int index = 1;
for (JsonElement element : list) {
for (final JsonElement element : list) {
if (count >= 3) break;
final JsonObject definitionObject = element.getAsJsonObject();
final String word = definitionObject.get("word").getAsString();
final String _definition = definitionObject.get("definition").getAsString();
final String originalDefinition = definitionObject.get("definition").getAsString();
final DecimalFormat formatter = new DecimalFormat("#,###");
@ -84,40 +89,35 @@ public class UrbanCommand extends Command {
final String thumbsDown = formatter.format(definitionObject.get("thumbs_down").getAsInt());
final String example = definitionObject.get("example").getAsString();
// whats the best way to implement this?
// what's the best way to implement this?
// also ohio code warning
Component definitionComponent = Component.empty();
final String definition = _definition.replaceAll("\r\n?", "\n");
final String definition = originalDefinition.replaceAll("\r\n?", "\n");
final String[] splittedDefinition = definition.split("[\\[\\]]");
for (int i = 0; i < splittedDefinition.length; i++) {
final String[] splitDefinition = definition.split("[\\[\\]]");
for (int i = 0; i < splitDefinition.length; i++) {
final boolean even = i % 2 == 0;
final String wordWithDefinition = word + " - " + definition;
final Component globalHoverEvent = Component.translatable(
"""
Written by %s
Thumbs up: %s
Thumbs down: %s
Example: %s""",
Component.text(author).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component.text(thumbsUp).color(NamedTextColor.GREEN),
Component.text(thumbsDown).color(NamedTextColor.RED),
Component.text(example.replaceAll("\r\n?", "\n")).color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
"commands.urban.hover.info",
Component.text(author, bot.colorPalette.string),
Component.text(thumbsUp, NamedTextColor.GREEN),
Component.text(thumbsDown, NamedTextColor.RED),
Component.text(example.replaceAll("\r\n?", "\n"), bot.colorPalette.string)
);
if (even) {
definitionComponent = definitionComponent.append(
Component
.text(splittedDefinition[i])
.color(NamedTextColor.GRAY)
.text(splitDefinition[i], NamedTextColor.GRAY)
.hoverEvent(
HoverEvent.showText(
globalHoverEvent
.append(Component.newline())
.append(Component.text("Click here to copy the word and the definition to your clipboard").color(NamedTextColor.GREEN))
.append(Component.translatable("commands.urban.hover.copy", NamedTextColor.GREEN))
)
)
.clickEvent(ClickEvent.copyToClipboard(wordWithDefinition))
@ -126,17 +126,23 @@ public class UrbanCommand extends Command {
final String command = context.prefix +
name +
" " +
splittedDefinition[i];
splitDefinition[i];
definitionComponent = definitionComponent.append(
Component
.text(splittedDefinition[i])
.text(splitDefinition[i])
.style(Style.style(TextDecoration.UNDERLINED))
.hoverEvent(
HoverEvent.showText(
globalHoverEvent
.append(Component.newline())
.append(Component.text("Click here to run " + command).color(NamedTextColor.GREEN))
.append(
Component.translatable(
"commands.urban.hover.run",
NamedTextColor.GREEN,
Component.text(command)
)
)
)
)
.clickEvent(
@ -151,11 +157,12 @@ public class UrbanCommand extends Command {
if (discord) {
discordComponent = discordComponent
.append(
Component.translatable(
"%s - %s",
Component.text(word).color(NamedTextColor.GRAY),
definitionComponent
).color(NamedTextColor.DARK_GRAY)
Component.translatable(
"%s - %s",
NamedTextColor.DARK_GRAY,
Component.text(word, NamedTextColor.GRAY),
definitionComponent
)
)
.append(Component.newline());
@ -163,10 +170,11 @@ public class UrbanCommand extends Command {
} else {
final Component component = Component.translatable(
"[%s] %s - %s",
Component.text(index).color(NamedTextColor.GREEN),
Component.text(word).color(NamedTextColor.GRAY),
NamedTextColor.DARK_GRAY,
Component.text(index, NamedTextColor.GREEN),
Component.text(word, NamedTextColor.GRAY),
definitionComponent
).color(NamedTextColor.DARK_GRAY);
);
context.sendOutput(component);
}
@ -175,13 +183,13 @@ public class UrbanCommand extends Command {
}
if (discord && !list.isEmpty()) context.sendOutput(discordComponent);
} catch (Exception e) {
e.printStackTrace();
context.sendOutput(Component.text(e.toString()).color(NamedTextColor.RED));
} catch (final Exception e) {
bot.logger.error(e);
context.sendOutput(Component.text(e.toString(), NamedTextColor.RED));
}
});
requestsPerSecond++;
requestsPerSecond.getAndIncrement();
return null;
}

View file

@ -1,10 +1,10 @@
package me.chayapak1.chomens_bot.commands;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.command.contexts.ConsoleCommandContext;
import me.chayapak1.chomens_bot.command.contexts.DiscordCommandContext;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -12,27 +12,35 @@ public class ValidateCommand extends Command {
public ValidateCommand () {
super(
"validate",
"Validates a hash",
new String[] { "" },
new String[] { "checkhash" },
TrustLevel.TRUSTED,
false
TrustLevel.TRUSTED
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
final Bot bot = context.bot;
public Component execute (final CommandContext context) {
// <red>Trusted - 1
// <dark_red>Admin - 2
// ...
final Component trustLevelComponent = context.trustLevel.component
.append(Component.text(" - "))
.append(Component.text(context.trustLevel.level));
final String[] fullArgs = context.fullArgs;
if (fullArgs.length == 0) return null;
final String hash = fullArgs[0];
if (bot.hashing.isCorrectHash(hash, context.userInputCommandName, context.sender)) return Component.text("Valid hash").color(NamedTextColor.GREEN);
else if (bot.hashing.isCorrectOwnerHash(hash, context.userInputCommandName, context.sender)) return Component.text("Valid OwnerHash").color(NamedTextColor.GREEN);
return null;
if (context instanceof DiscordCommandContext) return Component
.translatable(
"commands.validate.discord",
NamedTextColor.GREEN,
trustLevelComponent
);
else if (context instanceof ConsoleCommandContext) return Component
.translatable("commands.validate.console", NamedTextColor.GREEN);
else return Component.translatable(
context.sender.persistingData.authenticatedTrustLevel != TrustLevel.PUBLIC
? "commands.validate.player_authenticated"
: "commands.validate.player",
NamedTextColor.GREEN,
trustLevelComponent
);
}
}

View file

@ -7,31 +7,26 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.util.HttpUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class WeatherCommand extends Command {
public WeatherCommand () {
super(
"weather",
"Shows the weather in a place",
new String[] { "<location>" },
new String[] {},
TrustLevel.PUBLIC,
false
TrustLevel.PUBLIC
);
}
public Component execute (CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String location = context.getString(true, true);
@ -39,29 +34,23 @@ public class WeatherCommand extends Command {
final Gson gson = new Gson();
try {
final URL url = new URL(
final URL url = new URI(
"https://api.weatherapi.com/v1/current.json?key=" + bot.config.weatherApiKey + "&q=" +
URLEncoder.encode(
location,
location,
StandardCharsets.UTF_8
)
+ "&aqi=no"
);
).toURL();
final String jsonOutput = HttpUtilities.getRequest(url);
final JsonObject jsonObject = gson.fromJson(jsonOutput, JsonObject.class);
final ZoneId zoneId = ZoneId.of(jsonObject.get("location").getAsJsonObject().get("tz_id").getAsString());
final ZonedDateTime zonedDateTime = ZonedDateTime.now(zoneId);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm:ss a");
final String time = zonedDateTime.format(formatter);
return Component.translatable(
"Weather forecast for %s, %s:\n%s (%s), feels like %s (%s)\nTime: %s",
Component.text(jsonObject.get("location").getAsJsonObject().get("name").getAsString()).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component.text(jsonObject.get("location").getAsJsonObject().get("country").getAsString()).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
"commands.weather.info",
bot.colorPalette.defaultColor,
Component.text(jsonObject.get("location").getAsJsonObject().get("name").getAsString(), bot.colorPalette.string),
Component.text(jsonObject.get("location").getAsJsonObject().get("country").getAsString(), bot.colorPalette.string),
Component
.empty()
.append(
@ -73,7 +62,7 @@ public class WeatherCommand extends Command {
.get("temp_c")
.getAsString() + "°C"
)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
.color(bot.colorPalette.secondary)
),
Component
.text(
@ -83,7 +72,7 @@ public class WeatherCommand extends Command {
.get("temp_f")
.getAsString() + "°F"
)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary)),
.color(bot.colorPalette.secondary),
Component
.text(
jsonObject
@ -102,10 +91,49 @@ public class WeatherCommand extends Command {
.getAsString() + "°F"
)
.color(NamedTextColor.GREEN),
Component.text(time).color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
} catch (Exception e) {
throw new CommandException(Component.text("Location \"" + location + "\" not found"));
Component
.text(
jsonObject
.get("current")
.getAsJsonObject()
.get("condition")
.getAsJsonObject()
.get("text")
.getAsString()
)
.color(bot.colorPalette.string),
Component
.text(
jsonObject
.get("current")
.getAsJsonObject()
.get("cloud")
.getAsInt()
)
.append(Component.text("%"))
.color(bot.colorPalette.number),
Component
.text(
jsonObject
.get("current")
.getAsJsonObject()
.get("humidity")
.getAsInt()
)
.append(Component.text("%"))
.color(bot.colorPalette.number),
Component
.text(
jsonObject
.get("location")
.getAsJsonObject()
.get("localtime")
.getAsString()
)
.color(bot.colorPalette.string)
);
} catch (final Exception e) {
throw new CommandException(Component.translatable("commands.weather.error.not_found", Component.text(location)));
}
}
}

View file

@ -5,7 +5,6 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.format.NamedTextColor;
@ -17,19 +16,17 @@ public class WhitelistCommand extends Command {
public WhitelistCommand () {
super(
"whitelist",
"Manages whitelist",
new String[] { "enable", "disable", "add <player>", "remove <index>", "clear", "list" },
new String[] {},
TrustLevel.OWNER,
false
TrustLevel.ADMIN
);
}
@Override
public Component execute(CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String action = context.getString(false, true, true);
final String action = context.getAction();
switch (action) {
case "enable" -> {
@ -37,14 +34,14 @@ public class WhitelistCommand extends Command {
bot.whitelist.enable();
return Component.text("Enabled whitelist").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.whitelist.enable", bot.colorPalette.defaultColor);
}
case "disable" -> {
context.checkOverloadArgs(1);
bot.whitelist.disable();
return Component.text("Disabled whitelist").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.whitelist.disable", bot.colorPalette.defaultColor);
}
case "add" -> {
final String player = context.getString(true, true);
@ -52,9 +49,10 @@ public class WhitelistCommand extends Command {
bot.whitelist.add(player);
return Component.translatable(
"Added %s to the whitelist",
Component.text(player).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
"commands.whitelist.add",
bot.colorPalette.defaultColor,
Component.text(player, bot.colorPalette.username)
);
}
case "remove" -> {
try {
@ -63,11 +61,12 @@ public class WhitelistCommand extends Command {
final String player = bot.whitelist.remove(index);
return Component.translatable(
"Removed %s from the whitelist",
Component.text(player).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
} catch (IndexOutOfBoundsException | IllegalArgumentException | NullPointerException ignored) {
throw new CommandException(Component.text("Invalid index"));
"commands.whitelist.remove",
bot.colorPalette.defaultColor,
Component.text(player, bot.colorPalette.username)
);
} catch (final IndexOutOfBoundsException | IllegalArgumentException | NullPointerException e) {
throw new CommandException(Component.translatable("commands.generic.error.invalid_index"));
}
}
case "clear" -> {
@ -75,7 +74,7 @@ public class WhitelistCommand extends Command {
bot.whitelist.clear();
return Component.text("Cleared the whitelist").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
return Component.translatable("commands.whitelist.clear", bot.colorPalette.defaultColor);
}
case "list" -> {
context.checkOverloadArgs(1);
@ -83,29 +82,30 @@ public class WhitelistCommand extends Command {
final List<Component> playersComponent = new ArrayList<>();
int index = 0;
for (String player : bot.whitelist.list) {
for (final String player : bot.whitelist.list) {
playersComponent.add(
Component.translatable(
"%s %s",
Component.text(index).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
Component.text(player).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(NamedTextColor.DARK_GRAY)
NamedTextColor.DARK_GRAY,
Component.text(index, bot.colorPalette.number),
Component.text(player, bot.colorPalette.username)
)
);
index++;
}
return Component.empty()
.append(Component.text("Whitelisted players ").color(NamedTextColor.GREEN))
.append(Component.text("(").color(NamedTextColor.DARK_GRAY))
.append(Component.text(bot.whitelist.list.size()).color(NamedTextColor.GRAY))
.append(Component.text(")").color(NamedTextColor.DARK_GRAY))
.append(Component.translatable("commands.whitelist.whitelisted_players_text", NamedTextColor.GREEN))
.append(Component.text("(", NamedTextColor.DARK_GRAY))
.append(Component.text(bot.whitelist.list.size(), NamedTextColor.GRAY))
.append(Component.text(")", NamedTextColor.DARK_GRAY))
.append(Component.newline())
.append(
Component.join(JoinConfiguration.newlines(), playersComponent)
);
}
default -> throw new CommandException(Component.text("Invalid action"));
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));
}
}
}

View file

@ -9,44 +9,46 @@ import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.CommandContext;
import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.util.HTMLUtilities;
import me.chayapak1.chomens_bot.util.HttpUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class WikipediaCommand extends Command {
public static final String pageIDStringURL = "https://en.wikipedia.org/w/api.php?prop=info%%7Cpageprops&inprop=url&ppprop=disambiguation&titles=%s&format=json&redirects=&action=query&origin=*&";
public static final String outputStringURL = "https://en.wikipedia.org/w/api.php?prop=extracts&explaintext=&exintro=&pageids=%d&format=json&redirects=&action=query&origin=*&";
public static final String outputStringURL = "https://en.wikipedia.org/w/api.php?prop=extracts&exintro=&pageids=%d&format=json&redirects=&action=query&origin=*&";
public WikipediaCommand () {
super(
"wikipedia",
"Wikipedia in Minecraft",
new String[] { "<page>" },
new String[] { "wiki" },
TrustLevel.PUBLIC,
false
false,
new ChatPacketType[] { ChatPacketType.DISGUISED }
);
}
public Component execute (CommandContext context) throws CommandException {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String page = context.getString(true, true);
final Gson gson = new Gson();
bot.executorService.submit(() -> {
bot.executorService.execute(() -> {
try {
Component component = Component.empty();
final URL pageIDUrl = new URL(String.format(pageIDStringURL, URLEncoder.encode(page, StandardCharsets.UTF_8)));
final URL pageIDUrl = new URI(String.format(pageIDStringURL, URLEncoder.encode(page, StandardCharsets.UTF_8))).toURL();
final JsonObject pageIDJsonOutput = gson.fromJson(HttpUtilities.getRequest(pageIDUrl), JsonObject.class);
@ -56,7 +58,7 @@ public class WikipediaCommand extends Command {
if (redirectsElement != null) {
final JsonArray normalized = redirectsElement.getAsJsonArray();
for (JsonElement element : normalized) {
for (final JsonElement element : normalized) {
final JsonObject redirect = element.getAsJsonObject();
final String redirectedTo = redirect.get("to").getAsString();
@ -79,11 +81,15 @@ public class WikipediaCommand extends Command {
final int pageID = Integer.parseInt(pages.entrySet().iterator().next().getKey());
if (pageID == -1) {
context.sendOutput(Component.text("Cannot find page: " + page).color(NamedTextColor.RED));
return;
throw new CommandException(
Component.translatable(
"commands.wikipedia.error.not_found",
Component.text(page)
)
);
}
final URL outputUrl = new URL(String.format(outputStringURL, pageID));
final URL outputUrl = new URI(String.format(outputStringURL, pageID)).toURL();
final JsonObject outputJsonOutput = gson.fromJson(HttpUtilities.getRequest(outputUrl), JsonObject.class);
@ -93,7 +99,9 @@ public class WikipediaCommand extends Command {
.getAsJsonObject(String.valueOf(pageID));
final String title = pageOutput.get("title").getAsString();
final String extract = pageOutput.get("extract").getAsString();
final String extracted = HTMLUtilities.toFormattingCodes(pageOutput.get("extract").getAsString());
if (extracted == null) throw new CommandException(Component.translatable("commands.wikipedia.error.no_contents"));
component = component
.append(
@ -102,18 +110,20 @@ public class WikipediaCommand extends Command {
.style(
Style.style()
.decorate(TextDecoration.BOLD)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
.color(bot.colorPalette.secondary)
)
)
.append(Component.newline())
.append(Component.text(extract).color(NamedTextColor.GREEN));
.append(Component.text(extracted, NamedTextColor.GREEN));
context.sendOutput(component);
} catch (NumberFormatException e) {
context.sendOutput(Component.text("Failed parsing page ID").color(NamedTextColor.RED));
e.printStackTrace();
} catch (Exception e) {
context.sendOutput(Component.text(e.toString()).color(NamedTextColor.RED));
} catch (final NumberFormatException e) {
context.sendOutput(Component.translatable("commands.wikipedia.error.fail_page_id_parse", NamedTextColor.RED));
bot.logger.error(e);
} catch (final CommandException e) {
context.sendOutput(e.message.color(NamedTextColor.RED));
} catch (final Exception e) {
context.sendOutput(Component.text(e.toString(), NamedTextColor.RED));
}
});

View file

@ -1,8 +0,0 @@
package me.chayapak1.chomens_bot.data;
public record CommandLoop (
String command,
int interval
) {}

View file

@ -1,17 +0,0 @@
package me.chayapak1.chomens_bot.data;
public class FilteredPlayer {
public final String playerName;
public final boolean regex;
public final boolean ignoreCase;
public FilteredPlayer (
String playerName,
boolean regex,
boolean ignoreCase
) {
this.playerName = playerName;
this.regex = regex;
this.ignoreCase = ignoreCase;
}
}

View file

@ -1,10 +0,0 @@
package me.chayapak1.chomens_bot.data;
// https://gist.github.com/kaecy/286f8ad334aec3fcb588516feb727772#file-message-java
public class IRCMessage {
public String origin;
public String nickName;
public String command;
public String channel;
public String content;
}

View file

@ -1,23 +0,0 @@
package me.chayapak1.chomens_bot.data;
public class Mail {
public final String sentBy;
public final String sentTo;
public final long timeSent;
public final String server;
public final String contents;
public Mail (
String sentBy,
String sentTo,
long timeSent,
String server,
String contents
) {
this.sentBy = sentBy;
this.sentTo = sentTo;
this.timeSent = timeSent;
this.server = server;
this.contents = contents;
}
}

View file

@ -1,43 +0,0 @@
package me.chayapak1.chomens_bot.data;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntry;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import net.kyori.adventure.text.Component;
import java.security.PublicKey;
public class PlayerEntry {
public final GameProfile profile;
public GameMode gamemode;
public int latency;
public Component displayName;
public final long expiresAt;
public PublicKey publicKey;
public final byte[] keySignature;
public boolean listed;
public PlayerEntry(
GameProfile profile,
GameMode gamemode,
int latency,
Component displayName,
long expiresAt,
PublicKey publicKey,
byte[] keySignature,
boolean listed
) {
this.profile = profile;
this.gamemode = gamemode;
this.latency = latency;
this.displayName = displayName;
this.expiresAt = expiresAt;
this.publicKey = publicKey;
this.keySignature = keySignature;
this.listed = listed;
}
public PlayerEntry(PlayerListEntry entry) {
this(entry.getProfile(), entry.getGameMode(), entry.getLatency(), entry.getDisplayName(), entry.getExpiresAt(), entry.getPublicKey(), entry.getKeySignature(), entry.isListed());
}
}

View file

@ -1,6 +0,0 @@
package me.chayapak1.chomens_bot.data;
public record Rotation (
float yaw,
float pitch
) {}

View file

@ -1,40 +0,0 @@
package me.chayapak1.chomens_bot.data;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor;
import net.kyori.adventure.text.Component;
public class Team {
public String teamName;
public Component displayName;
public boolean friendlyFire;
public boolean seeFriendlyInvisibles;
public NameTagVisibility nametagVisibility;
public CollisionRule collisionRule;
public TeamColor color;
public Component prefix;
public Component suffix;
public Team (
String teamName,
Component displayName,
boolean friendlyFire,
boolean seeFriendlyInvisibles,
NameTagVisibility nametagVisibility,
CollisionRule collisionRule,
TeamColor color,
Component prefix,
Component suffix
) {
this.teamName = teamName;
this.displayName = displayName;
this.friendlyFire = friendlyFire;
this.seeFriendlyInvisibles = seeFriendlyInvisibles;
this.nametagVisibility = nametagVisibility;
this.collisionRule = collisionRule;
this.color = color;
this.prefix = prefix;
this.suffix = suffix;
}
}

View file

@ -1,8 +1,8 @@
package me.chayapak1.chomens_bot.data;
package me.chayapak1.chomens_bot.data.bossbar;
import net.kyori.adventure.text.Component;
import org.geysermc.mcprotocollib.protocol.data.game.BossBarColor;
import org.geysermc.mcprotocollib.protocol.data.game.BossBarDivision;
import net.kyori.adventure.text.Component;
import java.util.UUID;
@ -15,11 +15,11 @@ public class BossBar {
public float health;
public BossBar (
UUID uuid,
Component title,
BossBarColor color,
BossBarDivision division,
float health
final UUID uuid,
final Component title,
final BossBarColor color,
final BossBarDivision division,
final float health
) {
this.uuid = uuid;
this.title = title;
@ -28,4 +28,3 @@ public class BossBar {
this.health = health;
}
}

View file

@ -1,18 +1,21 @@
package me.chayapak1.chomens_bot.data;
package me.chayapak1.chomens_bot.data.bossbar;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.util.SNBTUtilities;
import net.kyori.adventure.text.Component;
import org.geysermc.mcprotocollib.protocol.data.game.BossBarColor;
import org.geysermc.mcprotocollib.protocol.data.game.BossBarDivision;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.util.UUID;
public class BotBossBar extends BossBar {
public UUID uuid = UUID.randomUUID(); // the random uuid will be temporary
public UUID uuid = UUID.randomUUID(); // temporary
public final Component secret = Component.text(Math.random() * 69420);
public final Component secret = Component
.translatable(
"",
Component.text(UUID.randomUUID().toString())
);
public boolean gotSecret = false;
@ -26,15 +29,15 @@ public class BotBossBar extends BossBar {
private long max;
private int value;
public BotBossBar(
Component title,
String players,
BossBarColor color,
BossBarDivision division,
boolean visible,
long max,
int value,
Bot bot
public BotBossBar (
final Component title,
final String players,
final BossBarColor color,
final BossBarDivision division,
final boolean visible,
final long max,
final int value,
final Bot bot
) {
super(null, title, color, division, value);
this.players = players;
@ -43,18 +46,16 @@ public class BotBossBar extends BossBar {
this.bot = bot;
}
public Component title() {
public Component title () {
return title;
}
public void setTitle(Component title) {
public void setTitle (final Component title) {
setTitle(title, false);
}
public void setTitle(Component title, boolean force) {
if (!gotSecret) return;
if (ComponentUtilities.isEqual(this.title, title) && !force) return;
public void setTitle (final Component title, final boolean force) {
if ((this.title.equals(title) || !gotSecret) && !force) return;
if (bot.bossbar.actionBar) {
bot.chat.actionBar(title, players);
@ -64,25 +65,31 @@ public class BotBossBar extends BossBar {
this.title = title;
final String serialized = GsonComponentSerializer.gson().serialize(title);
final String serialized = SNBTUtilities.fromComponent(bot.options.useSNBTComponents, title);
bot.core.run("minecraft:bossbar set " + id + " name " + serialized);
if (!bot.core.hasRateLimit()) bot.core.run("minecraft:execute as @e[type=minecraft:text_display,tag=" + bot.config.bossBarNamespace + "_" + onlyName + "] run data modify entity @s text set value '" + serialized.replace("\\", "\\\\").replace("'", "\\'") + "'");
if (!bot.core.hasRateLimit())
bot.core.run(
String.format(
"minecraft:execute as @e[type=minecraft:text_display,tag=%s_%s] run data modify entity @s text set value %s",
bot.config.namespace,
onlyName,
serialized
)
);
}
public BossBarColor color(BossBarColor color) {
public BossBarColor color (final BossBarColor color) {
return color;
}
public void setColor(BossBarColor color) {
public void setColor (final BossBarColor color) {
setColor(color, false);
}
public void setColor(BossBarColor color, boolean force) {
if (!gotSecret) return;
if (this.color == color && !force) return;
public void setColor (final BossBarColor color, final boolean force) {
if ((this.color == color || !gotSecret) && !force) return;
this.color = color;
@ -91,14 +98,14 @@ public class BotBossBar extends BossBar {
bot.core.run("minecraft:bossbar set " + id + " color " + (color == BossBarColor.LIME ? "green" : (color == BossBarColor.CYAN ? "blue" : color.name().toLowerCase())));
}
public String players() {
public String players () {
return players;
}
public void setPlayers(String players) {
if (!gotSecret) return;
public void setPlayers (final String players) { setPlayers(players, false); }
if (this.players.equals(players)) return;
public void setPlayers (final String players, final boolean force) {
if ((this.players.equals(players) || !gotSecret) && !force) return;
this.players = players;
@ -108,13 +115,13 @@ public class BotBossBar extends BossBar {
}
public BossBarDivision division () { return division; }
public void setDivision (BossBarDivision division) {
public void setDivision (final BossBarDivision division) {
setDivision(division, false);
}
public void setDivision (BossBarDivision _division, boolean force) {
if (!gotSecret) return;
if (this.division == _division && !force) return;
public void setDivision (final BossBarDivision _division, final boolean force) {
if ((this.division == _division || !gotSecret) && !force) return;
this.division = _division;
@ -134,13 +141,13 @@ public class BotBossBar extends BossBar {
}
public int value () { return value; }
public void setValue (int value) {
public void setValue (final int value) {
setValue(value, false);
}
public void setValue (int value, boolean force) {
if (!gotSecret) return;
if (this.value == value && !force) return;
public void setValue (final int value, final boolean force) {
if ((this.value == value || !gotSecret) && !force) return;
this.value = value;
@ -150,10 +157,13 @@ public class BotBossBar extends BossBar {
}
public boolean visible () { return visible; }
public void setVisible (boolean visible) {
if (!gotSecret) return;
if (this.visible == visible) return;
public void setVisible (final boolean visible) {
setVisible(visible, false);
}
public void setVisible (final boolean visible, final boolean force) {
if ((this.visible == visible || !gotSecret) && !force) return;
this.visible = visible;
@ -163,13 +173,13 @@ public class BotBossBar extends BossBar {
}
public long max () { return max; }
public void setMax (long max) {
public void setMax (final long max) {
setMax(max, false);
}
public void setMax (long max, boolean force) {
if (!gotSecret) return;
if (this.max == max && !force) return;
public void setMax (final long max, final boolean force) {
if ((this.max == max || !gotSecret) && !force) return;
this.max = max;

View file

@ -0,0 +1,7 @@
package me.chayapak1.chomens_bot.data.chat;
public enum ChatPacketType {
PLAYER,
DISGUISED,
SYSTEM
}

View file

@ -1,20 +1,7 @@
package me.chayapak1.chomens_bot.data.chat;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import net.kyori.adventure.text.Component;
public class PlayerMessage {
public final PlayerEntry sender;
public final Component displayName;
public final Component contents;
public PlayerMessage (
PlayerEntry sender,
Component displayName,
Component contents
) {
this.sender = sender;
this.displayName = displayName;
this.contents = contents;
}
public record PlayerMessage(PlayerEntry sender, Component displayName, Component contents) {
}

View file

@ -0,0 +1,41 @@
package me.chayapak1.chomens_bot.data.chomeNSMod;
import io.netty.buffer.ByteBuf;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public record PayloadMetadata(byte[] nonce, long timestamp) {
public static PayloadMetadata deserialize (final ByteBuf buf) {
final byte[] nonce = new byte[8];
buf.readBytes(nonce);
final long timestamp = buf.readLong();
return new PayloadMetadata(nonce, timestamp);
}
public void serialize (final ByteBuf buf) {
buf.writeBytes(nonce);
buf.writeLong(timestamp);
}
@Override
public boolean equals (final Object object) {
if (object == null || getClass() != object.getClass()) return false;
final PayloadMetadata metadata = (PayloadMetadata) object;
// java is so fucky about byte[]......... i have to use Arrays.equals()
return timestamp == metadata.timestamp && Arrays.equals(nonce, metadata.nonce);
}
@Override
public @NotNull String toString () {
return "PayloadMetadata{" +
"nonce=" + Arrays.toString(nonce) +
", timestamp=" + timestamp +
'}';
}
}

View file

@ -0,0 +1,6 @@
package me.chayapak1.chomens_bot.data.chomeNSMod;
public enum PayloadState {
JOINING,
DONE
}

View file

@ -0,0 +1,4 @@
package me.chayapak1.chomens_bot.data.chunk;
public record ChunkPos(int x, int z) {
}

View file

@ -0,0 +1,12 @@
package me.chayapak1.chomens_bot.data.cloop;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.ScheduledFuture;
public record CommandLoop(
String command,
long interval,
ChronoUnit unit,
ScheduledFuture<?> task
) { }

View file

@ -0,0 +1,28 @@
package me.chayapak1.chomens_bot.data.color;
import me.chayapak1.chomens_bot.Configuration;
import net.kyori.adventure.text.format.TextColor;
import static me.chayapak1.chomens_bot.util.ColorUtilities.getColorByString;
public class ColorPalette {
public final TextColor primary;
public final TextColor secondary;
public final TextColor defaultColor;
public final TextColor username;
public final TextColor uuid;
public final TextColor string;
public final TextColor number;
public final TextColor ownerName;
public ColorPalette (final Configuration.ColorPalette configColorPalette) {
this.primary = getColorByString(configColorPalette.primary);
this.secondary = getColorByString(configColorPalette.secondary);
this.defaultColor = getColorByString(configColorPalette.defaultColor);
this.username = getColorByString(configColorPalette.username);
this.uuid = getColorByString(configColorPalette.uuid);
this.string = getColorByString(configColorPalette.string);
this.number = getColorByString(configColorPalette.number);
this.ownerName = getColorByString(configColorPalette.ownerName);
}
}

View file

@ -0,0 +1,6 @@
package me.chayapak1.chomens_bot.data.entity;
public record Rotation(
float yaw,
float pitch
) { }

View file

@ -5,25 +5,12 @@ import me.chayapak1.chomens_bot.Bot;
public class EvalFunction {
public final String name;
protected final Bot bot;
public EvalFunction (
String name,
Bot bot
) {
public EvalFunction (final String name) {
this.name = name;
this.bot = bot;
}
public Output execute (Object ...args) throws Exception { return null; }
public Output execute (final Bot bot, final Object... args) throws Exception { return null; }
public static class Output {
public final String message;
public final boolean parseJSON;
public Output (String message, boolean parseJSON) {
this.message = message;
this.parseJSON = parseJSON;
}
public record Output(String message, boolean parseJSON) {
}
}

Some files were not shown because too many files have changed in this diff Show more