Compare commits

...

78 commits

Author SHA1 Message Date
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
efd837db36
fix: tick plugin breaking when there's an exception 2024-11-19 18:08:25 +07:00
e4970cc56b
refactor: refactor PersistentDataUtilities (credit: ChatGPT) 2024-11-19 17:59:38 +07:00
d3845f7c8a
refactor: optimize stopping discord 2024-11-19 17:42:11 +07:00
6c507219f2
feat: core place block queue 2024-11-17 17:49:06 +07:00
9f10ca8a3e
fix: fix nullpointerexception in FindAltsCommand 2024-11-17 17:41:05 +07:00
b424f29972
refactor: remove some outputs in console command 2024-11-17 17:01:43 +07:00
71d9caa0db
feat,refactor: add useCorePlaceBlock in config and refactor default config 2024-11-17 17:01:09 +07:00
a942cfd557
refactor: improve how the bot loads plugins 2024-11-17 16:37:05 +07:00
2a6ef53c25
fix: make servereval run in executorService 2024-11-17 11:46:17 +07:00
65aa230260
fix,refactor: fix findalts and use thenApplyAsync instead of thenApply for non-blocking 2024-11-16 18:41:34 +07:00
9b824e195a
"fix": my try on fixing the login spam (no work) 2024-11-16 13:07:53 +07:00
a44cae4d3e
fix: the commit before this: commandspy 2024-11-15 20:00:26 +07:00
e51bd0b1e5
refactor: don't use executorService when running a command in ChatCommandHandlerPlugin (69x faster) 2024-11-15 19:59:48 +07:00
832a391ad0
refactor: remove the englishLanguage.json 2024-11-15 19:59:27 +07:00
ed15e9b023
refactor: don't use joda time and use java's time classes 2024-11-15 19:41:23 +07:00
a9fb768795
fix: update languages and fix keybind parsing 2024-11-15 18:07:04 +07:00
c738a37948
"fix": this seems to do nothing 2024-11-14 18:43:57 +07:00
373736feed
feat: add *info (no arguments) 2024-11-14 16:41:33 +07:00
c0909d54aa
style fix: i forgor the color change 2024-11-14 16:18:17 +07:00
159cb8ce11
refactor: make cloop remove return command name 2024-11-14 16:11:16 +07:00
627b18ebf9
refactor: make whitelist remove by index like in the usage 2024-11-14 16:11:00 +07:00
66dc3b97ec
refactor: refactor PersistentDataUtilities, hopefully it will work ok
i do have a backup so this should be fine if it fails somehow
2024-11-12 20:04:22 +07:00
f322e2dd14
fix: more fix in Main 2024-11-12 19:28:09 +07:00
5e9a1f0917
fix: stopping stuff in Main and IRCPlugin 2024-11-12 16:43:45 +07:00
4ba78b843b
refactor,fix: music stuff, and add music locking (servereval only) 2024-11-12 16:40:46 +07:00
357559ae6e
fix: ignore exceptions in irc queue tick 2024-11-12 16:36:55 +07:00
7363252660
fix: fix loopPosition in Song 2024-11-12 16:09:00 +07:00
4b2b1e9e1a
fix: cool 2024-11-11 20:34:10 +07:00
28012c1194
fix: remove null uuid for extras chat format, which breaks a lot of commands 2024-11-11 17:58:51 +07:00
7b78ce7d06
fix: prevent whitelist from kicking the bot itself 2024-11-10 19:25:42 +07:00
11f478487d
fix: throw CommandException itself in GrepLogPlugin 2024-11-10 12:10:28 +07:00
4f8d5bfff5
fix: makeNewLogFile() in FileLoggerUtilities appending to the file instead of deleting the existing one 2024-11-10 08:31:43 +07:00
3811790c93
feat: *console printdisconnectedcause <true|false>
very useful for debugging
2024-11-07 17:52:12 +07:00
597a694727
fix: (maybe) ACTUALLY fix file logging (for the 69th time) 2024-11-05 16:44:56 +07:00
f561a9c810
refactor (eval): getBotUsername -> getBotInfo & make core() tracked in eval 2024-11-04 19:23:31 +07:00
4b16f12623
fix: mabe 2024-11-04 19:06:43 +07:00
783a7a937e
refactor: improve AuthPlugin by at least a bit 2024-11-04 16:57:28 +07:00
cb86896223
"fix": doesn't work 2024-11-03 19:34:57 +07:00
e62de655ba
fix: the commit before this 2024-11-02 19:30:24 +07:00
0ddf042311
feat: automatically make the bot go down if currentY > maxY 2024-11-02 19:27:08 +07:00
4764a8947e
refactor: make maps in PositionPlugin public for access with servereval 2024-11-02 18:31:28 +07:00
b3615770dc
fix: fix most OOBs, and also fix maxY in WorldPlugin 2024-11-02 18:18:47 +07:00
3ad8b11494
fix: finally fix voicechat 2024-11-02 17:37:09 +07:00
b118d50249
feat: getLatestChatMessage eval function 2024-11-02 17:12:23 +07:00
9e22cdfea5
feat: getBotUsername eval function 2024-11-02 15:29:47 +07:00
c655b5d0cd
chore (default_config.yml): some modifications to the comment at ownerName and backup 2024-11-02 14:37:06 +07:00
71ef35598c
style: use DecimalFormatter on greplog
from now on i am going to have the types on commits because why not
2024-11-02 14:29:02 +07:00
c707309958 Merge branch 'amy' 2024-11-02 12:59:56 +07:00
amy
bf52424792
refactor: fix buildscripts 2024-11-01 11:03:45 -03:00
b1dc8dfc7c Add fallback on translate (ComponentUtilities) 2024-10-30 19:02:11 +07:00
0d73ddd791 Fix newline on SeenCommand 2024-10-30 16:33:29 +07:00
3740a9b45f HOTFIX FIX 2024-10-29 19:28:15 +07:00
2de709c127 HOTFIX EMERGENCY https://files.chipmunk.land/pi0mz7cx.png 2024-10-29 19:22:36 +07:00
feed688a89 Add FilteredPlayer options info into FilterCommand's list 2024-10-29 16:45:25 +07:00
8e188d9ec7 Refactor FilterPlugin's getPlayer, hopefully it works the same 2024-10-28 18:19:35 +07:00
43d12f2471 Escape . in Discord to fix the invite leaking 2024-10-28 16:53:47 +07:00
bff3c2f58d Fix the "Fix the fix that doesn't fix.". 2024-10-27 11:29:55 +07:00
4142875c06 Fix the fix that doesn't fix. 2024-10-26 20:35:46 +07:00
32c3801acf Fix SeenCommand when the same player is online in multiple servers 2024-10-26 18:33:41 +07:00
9b214c7111 Correct the message 2024-10-26 18:18:16 +07:00
743072d8e3 FindAltsCommand 2024-10-26 18:11:28 +07:00
a4f7b0e6a8 forgor to change the thingy to use CommandException 2024-10-26 17:10:36 +07:00
d0f0911f8c Change SeenCommand invalid entry message 2024-10-26 17:08:37 +07:00
ef5ec8dc27 fix 2024-10-26 17:06:43 +07:00
a948ef84e2 unused (for now) 2024-10-26 09:23:49 +07:00
93 changed files with 2374 additions and 7552 deletions

62
.gitignore vendored
View file

@ -1,55 +1,17 @@
.gradle
**/build/
!src/**/build/
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/dictionaries
.idea/**/shelf
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
.gradle
build/
run/
.idea/misc.xml
# IntelliJ IDEA
.idea/
*.iml
*.ipr
*.iws
gradle
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties
# Cache of project
.gradletasknamecache
# Eclipse Gradle plugin generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Exploits plugin
/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/ExploitsPlugin.java
# 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/land/chipmunk/chayapak/chomens_bot/plugins/testing
/.idea/modules/
/.idea/modules.xml
src/main/java/me/chayapak1/chomens_bot/plugins/testing

3
.idea/.gitignore vendored
View file

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/.idea/workspace.xml

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
</module>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
</annotationProcessing>
<bytecodeTargetLevel target="17" />
</component>
</project>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
</project>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

View file

@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="minecraft-libraries" />
<option name="name" value="Minecraft Libraries" />
<option name="url" value="https://libraries.minecraft.net" />
</remote-repository>
<remote-repository>
<option name="id" value="dv8tion" />
<option name="name" value="m2-dv8tion" />
<option name="url" value="https://m2.dv8tion.net/releases" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="maven3" />
<option name="name" value="maven3" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven4" />
<option name="name" value="maven4" />
<option name="url" value="https://maven.maxhenkel.de/repository/public" />
</remote-repository>
<remote-repository>
<option name="id" value="opencollab" />
<option name="name" value="opencollab" />
<option name="url" value="https://repo.opencollab.dev/maven-snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenLocal" />
<option name="name" value="MavenLocal" />
<option name="url" value="file:$MAVEN_REPOSITORY$/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="opencollab" />
<option name="name" value="opencollab" />
<option name="url" value="https://repo.opencollab.dev/maven-releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://repo.opencollab.dev/main/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
</component>
</project>

View file

@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -1,27 +1,10 @@
# 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;
import java.util.UUID;
public class ExploitsPlugin {
public ExploitsPlugin (Bot bot) {}
public void kick (UUID uuid) {}
public void pcrash (UUID uuid) {}
}
```
Then at the root of the project run `./gradlew shadowJar` for Linux or `gradlew.bat shadowJar` for Windows
The .jar file will be at `build/libs`, to run the bot do `java -jar chomens_bot-rolling-all.jar`
The .jar file will be at `build/libs`, to run the bot simply do `java -jar chomens_bot-rolling-all.jar`

1
build-number.txt Normal file
View file

@ -0,0 +1 @@
1136

View file

@ -1,11 +1,8 @@
/*
* This file was generated by the Gradle 'init' task.
*/
import java.text.SimpleDateFormat
plugins {
id 'java'
id 'java-library'
id 'maven-publish'
id 'application'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
@ -16,7 +13,6 @@ java.sourceCompatibility = JavaVersion.VERSION_17
repositories {
mavenLocal()
mavenCentral()
maven {
@ -56,7 +52,6 @@ dependencies {
implementation 'org.yaml:snakeyaml:2.0'
implementation 'org.luaj:luaj-jse:3.0.1'
implementation 'net.dv8tion:JDA:5.0.0-beta.12'
implementation 'joda-time:joda-time:2.12.4'
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'
@ -64,12 +59,55 @@ dependencies {
implementation 'org.concentus:Concentus:1.0-SNAPSHOT'
}
jar {
manifest {
attributes 'Main-Class': 'me.chayapak1.chomens_bot.Main'
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'
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

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

5
gradlew vendored
View file

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@ -84,7 +86,8 @@ 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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

186
gradlew.bat vendored
View file

@ -1,92 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -1,5 +1 @@
/*
* This file was generated by the Gradle 'init' task.
*/
rootProject.name = 'chomens_bot'

View file

@ -41,7 +41,10 @@ public class Bot {
public Session session;
public boolean printDisconnectedCause = false;
public boolean loggedIn = false;
public long loginTime;
public final ExecutorService executorService = Main.executorService;
public final ScheduledExecutorService executor = Main.executor;
@ -96,16 +99,9 @@ public class Bot {
this.bots = bots;
this.config = config;
ConsolePlugin.addListener(new ConsolePlugin.Listener() {
@Override
public void ready() {
Bot.this.ready();
}
});
}
public void ready () {
public void connect () {
this.tick = new TickPlugin(this);
this.chat = new ChatPlugin(this);
this.commandSpy = new CommandSpyPlugin(this);
@ -177,8 +173,10 @@ public class Bot {
}
if (packet instanceof ClientboundLoginPacket) {
loggedIn = true;
loginTime = System.currentTimeMillis();
for (SessionListener listener : listeners) {
loggedIn = true;
listener.connected(new ConnectedEvent(session));
}
@ -251,6 +249,8 @@ public class Bot {
final Throwable cause = disconnectedEvent.getCause();
if (printDisconnectedCause && cause != null) cause.printStackTrace();
// lazy fix #69420
if (cause instanceof OutOfMemoryError) System.exit(1);

View file

@ -12,9 +12,6 @@ public class Configuration {
public String consoleCommandPrefix;
public Keys keys = new Keys();
public InternetCheck internetCheck = new InternetCheck();
public Backup backup = new Backup();
public String weatherApiKey;
@ -52,18 +49,16 @@ public class Configuration {
public int timeout = 6000;
}
public static class InternetCheck {
public boolean enabled = true;
public String address = "https://sus.red";
}
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 trustedKey;
public String adminKey;
public String ownerKey;
}
@ -92,19 +87,20 @@ public class Configuration {
}
public static class Discord {
public boolean enabled = true;
public boolean enabled = false;
public String prefix = "default!";
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 IRC {
public boolean enabled = true;
public boolean enabled = false;
public String prefix = "!";
public String host;
public int port;
@ -169,6 +165,7 @@ public class Configuration {
public boolean creayun = false;
public String serverName;
public boolean useCore = true;
public boolean useCorePlaceBlock = false;
public boolean useChat = false;
public boolean coreCommandSpy = false;
public boolean resolveSRV = true;

View file

@ -2,26 +2,21 @@ package me.chayapak1.chomens_bot;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.chayapak1.chomens_bot.plugins.ConsolePlugin;
import me.chayapak1.chomens_bot.util.HttpUtilities;
import me.chayapak1.chomens_bot.util.LoggerUtilities;
import me.chayapak1.chomens_bot.util.PersistentDataUtilities;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.requests.GatewayIntent;
import me.chayapak1.chomens_bot.plugins.DiscordPlugin;
import me.chayapak1.chomens_bot.plugins.IRCPlugin;
import me.chayapak1.chomens_bot.plugins.LoggerPlugin;
import me.chayapak1.chomens_bot.util.*;
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
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.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@ -43,9 +38,11 @@ public class Main {
private static boolean alreadyStarted = false;
private static final List<Thread> alreadyAddedThreads = new ArrayList<>();
private static boolean stopping = false;
private static JDA jda = null;
private static int backupFailTimes = 0;
private static DiscordPlugin discord;
public static void main(String[] args) throws IOException {
final Path configPath = Path.of("config.yml");
@ -72,7 +69,9 @@ public class Main {
configWriter.write(defaultConfig);
configWriter.close();
LoggerUtilities.info("config.yml file not found, so the default one was created");
LoggerUtilities.info("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);
@ -80,82 +79,61 @@ public class Main {
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;
} else {
executor.scheduleAtFixedRate(() -> {
boolean reachable;
try {
HttpUtilities.getRequest(new URL(config.backup.address));
reachable = true;
} catch (Exception e) {
reachable = false;
}
if (!reachable && !alreadyStarted) {
backupFailTimes++;
if (backupFailTimes > config.backup.failTimes) {
LoggerUtilities.info("Main instance is down! Starting backup instance");
initializeBots();
}
} else if (reachable && alreadyStarted) {
LoggerUtilities.info("Main instance is back up! Now stopping");
// no need to reset backupFailTimes because we are stopping anyway
stop();
}
}, 0, config.backup.interval, TimeUnit.MILLISECONDS);
}
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() {
alreadyStarted = true;
try {
Configuration.BotOption[] botsOptions = config.bots;
// idk if these should be here lol, but it is just the discord stuff
if (config.discord.enabled) {
JDABuilder builder = JDABuilder.createDefault(config.discord.token);
builder.enableIntents(GatewayIntent.MESSAGE_CONTENT);
try {
jda = builder.build();
jda.awaitReady();
} catch (InterruptedException ignored) {
System.exit(1);
}
jda.getPresence().setPresence(Activity.playing(config.discord.statusMessage), false);
}
final Configuration.BotOption[] botsOptions = config.bots;
for (Configuration.BotOption botOption : botsOptions) {
final Bot bot = new Bot(botOption, bots, config);
bots.add(bot);
}
// fard
new ConsolePlugin(bots, config, jda);
// initialize util classes and plugins
PersistentDataUtilities.init();
new ConsolePlugin();
LoggerPlugin.init();
if (config.discord.enabled) discord = new DiscordPlugin(config);
if (config.irc.enabled) new IRCPlugin(config);
LoggerUtilities.info("Initialized all bots. Now connecting");
for (Bot bot : bots) bot.connect();
} catch (Exception e) {
e.printStackTrace();
@ -163,30 +141,12 @@ public class Main {
}
}
private static void checkInternet () throws IOException {
if (!config.internetCheck.enabled) return;
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);
}
}
// most of these are stolen from HBot
public static void stop () {
if (stopping) return;
stopping = true;
executor.shutdown();
PersistentDataUtilities.stop();
@ -194,11 +154,9 @@ public class Main {
executorService.shutdown();
try {
final boolean executorDone = executor.awaitTermination(5, TimeUnit.SECONDS);
final boolean executorServiceDone = executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
final boolean ignoredExecutorDone = executor.awaitTermination(5, TimeUnit.SECONDS);
final boolean ignoredExecutorServiceDone = executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {}
ArrayList<Bot> copiedList;
synchronized (bots) {
@ -208,46 +166,42 @@ public class Main {
final boolean ircEnabled = config.irc.enabled;
final boolean discordEnabled = config.discord.enabled;
final boolean[] stoppedDiscord = new boolean[copiedList.size()];
int botIndex = 0;
for (Bot bot : copiedList) {
if (discordEnabled) {
final String channelId = bot.discord.servers.get(bot.host + ":" + bot.port);
try {
if (discordEnabled) {
final String channelId = bot.discord.servers.get(bot.host + ":" + bot.port);
bot.discord.sendMessageInstantly("Stopping..", channelId);
}
final MessageCreateAction messageAction = bot.discord.sendMessageInstantly("Stopping..", channelId, false);
if (ircEnabled) {
bot.irc.quit("Stopping..");
}
final int finalBotIndex = botIndex;
messageAction.queue(
(message) -> stoppedDiscord[finalBotIndex] = true,
(error) -> stoppedDiscord[finalBotIndex] = true // should i also set this to true on fail?
);
}
bot.stop();
if (ircEnabled) bot.irc.quit("Stopping..");
bot.stop();
} catch (Exception ignored) {}
botIndex++;
}
if (jda != null) jda.shutdown();
if (discordEnabled) {
discord.jda.shutdown();
for (int i = 0; i < 150; i++) {
boolean stoppedDiscord = true;
for (Bot bot : bots) {
if (!bot.discord.shuttedDown) {
stoppedDiscord = false;
break;
}
}
if (!stoppedDiscord) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
} else {
break;
}
try {
if (!ArrayUtilities.isAllTrue(stoppedDiscord)) Thread.sleep(50);
else break;
} catch (InterruptedException ignored) {}
}
}
System.exit(69); // nice
System.exit(0);
}
}

View file

@ -1,5 +1,6 @@
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;
@ -38,7 +39,7 @@ public class CreayunChatParser implements ChatParser {
final String contents = matcher.group(2);
PlayerEntry sender = bot.players.getEntry(displayName);
if (sender == null) sender = new PlayerEntry(new GameProfile(new UUID(0L, 0L), displayName), GameMode.SURVIVAL, 0, Component.text(displayName), 0L, null, new byte[0], true);
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));
}

View file

@ -1,5 +1,7 @@
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;
@ -43,9 +45,12 @@ public class KaboomChatParser implements ChatParser {
return null;
}
// 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(new UUID(0L, 0L), null), GameMode.SURVIVAL, 0, displayName, 0L, null, new byte[0], true); // new and currently using
// 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;
return new PlayerMessage(sender, displayName, contents);
}

View file

@ -4,9 +4,9 @@ import net.kyori.adventure.text.Component;
public abstract class Command {
public final String name;
public final String description;
public final String[] usages;
public final String[] aliases;
public String description;
public String[] usages;
public String[] aliases;
public final TrustLevel trustLevel;
public final boolean consoleOnly;

View file

@ -3,5 +3,6 @@ package me.chayapak1.chomens_bot.command;
public enum TrustLevel {
PUBLIC,
TRUSTED,
ADMIN,
OWNER
}

View file

@ -52,11 +52,12 @@ public class CloopCommand extends Command {
try {
final int index = context.getInteger(true);
bot.cloop.remove(index);
final CommandLoop cloop = bot.cloop.remove(index);
return Component.translatable(
"Removed cloop %s",
Component.text(index).color(ColorUtilities.getColorByString(bot.config.colorPalette.number))
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"));
@ -96,9 +97,7 @@ public class CloopCommand extends Command {
Component.join(JoinConfiguration.newlines(), cloopsComponent)
);
}
default -> {
throw new CommandException(Component.text("Invalid action"));
}
default -> throw new CommandException(Component.text("Invalid action"));
}
}
}

View file

@ -103,7 +103,7 @@ public class CommandBlockCommand extends Command {
if (future == null) return;
future.thenApply(output -> {
future.thenApplyAsync(output -> {
context.sendOutput(output);
return output;

View file

@ -19,7 +19,8 @@ public class ConsoleCommand extends Command {
"Controls stuff about console",
new String[] {
"server <server>",
"logtoconsole <true|false>"
"logtoconsole <true|false>",
"printdisconnectedreason <true|false>"
},
new String[] {},
TrustLevel.OWNER,
@ -47,8 +48,6 @@ public class ConsoleCommand extends Command {
if (server.equalsIgnoreCase("all")) {
eachBot.console.consoleServer = "all";
context.sendOutput(Component.text("Set the console server to all servers").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor)));
continue;
}
@ -56,9 +55,8 @@ public class ConsoleCommand extends Command {
// servers.find(server => server.toLowerCase().includes(args.join(' '))) in js i guess
eachBot.console.consoleServer = servers.stream()
.filter(eachServer -> eachServer.toLowerCase().contains(server))
.toArray(String[]::new)[0];
context.sendOutput(Component.text("Set the console server to " + bot.console.consoleServer).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor)));
.findFirst()
.orElse("all");
} catch (ArrayIndexOutOfBoundsException e) {
throw new CommandException(Component.text("Invalid server: " + server));
}
@ -76,6 +74,18 @@ public class ConsoleCommand extends Command {
bool ? Component.text("enabled").color(NamedTextColor.GREEN) : Component.text("disabled").color(NamedTextColor.RED)
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
case "printdisconnectedreason" -> {
context.checkOverloadArgs(2);
final boolean bool = context.getBoolean(true);
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));
}
}
return null;

View file

@ -38,7 +38,7 @@ public class EvalCommand extends Command {
final CompletableFuture<EvalOutput> future = bot.eval.run(command);
future.thenApply((output) -> {
future.thenApplyAsync(output -> {
if (output.isError()) context.sendOutput(Component.text(output.output()).color(NamedTextColor.RED));
else context.sendOutput(Component.text(output.output()));

View file

@ -32,7 +32,7 @@ public class FilterCommand extends Command {
"list"
},
new String[] { "filterplayer", "ban", "blacklist" },
TrustLevel.OWNER,
TrustLevel.ADMIN,
false
);
}
@ -101,11 +101,25 @@ public class FilterCommand extends Command {
for (JsonElement playerElement : FilterPlugin.filteredPlayers) {
final FilteredPlayer player = gson.fromJson(playerElement, FilteredPlayer.class);
Component options = Component.empty().color(NamedTextColor.DARK_GRAY);
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"));
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(")"));
}
filtersComponents.add(
Component.translatable(
"%s %s",
"%s %s %s",
Component.text(index).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
Component.text(player.playerName).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
Component.text(player.playerName).color(ColorUtilities.getColorByString(bot.config.colorPalette.username)),
options
).color(NamedTextColor.DARK_GRAY)
);

View file

@ -0,0 +1,95 @@
package me.chayapak1.chomens_bot.commands;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
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.TrustLevel;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.plugins.PlayersPersistentDataPlugin;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
public class FindAltsCommand extends Command {
public FindAltsCommand() {
super(
"findalts",
"Finds players with the same IP address",
new String[] { "<player>", "<ip>" },
new String[] { "alts", "sameip" },
TrustLevel.PUBLIC,
false
);
}
@Override
public Component execute(CommandContext context) throws Exception {
final Bot bot = context.bot;
final String player = context.getString(true, true);
final PlayerEntry playerEntry = bot.players.getEntry(player);
if (playerEntry == null) return handle(bot, player, true, player);
else {
final CompletableFuture<String> future = bot.players.getPlayerIP(playerEntry);
if (future == null) return null;
future.thenApplyAsync(targetIP -> {
context.sendOutput(handle(bot, targetIP, false, player));
return targetIP;
});
}
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");
if (ipsObject == null) return false;
final JsonElement currentServerIP = ipsObject.get(bot.host + ":" + bot.port);
if (currentServerIP == null) return false;
return currentServerIP.getAsString().equals(targetIP);
}
)
.map(Map.Entry::getKey);
Component component = Component
.translatable("Possible alts for the %s %s:")
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
.arguments(
Component.text(argumentIsIP ? "IP" : "player"),
Component.text(player).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
)
.appendNewline();
int i = 0;
for (String name : matches.toList()) {
component = component
.append(
Component
.text(name)
.color((i++ & 1) == 0 ? ColorUtilities.getColorByString(bot.config.colorPalette.primary) : ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
)
.appendSpace();
}
return component;
}
}

View file

@ -50,6 +50,7 @@ public class HelpCommand extends Command {
final List<Component> list = new ArrayList<>();
list.addAll(getCommandListByTrustLevel(TrustLevel.PUBLIC));
list.addAll(getCommandListByTrustLevel(TrustLevel.TRUSTED));
list.addAll(getCommandListByTrustLevel(TrustLevel.ADMIN));
list.addAll(getCommandListByTrustLevel(TrustLevel.OWNER));
return Component.empty()
@ -60,7 +61,8 @@ public class HelpCommand extends Command {
.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("Admin ").color(NamedTextColor.DARK_RED))
.append(Component.text("Owner").color(NamedTextColor.LIGHT_PURPLE))
.append(Component.text(") - ").color(NamedTextColor.DARK_GRAY))
.append(Component.join(JoinConfiguration.separator(Component.space()), list));
}
@ -98,7 +100,8 @@ public class HelpCommand extends Command {
return switch (trustLevel) {
case PUBLIC -> NamedTextColor.GREEN;
case TRUSTED -> NamedTextColor.RED;
case OWNER -> NamedTextColor.DARK_RED;
case ADMIN -> NamedTextColor.DARK_RED;
case OWNER -> NamedTextColor.LIGHT_PURPLE;
};
}

View file

@ -27,7 +27,7 @@ public class IPFilterCommand extends Command {
"list"
},
new String[] { "filterip", "banip", "ipban" },
TrustLevel.OWNER,
TrustLevel.ADMIN,
false
);
}

View file

@ -10,9 +10,12 @@ 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 net.kyori.adventure.text.format.Style;
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;
@ -21,21 +24,38 @@ 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.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
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 (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,
@ -49,7 +69,7 @@ public class InfoCommand extends Command {
final Bot bot = context.bot;
final String action = context.getString(false, true, true);
final String action = context.getString(false, false, true);
switch (action) {
case "creator" -> {
@ -179,6 +199,30 @@ public class InfoCommand extends Command {
.color(ColorUtilities.getColorByString(bot.config.colorPalette.uuid))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
case "botlogintime" -> {
final long loginTime = bot.loginTime;
final Instant instant = Instant.ofEpochMilli(loginTime);
final ZoneId zoneId = ZoneId.of("UTC");
final OffsetDateTime localDateTime = OffsetDateTime.ofInstant(instant, zoneId);
final DateTimeFormatter loginTimeFormatter = DateTimeFormatter.ofPattern("MMMM d, yyyy, hh:mm:ss a Z");
final String formattedLoginTime = localDateTime.format(loginTimeFormatter);
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(
"The bots login time is %s and has been on the server for %s",
Component
.text(formattedLoginTime)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component
.text(formattedTimeSince)
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
case "uptime" -> {
final long uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000;
@ -199,7 +243,60 @@ public class InfoCommand extends Command {
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
default -> {
throw new CommandException(Component.text("Invalid action"));
return Component.empty()
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
.append(Component.text("ChomeNS Bot").color(NamedTextColor.YELLOW))
.append(Component.space())
.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."
)
.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)
)
)
.append(Component.newline())
.append(Component.text("Compiled at "))
.append(
Component
.text(BUILD_INFO.getProperty("build.date", "unknown"))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
)
.append(
Component
.translatable(
", Git commit %s (%s)",
Component
.text(BUILD_INFO.getProperty("build.git.commit.hash", "unknown"))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component
.text(BUILD_INFO.getProperty("build.git.commit.count", "unknown"))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.number))
)
)
.append(Component.newline())
.append(
Component
.translatable(
"Build %s",
Component
.text(BUILD_INFO.getProperty("build.number", "unknown"))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.number))
)
);
}
}
}

View file

@ -6,9 +6,11 @@ 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.exploitMethods.Kick;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import java.util.Arrays;
import java.util.UUID;
public class KickCommand extends Command {
@ -16,17 +18,23 @@ public class KickCommand extends Command {
super(
"kick",
"Kicks a player",
new String[] { "<player>" },
new String[] {},
new String[] {},
TrustLevel.TRUSTED,
false
);
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 {
final Bot bot = context.bot;
final Kick method = context.getEnum(Kick.class);
final PlayerEntry entry = bot.players.getEntry(context.getString(true, true));
if (entry == null) throw new CommandException(Component.text("Invalid player name"));
@ -34,11 +42,16 @@ public class KickCommand extends Command {
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("Kicking player"))
.append(Component.space())
.append(Component.text(name).color(ColorUtilities.getColorByString(bot.config.colorPalette.username)))
.append(Component.space())
.append(Component.text("with method"))
.append(Component.space())
.append(Component.text(method.name()).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)))
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
}

View file

@ -16,14 +16,15 @@ import me.chayapak1.chomens_bot.util.PersistentDataUtilities;
import me.chayapak1.chomens_bot.util.UUIDUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
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;
@ -105,7 +106,7 @@ public class MailCommand extends Command {
throw new CommandException(Component.text("There was an error while sending your mail"));
}
future.thenApply(output -> {
future.thenApplyAsync(output -> {
try {
final List<Component> children = output.children();
@ -174,8 +175,12 @@ public class MailCommand extends Command {
if (!mail.sentTo.equals(sender.profile.getName())) continue;
final DateTimeFormatter formatter = DateTimeFormat.forPattern("MMMM d, YYYY, hh:mm:ss a Z");
final String formattedTime = formatter.print(mail.timeSent);
final Instant instant = Instant.ofEpochMilli(mail.timeSent);
final ZoneId zoneId = ZoneId.systemDefault();
final OffsetDateTime localDateTime = OffsetDateTime.ofInstant(instant, zoneId);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy, hh:mm:ss a Z");
final String formattedTime = localDateTime.format(formatter);
mailsComponent.add(
Component.translatable(

View file

@ -12,7 +12,6 @@ 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.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TranslatableComponent;
@ -36,8 +35,6 @@ import static me.chayapak1.chomens_bot.util.StringUtilities.isNotNullAndNotBlank
public class MusicCommand extends Command {
private Path root;
private int ratelimit = 0;
public MusicCommand () {
super(
"music",
@ -63,15 +60,11 @@ public class MusicCommand extends Command {
TrustLevel.PUBLIC,
false
);
Main.executor.scheduleAtFixedRate(() -> ratelimit = 0, 0, 5, TimeUnit.SECONDS);
}
@Override
public Component execute(CommandContext context) throws CommandException {
ratelimit++;
if (ratelimit > 10) return null;
if (context.bot.music.locked) throw new CommandException(Component.text("Managing music is currently locked"));
final String action = context.getString(false, true, true);
@ -213,7 +206,7 @@ public class MusicCommand extends Command {
throw new CommandException(Component.text("There was an error while getting your data"));
}
future.thenApply(output -> {
future.thenApplyAsync(output -> {
final List<Component> children = output.children();
if (
@ -264,14 +257,14 @@ public class MusicCommand extends Command {
final CompletableFuture<Component> future = bot.core.runTracked(
"minecraft:data get entity " +
UUIDUtilities.selector(context.sender.profile.getId()) +
" SelectedItem.tag.SongItemData.SongData"
" SelectedItem.components.minecraft:custom_data.SongItemData.SongData"
);
if (future == null) {
throw new CommandException(Component.text("There was an error while getting your data"));
}
future.thenApply(output -> {
future.thenApplyAsync(output -> {
final List<Component> children = output.children();
if (
@ -281,7 +274,7 @@ public class MusicCommand extends Command {
.key()
.equals("arguments.nbtpath.nothing_found")
) {
context.sendOutput(Component.text("Player has no SongItemData -> SongData NBT tag in the selected item").color(NamedTextColor.RED));
context.sendOutput(Component.text("Player has no SongItemData -> SongData NBT tag in their selected item's minecraft:custom_data").color(NamedTextColor.RED));
return output;
}

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

@ -10,11 +10,13 @@ import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.plugins.PlayersPersistentDataPlugin;
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 org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class SeenCommand extends Command {
public SeenCommand () {
@ -34,30 +36,44 @@ public class SeenCommand extends Command {
final String player = context.getString(true, true);
boolean online = false;
final List<Component> onlineComponents = new ArrayList<>();
for (Bot eachBot : bot.bots) {
if (eachBot.players.getEntry(player) != null) return Component.empty()
.append(Component.text(player))
.append(Component.text(" is currently online on "))
.append(Component.text(eachBot.host + ":" + eachBot.port))
.color(NamedTextColor.RED);
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)
);
}
}
if (online) return Component.join(JoinConfiguration.newlines(), onlineComponents);
final JsonElement playerElement = PlayersPersistentDataPlugin.playersObject.get(player);
if (playerElement == null) return Component.translatable(
if (playerElement == null) throw new CommandException(Component.translatable(
"%s was never seen",
Component.text(player)
).color(NamedTextColor.RED);
));
final JsonObject lastSeen = playerElement.getAsJsonObject().get("lastSeen").getAsJsonObject();
final JsonElement time = lastSeen.get("time");
if (time == null) throw new CommandException(Component.text("time is null.. (possible invalid 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 DateTime dateTime = new DateTime(time.getAsLong(), DateTimeZone.UTC);
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);
final DateTimeFormatter formatter = DateTimeFormat.forPattern("EEEE, MMMM d, YYYY, hh:mm:ss a z");
final String formattedTime = formatter.print(dateTime);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE, MMMM d, yyyy, hh:mm:ss a Z");
final String formattedTime = localDateTime.format(formatter);
final String server = lastSeen.get("server").getAsString();

View file

@ -26,24 +26,28 @@ public class ServerEvalCommand extends Command {
@Override
public Component execute(CommandContext context) throws CommandException {
try {
final Bot bot = context.bot;
final Bot bot = context.bot;
final Globals globals = JsePlatform.standardGlobals();
bot.executorService.submit(() -> {
try {
final Globals globals = JsePlatform.standardGlobals();
globals.set("bot", CoerceJavaToLua.coerce(bot));
globals.set("class", CoerceJavaToLua.coerce(Class.class));
globals.set("context", CoerceJavaToLua.coerce(context));
globals.set("bot", CoerceJavaToLua.coerce(bot));
globals.set("class", CoerceJavaToLua.coerce(Class.class));
globals.set("context", CoerceJavaToLua.coerce(context));
LuaValue chunk = globals.load(context.getString(true, true));
LuaValue chunk = globals.load(context.getString(true, true));
final LuaValue output = chunk.call();
final LuaValue output = chunk.call();
return Component.text(output.toString()).color(NamedTextColor.GREEN);
} catch (CommandException e) {
throw e;
} catch (Exception e) {
throw new CommandException(Component.text(e.toString()));
}
context.sendOutput(Component.text(output.toString()).color(NamedTextColor.GREEN));
} catch (CommandException e) {
context.sendOutput(e.message.color(NamedTextColor.RED));
} catch (Exception e) {
context.sendOutput(Component.text(e.toString()).color(NamedTextColor.RED));
}
});
return null;
}
}

View file

@ -8,10 +8,11 @@ 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;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.time.DateTimeException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class TimeCommand extends Command {
public TimeCommand () {
@ -27,26 +28,24 @@ public class TimeCommand extends Command {
@Override
public Component execute(CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String timezone = context.getString(true, true);
DateTimeZone zone;
try {
zone = DateTimeZone.forID(timezone);
} catch (IllegalArgumentException ignored) {
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);
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)"));
}
final DateTime dateTime = new DateTime(zone);
final DateTimeFormatter formatter = DateTimeFormat.forPattern("EEEE, MMMM d, YYYY, hh:mm:ss a");
final String formattedTime = formatter.print(dateTime);
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));
}
}

View file

@ -24,10 +24,15 @@ public class ValidateCommand extends Command {
public Component execute(CommandContext context) throws CommandException {
final Bot bot = context.bot;
final String hash = context.fullArgs[0];
final String[] fullArgs = context.fullArgs;
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);
if (fullArgs.length == 0) return null;
final String hash = fullArgs[0];
if (bot.hashing.isCorrectHash(hash, context.userInputCommandName, context.sender)) return Component.text("Valid trusted hash").color(NamedTextColor.GREEN);
else if (bot.hashing.isCorrectAdminHash(hash, context.userInputCommandName, context.sender)) return Component.text("Valid admin hash").color(NamedTextColor.GREEN);
else if (bot.hashing.isCorrectOwnerHash(hash, context.userInputCommandName, context.sender)) return Component.text("Valid owner hash").color(NamedTextColor.GREEN);
return null;
}

View file

@ -11,14 +11,13 @@ 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 org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
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 () {
@ -53,13 +52,11 @@ public class WeatherCommand extends Command {
final JsonObject jsonObject = gson.fromJson(jsonOutput, JsonObject.class);
final DateTimeFormatter formatter = DateTimeFormat.forPattern("hh:mm:ss a");
final ZoneId zoneId = ZoneId.of(jsonObject.get("location").getAsJsonObject().get("tz_id").getAsString());
final ZonedDateTime zonedDateTime = ZonedDateTime.now(zoneId);
final DateTimeZone zone = DateTimeZone.forID(jsonObject.get("location").getAsJsonObject().get("tz_id").getAsString());
final DateTime dateTime = new DateTime(zone);
final String time = formatter.print(dateTime);
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",

View file

@ -20,7 +20,7 @@ public class WhitelistCommand extends Command {
"Manages whitelist",
new String[] { "enable", "disable", "add <player>", "remove <index>", "clear", "list" },
new String[] {},
TrustLevel.OWNER,
TrustLevel.ADMIN,
false
);
}
@ -57,18 +57,18 @@ public class WhitelistCommand extends Command {
).color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
}
case "remove" -> {
final String player = context.getString(true, true);
try {
final int index = context.getInteger(true);
if (!bot.whitelist.list.contains(player)) throw new CommandException(Component.text("Player doesn't exist in the list"));
final String player = bot.whitelist.remove(index);
if (player.equals(bot.profile.getName())) throw new CommandException(Component.text("Cannot remove the bot"));
bot.whitelist.remove(player);
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));
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"));
}
}
case "clear" -> {
context.checkOverloadArgs(1);

View file

@ -15,7 +15,7 @@ public class EvalFunction {
this.bot = bot;
}
public Output execute (Object ...args) { return null; }
public Output execute (Object ...args) throws Exception { return null; }
public static class Output {
public final String message;

View file

@ -10,6 +10,8 @@ public class ChatFunction extends EvalFunction {
@Override
public Output execute(Object... args) {
if (args.length == 0) return null;
final String message = (String) args[0];
bot.chat.send(message);

View file

@ -2,6 +2,11 @@ package me.chayapak1.chomens_bot.evalFunctions;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class CoreFunction extends EvalFunction {
public CoreFunction (Bot bot) {
@ -9,11 +14,13 @@ public class CoreFunction extends EvalFunction {
}
@Override
public Output execute(Object... args) {
public Output execute(Object... args) throws Exception {
if (args.length == 0) return null;
final String command = (String) args[0];
bot.core.run(command);
final CompletableFuture<Component> future = bot.core.runTracked(command);
return null;
return new Output(GsonComponentSerializer.gson().serialize(future.get(1, TimeUnit.SECONDS)), true);
}
}

View file

@ -10,6 +10,8 @@ public class CorePlaceBlockFunction extends EvalFunction {
@Override
public Output execute(Object... args) {
if (args.length == 0) return null;
final String command = (String) args[0];
bot.core.runPlaceBlock(command);

View file

@ -0,0 +1,23 @@
package me.chayapak1.chomens_bot.evalFunctions;
import com.google.gson.JsonObject;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
public class GetBotInfoFunction extends EvalFunction {
public GetBotInfoFunction(Bot bot) {
super("getBotInfo", bot);
}
@Override
public Output execute(Object... args) {
final JsonObject object = new JsonObject();
object.addProperty("usernane", bot.username);
object.addProperty("host", bot.host);
object.addProperty("port", bot.port);
object.addProperty("loggedIn", bot.loggedIn);
return new Output(object.toString(), true);
}
}

View file

@ -0,0 +1,33 @@
package me.chayapak1.chomens_bot.evalFunctions;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
import me.chayapak1.chomens_bot.plugins.ChatPlugin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
public class GetLatestChatMessageFunction extends EvalFunction {
private String latestMessage = "";
public GetLatestChatMessageFunction (Bot bot) {
super("getLatestChatMessage", bot);
bot.chat.addListener(new ChatPlugin.Listener() {
@Override
public boolean systemMessageReceived(Component component, String string, String ansi) {
messageReceived(component);
return true;
}
});
}
private void messageReceived (Component component) {
latestMessage = GsonComponentSerializer.gson().serialize(component);
}
@Override
public Output execute(Object... args) {
return new Output(latestMessage, true);
}
}

View file

@ -23,6 +23,8 @@ public class AuthPlugin extends PlayersPlugin.Listener {
private boolean hasCorrectHash;
private PlayerEntry targetPlayer;
private boolean started = false;
public AuthPlugin (Bot bot) {
@ -39,7 +41,7 @@ public class AuthPlugin extends PlayersPlugin.Listener {
return AuthPlugin.this.systemMessageReceived(component);
}
});
bot.executor.scheduleAtFixedRate(this::check, 0, 1, TimeUnit.SECONDS);
bot.executor.scheduleAtFixedRate(this::check, 1, 3, TimeUnit.SECONDS);
}
private String getSanitizedOwnerName() {
@ -50,6 +52,8 @@ public class AuthPlugin extends PlayersPlugin.Listener {
public void playerJoined(PlayerEntry target) {
if (!target.profile.getName().equals(getSanitizedOwnerName()) || !bot.options.useCore) return;
targetPlayer = target;
bot.executor.schedule(() -> sendVerificationMessage(target, true), 2, TimeUnit.SECONDS);
}
@ -110,6 +114,8 @@ public class AuthPlugin extends PlayersPlugin.Listener {
hasCorrectHash = inputHash.equals(hash);
if (hasCorrectHash && targetPlayer != null) bot.chat.tellraw(Component.text("You have been verified").color(NamedTextColor.GREEN), targetPlayer.profile.getId());
return false;
} catch (Exception ignored) {}
@ -117,7 +123,7 @@ public class AuthPlugin extends PlayersPlugin.Listener {
}
private void check() {
if (!started) return;
if (!started || !bot.config.ownerAuthentication.enabled) return;
final PlayerEntry entry = bot.players.getEntry(getSanitizedOwnerName());

View file

@ -5,32 +5,37 @@ import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import java.util.concurrent.TimeUnit;
public class BruhifyPlugin extends TickPlugin.Listener {
private final Bot bot;
public class BruhifyPlugin {
public String bruhifyText = "";
private int startHue = 0;
public BruhifyPlugin (Bot bot) {
bot.executor.scheduleAtFixedRate(() -> {
if (bruhifyText.isEmpty()) return;
this.bot = bot;
int hue = startHue;
String displayName = bruhifyText;
int increment = 360 / Math.max(displayName.length(), 20);
bot.tick.addListener(this);
}
Component component = Component.empty();
@Override
public void onTick() {
if (bruhifyText.isEmpty()) return;
for (char character : displayName.toCharArray()) {
String color = String.format("#%06x", ColorUtilities.hsvToRgb(hue, 100, 100));
component = component.append(Component.text(character).color(TextColor.fromHexString(color)));
hue = (hue + increment) % 360;
}
int hue = startHue;
String displayName = bruhifyText;
int increment = 360 / Math.max(displayName.length(), 20);
bot.chat.actionBar(component);
Component component = Component.empty();
startHue = (startHue + increment) % 360;
}, 0, 50, TimeUnit.MILLISECONDS);
for (char character : displayName.toCharArray()) {
String color = String.format("#%06x", ColorUtilities.hsvToRgb(hue, 100, 100));
component = component.append(Component.text(character).color(TextColor.fromHexString(color)));
hue = (hue + increment) % 360;
}
bot.chat.actionBar(component);
startHue = (startHue + increment) % 360;
}
}

View file

@ -58,11 +58,9 @@ public class ChatCommandHandlerPlugin extends ChatPlugin.Listener {
final PlayerCommandContext context = new PlayerCommandContext(bot, displayName, prefix, "@a", message.sender);
bot.executorService.submit(() -> {
final Component output = bot.commandHandler.executeCommand(commandString, context, null);
final Component output = bot.commandHandler.executeCommand(commandString, context, null);
if (output != null) context.sendOutput(output);
});
if (output != null) context.sendOutput(output);
return true;
}
@ -94,10 +92,8 @@ public class ChatCommandHandlerPlugin extends ChatPlugin.Listener {
final PlayerCommandContext context = new PlayerCommandContext(bot, displayName, prefix, selector, sender);
bot.executorService.submit(() -> {
final Component output = bot.commandHandler.executeCommand(commandString, context, null);
final Component output = bot.commandHandler.executeCommand(commandString, context, null);
if (output != null) context.sendOutput(output);
});
if (output != null) context.sendOutput(output);
}
}

View file

@ -26,14 +26,14 @@ public class CloopPlugin {
loopTasks.add(bot.executor.scheduleAtFixedRate(loopTask, 0, interval, TimeUnit.MILLISECONDS));
}
public void remove (int index) {
public CommandLoop remove (int index) {
ScheduledFuture<?> loopTask = loopTasks.remove(index);
if (loopTask != null) {
loopTask.cancel(true);
}
loops.remove(index);
return loops.remove(index);
}
public void clear () {

View file

@ -51,13 +51,13 @@ public class CommandHandlerPlugin {
registerCommand(new EvalCommand());
registerCommand(new InfoCommand());
registerCommand(new ConsoleCommand());
registerCommand(new PCrashCommand());
// registerCommand(new ScreenshareCommand());
registerCommand(new WhitelistCommand());
registerCommand(new SeenCommand());
registerCommand(new IPFilterCommand());
registerCommand(new StopCommand());
registerCommand(new GrepLogCommand());
registerCommand(new FindAltsCommand());
}
public boolean disabled = false;
@ -93,6 +93,8 @@ public class CommandHandlerPlugin {
final String[] splitInput = input.trim().split("\\s+");
if (splitInput.length == 0) return null;
final String commandName = splitInput[0];
final Command command = findCommand(commands, commandName);
@ -123,28 +125,43 @@ public class CommandHandlerPlugin {
final String trustedRoleName = bot.config.discord.trustedRoleName;
final String adminRoleName = bot.config.discord.adminRoleName;
final String ownerRoleName = bot.config.discord.ownerRoleName;
if (
command.trustLevel == TrustLevel.TRUSTED &&
roles.stream().noneMatch(role -> role.getName().equalsIgnoreCase(trustedRoleName)) &&
roles.stream().noneMatch(role -> role.getName().equalsIgnoreCase(adminRoleName))
roles.stream().noneMatch(role -> role.getName().equalsIgnoreCase(adminRoleName)) &&
roles.stream().noneMatch(role -> role.getName().equalsIgnoreCase(ownerRoleName))
) return Component.text("You're not in the trusted role!").color(NamedTextColor.RED);
if (
command.trustLevel == TrustLevel.OWNER &&
roles.stream().noneMatch(role -> role.getName().equalsIgnoreCase(adminRoleName))
command.trustLevel == TrustLevel.ADMIN &&
roles.stream().noneMatch(role -> role.getName().equalsIgnoreCase(adminRoleName)) &&
roles.stream().noneMatch(role -> role.getName().equalsIgnoreCase(ownerRoleName))
) return Component.text("You're not in the admin role!").color(NamedTextColor.RED);
if (
command.trustLevel == TrustLevel.OWNER &&
roles.stream().noneMatch(role -> role.getName().equalsIgnoreCase(ownerRoleName))
) return Component.text("You're not in the owner role!").color(NamedTextColor.RED);
} else {
if (
command.trustLevel == TrustLevel.TRUSTED &&
!bot.hashing.isCorrectHash(userHash, splitInput[0], context.sender) &&
!bot.hashing.isCorrectAdminHash(userHash, splitInput[0], context.sender) &&
!bot.hashing.isCorrectOwnerHash(userHash, splitInput[0], context.sender)
) return Component.text("Invalid hash").color(NamedTextColor.RED);
if (
command.trustLevel == TrustLevel.ADMIN &&
!bot.hashing.isCorrectAdminHash(userHash, splitInput[0], context.sender) &&
!bot.hashing.isCorrectOwnerHash(userHash, splitInput[0], context.sender)
) return Component.text("Invalid admin hash").color(NamedTextColor.RED);
if (
command.trustLevel == TrustLevel.OWNER &&
!bot.hashing.isCorrectOwnerHash(userHash, splitInput[0], context.sender)
) return Component.text("Invalid OwnerHash").color(NamedTextColor.RED);
) return Component.text("Invalid owner hash").color(NamedTextColor.RED);
}
}

View file

@ -1,17 +1,14 @@
package me.chayapak1.chomens_bot.plugins;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Configuration;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.Command;
import me.chayapak1.chomens_bot.command.ConsoleCommandContext;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.dv8tion.jda.api.JDA;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.jline.reader.*;
import java.util.ArrayList;
import java.util.List;
public class ConsolePlugin implements Completer {
@ -21,12 +18,10 @@ public class ConsolePlugin implements Completer {
public String consoleServer = "all";
public String prefix;
private String prefix;
private static final List<Listener> listeners = new ArrayList<>();
public ConsolePlugin (List<Bot> allBots, Configuration config, JDA jda) {
this.allBots = allBots;
public ConsolePlugin () {
this.allBots = Main.bots;
this.reader = LineReaderBuilder
.builder()
.completer(this)
@ -38,13 +33,8 @@ public class ConsolePlugin implements Completer {
prefix = bot.config.consoleCommandPrefix;
bot.console = this;
bot.logger = new LoggerPlugin(bot);
}
new DiscordPlugin(config, jda);
if (config.irc.enabled) new IRCPlugin(config);
final String prompt = "> ";
Main.executorService.submit(() -> {
@ -59,8 +49,6 @@ public class ConsolePlugin implements Completer {
handleLine(line);
}
});
for (Listener listener : listeners) { listener.ready(); }
}
@Override
@ -82,7 +70,7 @@ public class ConsolePlugin implements Completer {
candidates.addAll(filteredCommands);
}
public void handleLine (String line) {
private void handleLine (String line) {
if (line == null) return;
for (Bot bot : allBots) {
@ -112,10 +100,4 @@ public class ConsolePlugin implements Completer {
);
}
}
public static void addListener (Listener listener) { listeners.add(listener); }
public static class Listener {
public void ready () {}
}
}

View file

@ -30,10 +30,7 @@ import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.cloudburstmc.math.vector.Vector3i;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -55,6 +52,8 @@ public class CorePlugin extends PositionPlugin.Listener {
public Vector3i block = null;
public final List<String> placeBlockQueue = Collections.synchronizedList(new ArrayList<>());
private int nextTransactionId = 0;
private final Map<Integer, CompletableFuture<Component>> transactions = new HashMap<>();
@ -120,6 +119,27 @@ public class CorePlugin extends PositionPlugin.Listener {
return CorePlugin.this.systemMessageReceived(component);
}
});
bot.tick.addListener(new TickPlugin.Listener() {
@Override
public void onTick() {
try {
final List<String> clonedQueue = new ArrayList<>(placeBlockQueue);
if (clonedQueue.isEmpty()) return;
if (clonedQueue.size() > 500) {
placeBlockQueue.clear();
return;
}
forceRunPlaceBlock(clonedQueue.get(0));
placeBlockQueue.remove(0);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public boolean hasRateLimit () {
@ -171,6 +191,11 @@ public class CorePlugin extends PositionPlugin.Listener {
if (!ready || command.length() > 32767) return;
if (bot.options.useCore) {
if (bot.options.useCorePlaceBlock) {
runPlaceBlock(command);
return;
}
if (isRateLimited() && hasRateLimit()) return;
forceRun(command);
@ -185,6 +210,11 @@ public class CorePlugin extends PositionPlugin.Listener {
public CompletableFuture<Component> runTracked (String command) {
if (!bot.options.useCore) return null;
if (bot.options.useCorePlaceBlock) {
runPlaceBlock(command);
return null;
}
final Vector3i coreBlock = block;
run(command);
@ -239,7 +269,7 @@ public class CorePlugin extends PositionPlugin.Listener {
final String stringLastOutput = ((TextComponent) children.get(1)).content();
future.complete(Component.join(JoinConfiguration.separator(Component.space()), GsonComponentSerializer.gson().deserialize(stringLastOutput).children()));
future.complete(Component.join(JoinConfiguration.separator(Component.empty()), GsonComponentSerializer.gson().deserialize(stringLastOutput).children()));
return false;
} catch (ClassCastException | NumberFormatException ignored) {}
@ -248,6 +278,10 @@ public class CorePlugin extends PositionPlugin.Listener {
}
public void runPlaceBlock (String command) {
bot.executorService.submit(() -> placeBlockQueue.add(command));
}
private void forceRunPlaceBlock (String command) {
if (!ready || !bot.options.useCore) return;
final NbtMapBuilder blockEntityTagBuilder = NbtMap.builder();
@ -374,6 +408,8 @@ public class CorePlugin extends PositionPlugin.Listener {
@Override
public void positionChange (Vector3i position) {
if (bot.position.isGoingDownFromHeightLimit) return;
from = Vector3i.from(
(int) (fromSize.getX() + Math.floor((double) bot.position.position.getX() / 16) * 16),
MathUtilities.clamp(fromSize.getY(), bot.world.minY, bot.world.maxY),

View file

@ -9,14 +9,14 @@ import me.chayapak1.chomens_bot.util.ColorUtilities;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.LoggerUtilities;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.events.session.ShutdownEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.event.ClickEvent;
@ -35,7 +35,7 @@ import java.util.*;
// please ignore my ohio code
// also this is one of the classes which has >100 lines or actually >400 LMAO
public class DiscordPlugin {
public final JDA jda;
public JDA jda;
public final Map<String, String> servers;
@ -49,11 +49,10 @@ public class DiscordPlugin {
public boolean shuttedDown = false;
public DiscordPlugin (Configuration config, JDA jda) {
public DiscordPlugin (Configuration config) {
final Configuration.Discord options = config.discord;
this.prefix = options.prefix;
this.servers = options.servers;
this.jda = jda;
this.discordUrl = config.discord.inviteLink;
this.messagePrefix = Component.empty()
.append(Component.text("ChomeNS ").color(NamedTextColor.YELLOW))
@ -65,8 +64,17 @@ public class DiscordPlugin {
)
.clickEvent(ClickEvent.openUrl(discordUrl));
final JDABuilder builder = JDABuilder.createDefault(config.discord.token);
builder.enableIntents(GatewayIntent.MESSAGE_CONTENT);
try {
jda = builder.build();
jda.awaitReady();
} catch (InterruptedException ignored) {}
if (jda == null) return;
jda.getPresence().setPresence(Activity.playing(config.discord.statusMessage), false);
for (Bot bot : Main.bots) {
final String channelId = servers.get(bot.host + ":" + bot.port);
@ -78,7 +86,9 @@ public class DiscordPlugin {
bot.tick.addListener(new TickPlugin.Listener() {
@Override
public void onAlwaysTick () {
onDiscordTick(channelId);
try {
onDiscordTick(channelId);
} catch (Exception ignored) {}
}
});
@ -353,24 +363,31 @@ public class DiscordPlugin {
}
}
public void sendMessageInstantly (String message, String channelId) {
if (jda == null) return;
public void sendMessageInstantly (String message, String channelId) { sendMessageInstantly(message, channelId, true); }
public MessageCreateAction sendMessageInstantly (String message, String channelId, boolean queue) {
if (jda == null) return null;
final TextChannel logChannel = jda.getTextChannelById(channelId);
if (logChannel == null) {
LoggerUtilities.error("Log channel for " + channelId + " is null");
return;
return null;
}
logChannel.sendMessage(message).queue(
(msg) -> doneSendingInLogs.put(channelId, true),
(err) -> {
err.printStackTrace();
if (queue) {
logChannel.sendMessage(message).queue(
(msg) -> doneSendingInLogs.put(channelId, true),
(err) -> {
err.printStackTrace();
doneSendingInLogs.put(channelId, false);
}
);
doneSendingInLogs.put(channelId, false);
}
);
return null;
} else {
return logChannel.sendMessage(message);
}
}
public void onDiscordTick(String channelId) {
@ -389,7 +406,7 @@ public class DiscordPlugin {
String message;
synchronized (logMessages) {
StringBuilder logMessage = logMessages.get(channelId);
message = logMessage.toString();
message = logMessage.toString().replace(".", "\u200b.\u200b"); // the ZWSP fixes discord.gg showing invite
final int maxLength = 2_000 - ("""
```ansi

View file

@ -7,10 +7,7 @@ import io.socket.client.Socket;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
import me.chayapak1.chomens_bot.data.eval.EvalOutput;
import me.chayapak1.chomens_bot.evalFunctions.ChatFunction;
import me.chayapak1.chomens_bot.evalFunctions.CoreFunction;
import me.chayapak1.chomens_bot.evalFunctions.CorePlaceBlockFunction;
import me.chayapak1.chomens_bot.evalFunctions.GetPlayerListFunction;
import me.chayapak1.chomens_bot.evalFunctions.*;
import java.util.ArrayList;
import java.util.HashMap;
@ -38,6 +35,8 @@ public class EvalPlugin {
functions.add(new CorePlaceBlockFunction(bot));
functions.add(new ChatFunction(bot));
functions.add(new GetPlayerListFunction(bot));
functions.add(new GetBotInfoFunction(bot));
functions.add(new GetLatestChatMessageFunction(bot));
try {
socket = IO.socket(bot.config.eval.address);
@ -61,16 +60,20 @@ public class EvalPlugin {
socket.on(Socket.EVENT_CONNECT_ERROR, (args) -> connected = false);
for (EvalFunction function : functions) {
socket.on(BRIDGE_PREFIX + function.name, args -> {
final EvalFunction.Output output = function.execute(args);
socket.on(BRIDGE_PREFIX + function.name, args -> new Thread(() -> {
try {
final EvalFunction.Output output = function.execute(args);
if (output == null) return;
if (output == null) return;
socket.emit("functionOutput:" + function.name, output.message, output.parseJSON);
});
socket.emit("functionOutput:" + function.name, output.message, output.parseJSON);
} catch (Exception ignored) {}
}).start());
}
socket.on("codeOutput", (args) -> {
if (args.length < 3) return;
final int id = (int) args[0];
final boolean isError = (boolean) args[1];
final String output = (String) args[2];

View file

@ -25,8 +25,8 @@ public class FilterPlugin extends PlayersPlugin.Listener {
private final Gson gson = new Gson();
static {
if (PersistentDataUtilities.jsonObject.has("filters")) {
filteredPlayers = PersistentDataUtilities.jsonObject.get("filters").getAsJsonArray();
if (PersistentDataUtilities.has("filters")) {
filteredPlayers = PersistentDataUtilities.get("filters").getAsJsonArray();
}
}
@ -55,55 +55,54 @@ public class FilterPlugin extends PlayersPlugin.Listener {
}
private FilteredPlayer getPlayer (String name) {
// mess
// also regex and ignorecase codes from greplog plugin
FilteredPlayer filteredPlayer = null;
for (JsonElement filteredPlayerElement : filteredPlayers) {
final FilteredPlayer _filteredPlayer = gson.fromJson(filteredPlayerElement, FilteredPlayer.class);
final FilteredPlayer filteredPlayer = gson.fromJson(filteredPlayerElement, FilteredPlayer.class);
if (_filteredPlayer.regex) {
Pattern pattern = null;
if (_filteredPlayer.ignoreCase) {
pattern = Pattern.compile(_filteredPlayer.playerName, Pattern.CASE_INSENSITIVE);
} else {
try {
pattern = Pattern.compile(_filteredPlayer.playerName);
} catch (Exception e) {
bot.chat.tellraw(Component.text(e.toString()).color(NamedTextColor.RED));
}
}
if (pattern == null) break;
if (pattern.matcher(name).find()) {
filteredPlayer = _filteredPlayer;
break;
}
} else {
if (_filteredPlayer.ignoreCase) {
if (_filteredPlayer.playerName.toLowerCase().equals(name)) {
filteredPlayer = _filteredPlayer;
break;
}
} else {
if (_filteredPlayer.playerName.equals(name)) {
filteredPlayer = _filteredPlayer;
break;
}
}
if (matchesPlayer(name, filteredPlayer)) {
return filteredPlayer;
}
}
return filteredPlayer;
return null;
}
private boolean matchesPlayer (String name, FilteredPlayer player) {
if (player.regex) {
final Pattern pattern = compilePattern(player);
return pattern != null && pattern.matcher(name).find();
} else {
return compareNames(name, player);
}
}
private Pattern compilePattern (FilteredPlayer player) {
try {
final int flags = player.ignoreCase ? Pattern.CASE_INSENSITIVE : 0;
return Pattern.compile(player.playerName, flags);
} catch (Exception e) {
bot.chat.tellraw(Component.text(e.toString()).color(NamedTextColor.RED));
return null;
}
}
private boolean compareNames (String name, FilteredPlayer player) {
final String playerName = player.ignoreCase ? player.playerName.toLowerCase() : player.playerName;
final String targetName = player.ignoreCase ? name.toLowerCase() : name;
return playerName.equals(targetName);
}
@Override
public void playerJoined (PlayerEntry target) {
final FilteredPlayer player = getPlayer(target.profile.getName());
bot.executorService.submit(() -> {
final FilteredPlayer player = getPlayer(target.profile.getName());
if (player == null) return;
if (player == null) return;
doAll(target);
doAll(target);
});
}
@Override

View file

@ -18,6 +18,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.regex.Pattern;
@ -104,10 +105,12 @@ public class GrepLogPlugin {
.queue(message -> {
final String url = message.getAttachments().get(0).getUrl();
final DecimalFormat formatter = new DecimalFormat("#,###");
final Component component = Component.translatable("Found %s matches for %s. You can see the results by clicking %s or in the Discord server.")
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
.arguments(
Component.text(matches).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
Component.text(formatter.format(matches)).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
Component.text(input).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
Component
.text("here")
@ -126,6 +129,9 @@ public class GrepLogPlugin {
context.sendOutput(component);
});
} catch (CommandException e) {
running = false;
throw e;
} catch (FileNotFoundException e) {
running = false;
throw new CommandException(Component.text("File not found"));

View file

@ -14,10 +14,13 @@ public class HashingPlugin {
this.bot = bot;
}
public String getHash (String prefix, PlayerEntry sender, boolean sectionSigns) {
final long time = System.currentTimeMillis() / 5_000;
public String getHash (String prefix, PlayerEntry sender, boolean sectionSigns) { return getGenericHash(bot.config.keys.trustedKey, prefix, sender, sectionSigns); }
public String getAdminHash (String prefix, PlayerEntry sender, boolean sectionSigns) { return getGenericHash(bot.config.keys.adminKey, prefix, sender, sectionSigns); }
public String getOwnerHash (String prefix, PlayerEntry sender, boolean sectionSigns) { return getGenericHash(bot.config.keys.ownerKey, prefix, sender, sectionSigns); }
final String key = bot.config.keys.normalKey;
// should this be public?
public String getGenericHash (String key, String prefix, PlayerEntry sender, boolean sectionSigns) {
final long time = System.currentTimeMillis() / 5_000;
final String hashValue = sender.profile.getIdAsString() + prefix + time + key;
@ -35,40 +38,25 @@ public class HashingPlugin {
hash;
}
public String getOwnerHash (String prefix, PlayerEntry sender, boolean sectionSigns) {
final long time = System.currentTimeMillis() / 5_000;
private boolean checkHash (String hash, String input, String prefix, PlayerEntry sender) {
// removes reset section sign
if (input.length() == (16 * 2 /* <-- don't forget, we have the section signs */) + 2 && input.endsWith("§r")) input = input.substring(0, input.length() - 2);
final String key = bot.config.keys.ownerKey;
final String value = sender.profile.getIdAsString() + prefix + time + key;
final String hash = Hashing.sha256()
.hashString(value, StandardCharsets.UTF_8)
.toString()
.substring(0, 16);
return sectionSigns ?
String.join("",
Arrays.stream(hash.split(""))
.map((letter) -> "§" + letter)
.toArray(String[]::new)
) :
hash;
return input.equals(hash);
}
public boolean isCorrectHash (String hash, String prefix, PlayerEntry sender) {
// removes reset section sign
if (hash.length() == (16 * 2 /* <-- don't forget, we have the section signs */) + 2 && hash.endsWith("§r")) hash = hash.substring(0, hash.length() - 2);
return hash.equals(getHash(prefix, sender, true)) ||
hash.equals(getHash(prefix, sender, false));
public boolean isCorrectHash (String input, String prefix, PlayerEntry sender) {
return checkHash(getHash(prefix, sender, true), input, prefix, sender) ||
checkHash(getHash(prefix, sender, false), input, prefix, sender);
}
public boolean isCorrectOwnerHash (String hash, String prefix, PlayerEntry sender) {
// removes reset section sign
if (hash.length() == (16 * 2 /* <-- don't forget, we have the section signs */) + 2 && hash.endsWith("§r")) hash = hash.substring(0, hash.length() - 2);
public boolean isCorrectAdminHash (String input, String prefix, PlayerEntry sender) {
return checkHash(getAdminHash(prefix, sender, true), input, prefix, sender) ||
checkHash(getAdminHash(prefix, sender, false), input, prefix, sender);
}
return hash.equals(getOwnerHash(prefix, sender, true)) ||
hash.equals(getOwnerHash(prefix, sender, false));
public boolean isCorrectOwnerHash (String input, String prefix, PlayerEntry sender) {
return checkHash(getOwnerHash(prefix, sender, true), input, prefix, sender) ||
checkHash(getOwnerHash(prefix, sender, false), input, prefix, sender);
}
}

View file

@ -4,12 +4,8 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.PersistentDataUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@ -19,8 +15,8 @@ public class IPFilterPlugin extends PlayersPlugin.Listener {
public static JsonArray filteredIPs = new JsonArray();
static {
if (PersistentDataUtilities.jsonObject.has("ipFilters")) {
filteredIPs = PersistentDataUtilities.jsonObject.get("ipFilters").getAsJsonArray();
if (PersistentDataUtilities.has("ipFilters")) {
filteredIPs = PersistentDataUtilities.get("ipFilters").getAsJsonArray();
}
}
@ -29,7 +25,7 @@ public class IPFilterPlugin extends PlayersPlugin.Listener {
bot.players.addListener(this);
bot.executor.scheduleAtFixedRate(this::checkAllPlayers, 0, 2, TimeUnit.SECONDS);
bot.executor.scheduleAtFixedRate(this::checkAllPlayers, 0, 10, TimeUnit.SECONDS);
}
@Override
@ -40,21 +36,14 @@ public class IPFilterPlugin extends PlayersPlugin.Listener {
}
private void check (PlayerEntry target) {
final CompletableFuture<Component> future = bot.core.runTracked("essentials:seen " + target.profile.getIdAsString());
if (bot.options.useCorePlaceBlock) return; // it will spam the place block core so i ignored this
final CompletableFuture<String> future = bot.players.getPlayerIP(target);
if (future == null) return;
future.thenApply(output -> {
final List<Component> children = output.children();
String stringified = ComponentUtilities.stringify(Component.join(JoinConfiguration.separator(Component.space()), children));
if (!stringified.startsWith(" - IP Address: ")) return output;
stringified = stringified.trim().substring(" - IP Address: ".length());
if (stringified.startsWith("/")) stringified = stringified.substring(1);
handleIP(stringified, target);
future.thenApplyAsync(output -> {
handleIP(output, target);
return output;
});
@ -71,7 +60,9 @@ public class IPFilterPlugin extends PlayersPlugin.Listener {
private void checkAllPlayers () {
if (filteredIPs.isEmpty()) return;
for (PlayerEntry entry : bot.players.list) check(entry);
bot.executorService.submit(() -> {
for (PlayerEntry entry : bot.players.list) check(entry);
});
}
public String remove (int index) {

View file

@ -169,7 +169,7 @@ public class IRCPlugin extends ListenerAdapter {
}
public void quit (String reason) {
bot.sendIRC().quitServer(reason);
if (bot.isConnected()) bot.sendIRC().quitServer(reason);
}
private void connected (Bot bot) {
@ -202,9 +202,7 @@ public class IRCPlugin extends ListenerAdapter {
bot.sendIRC().message(entry.getKey(), withIRCColors);
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception ignored) {}
}
private void addMessageToQueue (Bot bot, String message) {

View file

@ -1,6 +1,7 @@
package me.chayapak1.chomens_bot.plugins;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import me.chayapak1.chomens_bot.util.LoggerUtilities;
import net.kyori.adventure.text.Component;
@ -8,6 +9,10 @@ import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent;
import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent;
public class LoggerPlugin extends ChatPlugin.Listener {
public static void init () {
for (Bot bot : Main.bots) new LoggerPlugin(bot);
}
private final Bot bot;
private boolean addedListener = false;
@ -16,7 +21,7 @@ public class LoggerPlugin extends ChatPlugin.Listener {
private int totalConnects = 0;
public LoggerPlugin(Bot bot) {
public LoggerPlugin (Bot bot) {
this.bot = bot;
bot.addListener(new Bot.Listener() {
@ -75,6 +80,8 @@ public class LoggerPlugin extends ChatPlugin.Listener {
log(message + string, true, false);
}
});
bot.logger = this;
}
public void log (String message) {

View file

@ -19,8 +19,8 @@ public class MailPlugin extends PlayersPlugin.Listener {
private final Gson gson = new Gson();
static {
if (PersistentDataUtilities.jsonObject.has("mails")) {
mails = PersistentDataUtilities.jsonObject.get("mails").getAsJsonArray();
if (PersistentDataUtilities.has("mails")) {
mails = PersistentDataUtilities.get("mails").getAsJsonArray();
}
}

View file

@ -56,6 +56,8 @@ public class MusicPlayerPlugin extends Bot.Listener {
private int limit = 0;
public boolean locked = false; // this can be set through servereval
private final String bossbarName = "music";
public BossBarColor bossBarColor;
@ -73,7 +75,7 @@ public class MusicPlayerPlugin extends Bot.Listener {
}
public void loadSong (Path location, PlayerEntry sender) {
if (songQueue.size() > 100) return;
if (songQueue.size() > 500) return;
loaderThread = new SongLoaderThread(location, bot, sender.profile.getName());
@ -90,7 +92,7 @@ public class MusicPlayerPlugin extends Bot.Listener {
}
public void loadSong (URL location, PlayerEntry sender) {
if (songQueue.size() > 100) return;
if (songQueue.size() > 500) return;
limit++;
@ -114,7 +116,7 @@ public class MusicPlayerPlugin extends Bot.Listener {
}
public void loadSong (byte[] data, PlayerEntry sender) {
if (songQueue.size() > 100) return;
if (songQueue.size() > 500) return;
loaderThread = new SongLoaderThread(data, bot, sender.profile.getName());
@ -194,13 +196,9 @@ public class MusicPlayerPlugin extends Bot.Listener {
if (songQueue.isEmpty()) {
stopPlaying();
removeBossBar();
bot.chat.tellraw(
Component
.text("Finished playing every song in the queue")
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
);
return;
}
if (currentSong.size() > 0) {
currentSong = songQueue.get(0);
currentSong.setTime(0);

View file

@ -1,18 +1,21 @@
package me.chayapak1.chomens_bot.plugins;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.PersistentDataUtilities;
import java.time.Instant;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class PlayersPersistentDataPlugin extends PlayersPlugin.Listener {
public static JsonObject playersObject = new JsonObject();
static {
if (PersistentDataUtilities.jsonObject.has("players")) {
playersObject = PersistentDataUtilities.jsonObject.get("players").getAsJsonObject();
if (PersistentDataUtilities.has("players")) {
playersObject = PersistentDataUtilities.get("players").getAsJsonObject();
}
}
@ -26,12 +29,40 @@ public class PlayersPersistentDataPlugin extends PlayersPlugin.Listener {
@Override
public void playerJoined(PlayerEntry target) {
if (playersObject.has(getName(target))) return;
final JsonElement originalElement = playersObject.get(target.profile.getName());
final JsonObject object = new JsonObject();
object.addProperty("uuid", target.profile.getIdAsString());
object.add("lastSeen", new JsonObject());
JsonObject object;
if (originalElement == null) {
object = new JsonObject();
object.addProperty("uuid", target.profile.getIdAsString());
object.add("ips", new JsonObject());
} else {
object = originalElement.getAsJsonObject();
}
final CompletableFuture<String> future = bot.players.getPlayerIP(target);
if (future == null) {
setPersistentEntry(target, object);
return;
}
future.completeOnTimeout(null, 5, TimeUnit.SECONDS);
future.thenApplyAsync(output -> {
if (output != null) {
object.getAsJsonObject("ips").addProperty(bot.host + ":" + bot.port, output);
}
setPersistentEntry(target, object);
return output;
});
}
// is this bad?
private void setPersistentEntry (PlayerEntry target, JsonObject object) {
playersObject.add(getName(target), object);
PersistentDataUtilities.put("players", playersObject);

View file

@ -1,5 +1,7 @@
package me.chayapak1.chomens_bot.plugins;
import me.chayapak1.chomens_bot.util.ComponentUtilities;
import net.kyori.adventure.text.JoinConfiguration;
import org.geysermc.mcprotocollib.network.Session;
import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent;
import org.geysermc.mcprotocollib.network.packet.Packet;
@ -16,6 +18,7 @@ import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class PlayersPlugin extends Bot.Listener {
private final Bot bot;
@ -58,6 +61,31 @@ public class PlayersPlugin extends Bot.Listener {
}
}
public CompletableFuture<String> getPlayerIP (PlayerEntry target) {
final CompletableFuture<String> outputFuture = new CompletableFuture<>();
final CompletableFuture<Component> trackedCoreFuture = bot.core.runTracked("essentials:seen " + target.profile.getIdAsString());
if (trackedCoreFuture == null) return null;
trackedCoreFuture.thenApplyAsync(output -> {
final List<Component> children = output.children();
String stringified = ComponentUtilities.stringify(Component.join(JoinConfiguration.separator(Component.empty()), children));
if (!stringified.startsWith(" - IP Address: ")) return output;
stringified = stringified.trim().substring(" - IP Address: ".length());
if (stringified.startsWith("/")) stringified = stringified.substring(1);
outputFuture.complete(stringified);
return output;
});
return outputFuture;
}
public final PlayerEntry getEntry (UUID uuid) {
for (PlayerEntry candidate : list) {
if (candidate.profile.getId().equals(uuid)) {
@ -164,7 +192,7 @@ public class PlayersPlugin extends Bot.Listener {
final PlayerEntry target = getEntry(uuid);
if (target == null) return;
bot.tabComplete.tabComplete("/minecraft:scoreboard players add ").thenApply(packet -> {
bot.tabComplete.tabComplete("/minecraft:scoreboard players add ").thenApplyAsync(packet -> {
final String[] matches = packet.getMatches();
final Component[] tooltips = packet.getTooltips();
final String username = target.profile.getName();

View file

@ -30,6 +30,8 @@ public class PositionPlugin extends Bot.Listener {
public Vector3i position = Vector3i.from(0, 0, 0);
public boolean isGoingDownFromHeightLimit = false; // cool variable name
private final Map<Integer, PlayerEntry> entityIdMap = new HashMap<>();
private final Map<Integer, Vector3f> positionMap = new HashMap<>();
private final Map<Integer, Rotation> rotationMap = new HashMap<>();
@ -40,12 +42,23 @@ public class PositionPlugin extends Bot.Listener {
bot.addListener(this);
// notchian clients also does this, sends the position packet every second
bot.executor.scheduleAtFixedRate(() -> bot.session.send(new ServerboundMovePlayerPosPacket(
false,
position.getX(),
position.getY(),
position.getZ()
)), 0, 1, TimeUnit.SECONDS);
bot.executor.scheduleAtFixedRate(() -> {
if (isGoingDownFromHeightLimit) return;
bot.session.send(new ServerboundMovePlayerPosPacket(
false,
position.getX(),
position.getY(),
position.getZ()
));
}, 0, 1, TimeUnit.SECONDS);
bot.tick.addListener(new TickPlugin.Listener() {
@Override
public void onTick() {
handleHeightLimit();
}
});
}
@Override
@ -145,6 +158,52 @@ public class PositionPlugin extends Bot.Listener {
for (Listener listener : listeners) listener.playerMoved(player, position, rotation);
}
// for now this is used in CorePlugin when placing the command block
private void handleHeightLimit () {
final int y = position.getY();
final int minY = bot.world.minY;
final int maxY = bot.world.maxY;
if (y < maxY) {
if (isGoingDownFromHeightLimit) {
isGoingDownFromHeightLimit = false;
for (Listener listener : listeners) { listener.positionChange(position); }
}
return;
}
isGoingDownFromHeightLimit = true;
final Vector3i newPosition = Vector3i.from(
position.getX(),
position.getY() - 2,
position.getZ()
);
position = newPosition;
if (position.getY() > maxY + 500 || position.getY() < minY) {
String command = "/";
if (bot.serverPluginsManager.hasPlugin(ServerPluginsManagerPlugin.ESSENTIALS)) command += "essentials:";
command += String.format("tp ~ %s ~", maxY - 1);
bot.chat.send(command);
return;
}
bot.session.send(new ServerboundMovePlayerPosPacket(
false,
newPosition.getX(),
newPosition.getY(),
newPosition.getZ()
));
}
public Vector3f getPlayerPosition (String playerName) {
int entityId = -1;
for (Map.Entry<Integer, PlayerEntry> entry : entityIdMap.entrySet()) {

View file

@ -27,7 +27,7 @@ public class ServerPluginsManagerPlugin extends Bot.Listener {
public void connected(ConnectedEvent event) {
final CompletableFuture<ClientboundCommandSuggestionsPacket> future = bot.tabComplete.tabComplete("/ver ");
future.thenApply((packet) -> {
future.thenApplyAsync((packet) -> {
final String[] matches = packet.getMatches();
// should i just use the plugins as the String array instead of a list?

View file

@ -13,7 +13,7 @@ public class TagPlugin extends CorePlugin.Listener {
public TagPlugin (Bot bot) {
this.bot = bot;
bot.executor.scheduleAtFixedRate(this::runCommand, 5, 20, TimeUnit.SECONDS);
bot.executor.scheduleAtFixedRate(this::runCommand, 5, 30, TimeUnit.SECONDS);
}
private void runCommand () {

View file

@ -18,11 +18,25 @@ public class TickPlugin {
}
private void tick () {
for (Listener listener : listeners) listener.onAlwaysTick();
for (Listener listener : listeners) {
try {
listener.onAlwaysTick();
} catch (Exception e) {
bot.logger.error("Caught exception in an always tick listener!");
e.printStackTrace();
}
}
if (!bot.loggedIn) return;
for (Listener listener : listeners) listener.onTick();
for (Listener listener : listeners) {
try {
listener.onTick();
} catch (Exception e) {
bot.logger.error("Caught exception in a tick listener!");
e.printStackTrace();
}
}
}
public void addListener (Listener listener) {

View file

@ -5,10 +5,9 @@ import me.chayapak1.chomens_bot.data.PlayerEntry;
import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;
@ -64,10 +63,10 @@ public class TrustedPlugin extends PlayersPlugin.Listener {
Component.text(target.profile.getName()).color(ColorUtilities.getColorByString(bot.config.colorPalette.username))
).color(NamedTextColor.GREEN);
} else {
final DateTime now = DateTime.now();
final LocalDateTime now = LocalDateTime.now();
final DateTimeFormatter formatter = DateTimeFormat.forPattern("hh:mm:ss a");
final String formattedTime = formatter.print(now);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm:ss a");
final String formattedTime = now.format(formatter);
component = Component.translatable(
"""

View file

@ -58,7 +58,7 @@ public class VoiceChatPlugin extends Bot.Listener {
bot.session.send(new ServerboundCustomPayloadPacket(
Key.key("voicechat:request_secret"),
new FriendlyByteBuf(Unpooled.buffer()).writeInt(17).array()
new FriendlyByteBuf(Unpooled.buffer()).writeInt(18).array()
));
bot.session.send(new ServerboundCustomPayloadPacket(

View file

@ -51,13 +51,7 @@ public class WhitelistPlugin extends PlayersPlugin.Listener {
}
public void add (String player) { list.add(player); }
public void remove (String player) {
list.removeIf(eachPlayer -> eachPlayer.equals(player));
final PlayerEntry entry = bot.players.getEntry(player);
if (entry != null) handle(entry);
}
public String remove (int index) { return list.remove(index); }
public void clear () {
list.removeIf(eachPlayer -> !eachPlayer.equals(bot.profile.getName()));
}
@ -72,7 +66,7 @@ public class WhitelistPlugin extends PlayersPlugin.Listener {
}
public void commandReceived (PlayerEntry entry, String command) {
if (!enabled || list.contains(entry.profile.getName())) return;
if (!enabled || list.contains(entry.profile.getName()) || entry.profile.equals(bot.profile)) return;
if (
command.startsWith("mute") ||
@ -90,7 +84,7 @@ public class WhitelistPlugin extends PlayersPlugin.Listener {
}
private void handle (PlayerEntry entry) {
if (!enabled || list.contains(entry.profile.getName())) return;
if (!enabled || list.contains(entry.profile.getName()) || entry.profile.equals(bot.profile)) return;
bot.filter.doAll(entry);
}

View file

@ -34,14 +34,17 @@ public class WorldPlugin extends Bot.Listener {
private void worldChanged (String dimension) {
final RegistryEntry currentDimension = registry.stream()
.filter(eachDimension -> eachDimension.getId().asString().equals(dimension))
.toArray(RegistryEntry[]::new)[0];
.findFirst()
.orElse(null);
if (currentDimension == null) return;
final NbtMap data = currentDimension.getData();
if (data == null) return;
minY = data.getInt("min_y");
maxY = data.getInt("height");
maxY = data.getInt("height") + minY;
for (Listener listener : listeners) listener.worldChanged(dimension);
}

View file

@ -215,7 +215,8 @@ public class MidiConverter implements Converter {
shiftedInstrument = Arrays.stream(instrumentList)
.filter(ins -> ins.offset == closest)
.toArray(Instrument[]::new)[0];
.findFirst()
.orElse(null);
}
}

View file

@ -278,14 +278,14 @@ public class NBSConverter implements Converter {
private static final Map<String, String> subtitles = new HashMap<>();
static {
for (Map.Entry<String, String> entry : ComponentUtilities.englishLanguage.entrySet()) {
for (Map.Entry<String, String> entry : ComponentUtilities.language.entrySet()) {
if (!entry.getKey().startsWith("subtitles.")) continue;
subtitles.put(entry.getKey(), entry.getValue());
}
}
private static List<String> sounds = loadJsonStringArray("sounds.json");
private static final List<String> sounds = loadJsonStringArray("sounds.json");
private static List<String> loadJsonStringArray (String name) {
List<String> list = new ArrayList<>();

View file

@ -17,7 +17,7 @@ public class Song {
public long startTime = 0; // Start time in millis since unix epoch
public long length = 0; // Milliseconds in the song
public long time = 0; // Time since start of song
public long loopPosition = 200; // Milliseconds into the song to start looping
public long loopPosition = 0; // Milliseconds into the song to start looping
public final Map<Long, String> lyrics = new HashMap<>();
@ -80,7 +80,7 @@ public class Song {
*/
public void play () {
if (paused) {
if (loopPosition != 200) bot.music.loop = Loop.CURRENT;
if (loopPosition != 0) bot.music.loop = Loop.CURRENT;
paused = false;
startTime = System.currentTimeMillis() - time;
}

View file

@ -1,6 +1,7 @@
package me.chayapak1.chomens_bot.song;
import me.chayapak1.chomens_bot.Bot;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -46,7 +47,7 @@ public class SongPlayerConverter implements Converter {
song.length = songLength;
// song.looping = loop > 0;
// song.loopCount = loopCount;
song.loopPosition = loopPosition == 0 ? 200 : loopPosition;
song.loopPosition = loopPosition;
long time = 0;
while (true) {
@ -107,12 +108,12 @@ public class SongPlayerConverter implements Converter {
}
@Override
public int read(byte b[]) throws IOException {
public int read(byte @NotNull [] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte b[], int off, int len) throws IOException {
public int read(byte @NotNull [] b, int off, int len) throws IOException {
int i = original.read(b, off, len);
if (i>=0) incrementCounter(i);
return i;

View file

@ -0,0 +1,9 @@
package me.chayapak1.chomens_bot.util;
public class ArrayUtilities {
public static boolean isAllTrue (boolean[] array) {
for (boolean value : array) if (!value) return false;
return true;
}
}

View file

@ -26,7 +26,6 @@ public class ComponentUtilities {
// component parsing
public static final Map<String, String> language = loadJsonStringMap("language.json");
public static final Map<String, String> englishLanguage = loadJsonStringMap("englishLanguage.json");
private static final Map<String, String> voiceChatLanguage = loadJsonStringMap("voiceChatLanguage.json");
private static final Map<String, String> keybinds = loadJsonStringMap("keybinds.json");
@ -64,9 +63,6 @@ public class ComponentUtilities {
String lastColor
) {}
private ComponentUtilities () {
}
private static Map<String, String> loadJsonStringMap (String name) {
Map<String, String> map = new HashMap<>();
@ -81,13 +77,15 @@ public class ComponentUtilities {
return map;
}
private static String getOrReturnKey (String key) {
private static String getOrReturnFallback (TranslatableComponent component) {
final String key = component.key();
final String minecraftKey = language.get(key);
final String voiceChatKey = voiceChatLanguage.get(key);
if (minecraftKey != null) return minecraftKey;
else if (voiceChatKey != null) return voiceChatKey;
else return key;
else return component.fallback() != null ? component.fallback() : key;
}
public static String stringify (Component message) { return stringify(message, null); }
@ -264,7 +262,7 @@ public class ComponentUtilities {
}
public static PartiallyStringified stringifyPartially (TranslatableComponent message, boolean motd, boolean ansi, String lastColor, boolean noHex) {
String format = getOrReturnKey(message.key());
String format = getOrReturnFallback(message);
// totallynotskidded from HBot (and changed a bit)
Matcher matcher = ARG_PATTERN.matcher(format);
@ -278,6 +276,8 @@ public class ComponentUtilities {
int i = 0;
while (matcher.find()) {
if (i > 300) break;
if (matcher.group().equals("%%")) {
matcher.appendReplacement(sb, "%");
} else {
@ -317,7 +317,7 @@ public class ComponentUtilities {
public static PartiallyStringified stringifyPartially (KeybindComponent message, boolean motd, boolean ansi, String lastColor, boolean noHex) {
String keybind = message.keybind();
Component component = keybinds.containsKey(keybind) ? Component.translatable(keybind) : Component.text(keybind); // TODO: Fix some keys like `key.keyboard.a`
Component component = keybinds.containsKey(keybind) ? Component.translatable(keybinds.get(keybind)) : Component.text(keybind);
return stringifyPartially(component, motd, ansi, lastColor, noHex);
}
}

View file

@ -2,10 +2,8 @@ package me.chayapak1.chomens_bot.util;
import me.chayapak1.chomens_bot.Main;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -17,12 +15,13 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPOutputStream;
// totallynotskidded from HBot
// original source code from hhhzzzsss, specifically HBot.
// source: https://github.com/hhhzzzsss/HBot-Release/blob/main/src/main/java/com/github/hhhzzzsss/hbot/Logger.java
public class FileLoggerUtilities {
public static final Path logDirectory = Path.of("logs");
public static final Path logPath = Paths.get(logDirectory.toString(), "log.txt");
public static BufferedWriter logWriter;
public static OutputStreamWriter logWriter;
public static LocalDate currentLogDate;
public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("'['dd/MM/yyyy HH:mm:ss']' ");
@ -78,56 +77,49 @@ public class FileLoggerUtilities {
}
}
public static void makeNewLogFile() throws IOException {
public static synchronized void makeNewLogFile() throws IOException {
currentLogDate = LocalDate.now();
if (!Files.exists(logPath)) Files.createFile(logPath);
logWriter = Files.newBufferedWriter(logPath, StandardOpenOption.TRUNCATE_EXISTING);
logWriter = new OutputStreamWriter(Files.newOutputStream(logPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING), StandardCharsets.UTF_8);
logWriter.write(currentLogDate.toString() + '\n');
logWriter.flush();
}
public static void openLogFile() throws IOException {
public static synchronized void openLogFile() throws IOException {
currentLogDate = LocalDate.parse(getLogDate(logPath));
logWriter = Files.newBufferedWriter(logPath, StandardOpenOption.APPEND);
logWriter = new OutputStreamWriter(Files.newOutputStream(logPath, StandardOpenOption.CREATE, StandardOpenOption.APPEND), StandardCharsets.UTF_8);
}
public static void compressLogFile() throws IOException {
public static synchronized void compressLogFile() throws IOException {
if (Files.size(logPath) > 100 * 1024 * 1024) { // Will not save because log file is too big
return;
}
final Path path = Paths.get(logDirectory.toString(), getLogDate(logPath) + ".txt.gz");
Files.createFile(path);
InputStream in = Files.newInputStream(logPath);
GZIPOutputStream out = new GZIPOutputStream(Files.newOutputStream(path));
byte[] buffer = new byte[1024];
int size;
while ((size = in.read(buffer)) > 0) {
out.write(buffer, 0, size);
try (
final InputStream in = Files.newInputStream(logPath, StandardOpenOption.READ);
final GZIPOutputStream out = new GZIPOutputStream(Files.newOutputStream(path, StandardOpenOption.CREATE))
) {
byte[] buffer = new byte[1024];
int size;
while ((size = in.read(buffer)) > 0) {
out.write(buffer, 0, size);
}
}
in.close();
out.finish();
out.close();
}
public static String getLogDate(Path path) throws IOException {
BufferedReader reader = Files.newBufferedReader(path);
String date = reader.readLine();
reader.close();
return date;
public static synchronized String getLogDate (Path filePath) throws IOException {
try (final BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
return reader.readLine();
}
}
public static boolean logIsCurrent(Path path) throws IOException {
public static synchronized boolean logIsCurrent(Path path) throws IOException {
LocalDate date = LocalDate.now();
return getLogDate(path).equals(date.toString());
}
public static void log(String str) {
public static synchronized void log(String str) {
if (freezeTime > System.currentTimeMillis()) {
return;
}
@ -161,8 +153,7 @@ public class FileLoggerUtilities {
logWriter.write("\n");
}
if (str.length() > 32767) logWriter.write("Message too big, not logging this message"); // should these stuff be hardcoded?
else logWriter.write(getTimePrefix() + str.replaceAll("\\[(\\d+?)x](?=$|[\r\n])", "[/$1x]")); // the replaceAll will prevent conflicts with the duplicate counter
logWriter.write(getTimePrefix() + str.replaceAll("\\[(\\d+?)x](?=$|[\r\n])", "[/$1x]")); // the replaceAll will prevent conflicts with the duplicate counter
logWriter.flush();
duplicateCounter = 1;

View file

@ -38,6 +38,8 @@ public class LoggerUtilities {
}
public static void log (String message) { log(null, message, true, true); }
public static void log (Component message) { log(null, ComponentUtilities.stringifyAnsi(message)); }
public static void log (Bot bot, Component message) { log(bot, ComponentUtilities.stringifyAnsi(message)); }
public static void log (Bot bot, String message) { log(bot, message, true, true); }
public static void log (Bot bot, String message, boolean logToFile, boolean logToConsole) {
final String component = prefix(bot, Component.text("Log").color(NamedTextColor.GOLD), message);
@ -58,6 +60,8 @@ public class LoggerUtilities {
}
public static void info (String message) { info(null, message); }
public static void info (Component message) { info(null, ComponentUtilities.stringifyAnsi(message)); }
public static void info (Bot bot, Component message) { info(bot, ComponentUtilities.stringifyAnsi(message)); }
public static void info (Bot bot, String message) {
final String component = prefix(bot, Component.text("Info").color(NamedTextColor.GREEN), message);
@ -66,6 +70,8 @@ public class LoggerUtilities {
}
public static void error (String message) { error(null, message); }
public static void error (Component message) { error(null, ComponentUtilities.stringifyAnsi(message)); }
public static void error (Bot bot, Component message) { error(bot, ComponentUtilities.stringifyAnsi(message)); }
public static void error (Bot bot, String message) {
final String component = prefix(bot, Component.text("Error").color(NamedTextColor.RED), message);

View file

@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import me.chayapak1.chomens_bot.Main;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@ -12,101 +11,95 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class PersistentDataUtilities {
public static final Path path = Path.of("persistent.json");
private static final Path path = Path.of("persistent.json");
private static BufferedWriter writer;
private static final Gson gson = new Gson();
public static JsonObject jsonObject = new JsonObject();
private static JsonObject jsonObject = new JsonObject();
private static final Map<String, JsonElement> queue = new ConcurrentHashMap<>();
private static final ReentrantLock lock = new ReentrantLock();
private static final ScheduledFuture<?> future;
private static volatile boolean stopping = false;
static {
init();
public static void init () {
lock.lock();
future = Main.executor.scheduleAtFixedRate(() -> {
try {
if (queue.isEmpty()) return;
final Map.Entry<String, JsonElement> entry = queue.entrySet().iterator().next(); // is this the best way to get the first item of the map?
final String property = entry.getKey();
final JsonElement value = entry.getValue();
Main.executorService.submit(() -> {
jsonObject.add(property, value);
write(jsonObject.toString());
queue.remove(property);
});
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 100, TimeUnit.MILLISECONDS);
}
private static void init () {
try {
if (!Files.exists(path)) Files.createFile(path);
// loads the persistent data from the last session
else {
final BufferedReader reader = Files.newBufferedReader(path);
final Gson gson = new Gson();
jsonObject = gson.fromJson(reader, JsonObject.class);
if (Files.exists(path)) {
try (BufferedReader reader = Files.newBufferedReader(path)) {
jsonObject = gson.fromJson(reader, JsonObject.class);
}
} else {
Files.createFile(path);
}
writer = Files.newBufferedWriter(path, StandardOpenOption.TRUNCATE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private static void write (String string) {
private static synchronized void writeToFile() {
if (stopping) return;
lock.lock();
try (BufferedWriter writer = Files.newBufferedWriter(path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
writer.write(gson.toJson(jsonObject));
} catch (IOException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static synchronized void stop () {
stopping = true;
lock.lock();
try {
writer.close();
// ? how do i clear the file contents without making a completely new writer?
// or is this the only way?
writer = Files.newBufferedWriter(path, StandardOpenOption.TRUNCATE_EXISTING);
writer.write(string);
writer.flush();
} catch (IOException ignored) {}
writeToFile();
} finally {
lock.unlock();
}
}
public static void stop () {
future.cancel(false);
write(jsonObject.toString());
public static synchronized boolean has (String property) {
return jsonObject.has(property);
}
public static void put (String property, JsonElement value) {
Main.executorService.submit(() -> queue.put(property, value));
public static synchronized JsonElement get (String property) {
return jsonObject.get(property);
}
public static void put (String property, String value) {
Main.executorService.submit(() -> queue.put(property, new JsonPrimitive(value)));
public static synchronized void put (String property, JsonElement value) {
lock.lock();
try {
jsonObject.add(property, value);
writeToFile();
} finally {
lock.unlock();
}
}
public static void put (String property, boolean value) {
Main.executorService.submit(() -> queue.put(property, new JsonPrimitive(value)));
public static synchronized void put (String property, String value) {
put(property, new JsonPrimitive(value));
}
public static void put (String property, int value) {
Main.executorService.submit(() -> queue.put(property, new JsonPrimitive(value)));
public static synchronized void put (String property, boolean value) {
put(property, new JsonPrimitive(value));
}
public static void put (String property, char value) {
Main.executorService.submit(() -> queue.put(property, new JsonPrimitive(value)));
public static synchronized void put (String property, int value) {
put(property, new JsonPrimitive(value));
}
public static synchronized void put (String property, char value) {
put(property, new JsonPrimitive(value));
}
}

View file

@ -70,9 +70,9 @@ public class JavaOpusEncoder {
public static OpusApplication getApplication(de.maxhenkel.opus4j.OpusEncoder.Application application) {
return switch (application) {
default -> OpusApplication.OPUS_APPLICATION_VOIP;
case AUDIO -> OpusApplication.OPUS_APPLICATION_AUDIO;
case LOW_DELAY -> OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY;
default -> OpusApplication.OPUS_APPLICATION_VOIP;
};
}
}

View file

@ -0,0 +1,4 @@
build.date=${compileDate}
build.git.commit.count=${gitCommitCount}
build.git.commit.hash=${gitCommitHash}
build.number=${buildNumber}

View file

@ -9,33 +9,34 @@ commandSpyPrefixes:
consoleCommandPrefix: '.'
internetCheck:
enabled: true
address: 'https://sus.red'
# how backup works is that it checks for the address every 1 minute,
# how backup works is that it makes a http request for the address
# if the address is reachable it will not start the bot
# if the address is not reachable then it will start the bot
#
# *** it doesn't care about the status code
# if the bot has already been started and the address is back up it
# will stop the bot (using System.exit(1))
# will stop the bot
backup:
enabled: false
address: 'https://fard.sex/check'
interval: 1000 # in milliseconds
failTimes: 2
discord:
enabled: false
prefix: 'default!'
token: 'token here'
trustedRoleName: 'Trusted'
adminRoleName: 'Admin' # NOTE: admin will be able to access servereval..
adminRoleName: 'Admin'
ownerRoleName: 'Owner'
statusMessage: 'Say Gex'
inviteLink: 'https://discord.gg/xdgCkUyaA4'
servers:
localhost:25565: 'channel id'
irc:
enabled: true
enabled: false
prefix: '!'
host: 'irc.libera.chat'
port: 6665
@ -59,7 +60,8 @@ colorPalette:
number: 'gold'
ownerName: 'green'
ownerName: 'chayapak' # currently this is only used in the console
# you HAVE TO CHANGE THIS if you are hosting another instance of my bot.
ownerName: 'XxChange_mexX'
imposterFormatChecker:
enabled: false
@ -74,8 +76,9 @@ trusted:
- 'player name'
keys:
normalKey: 'normal hash key here'
ownerKey: 'OwnerHash™ key here'
trustedKey: 'trusted key here'
adminKey: 'admin key here'
ownerKey: 'owner key here'
weatherApiKey: 'key here' # weatherapi.com key
@ -130,7 +133,8 @@ bots:
# username - optional, if not specified it will just use a random username
# creayun - defaults to false
# serverName - name it whatever you like, it will be used as server name in trusted broadcast and in console
# useCore - if enabled it just sends the command using chat instead of using core. recommended to enable useChat too when this is enabled
# useCore - if enabled it just sends the command using chat instead of using core. recommended to enable useChat too when this is disabled
# useCorePlaceBlock - uses the place block core instead of the main core. only used if useCore is enabled
# useChat - when the bot tellraws it will chat instead of using the core to run tellraw
# coreCommandSpy - set to true if server supports enabling player's commandspy though command block
# resolveSRV - whether to resolve SRV records on the server. the notchian minecraft doesn't resolve them
@ -139,22 +143,23 @@ bots:
- host: 'localhost'
port: 25565
username: 'ChomeNS_Bot'
creayun: false
serverName: 'Localhost'
useCore: true
useChat: false
coreCommandSpy: false
resolveSRV: true
reconnectDelay: 2000
removeNamespaces: false
chatQueueDelay: 125
coreRateLimit:
limit: 10
reset: 1000 # in milliseconds
# or without the optional ones:
# or with the optional ones
# - host: 'localhost'
# port: 25565
# username: 'ChomeNS_Bot'
# creayun: false
# serverName: 'Localhost'
# useCore: true
# useCorePlaceBlock: false
# useChat: false
# coreCommandSpy: false
# resolveSRV: true
# reconnectDelay: 2000
# removeNamespaces: false
# chatQueueDelay: 125
# coreRateLimit:
# limit: 10
# reset: 1000 # in milliseconds

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -33,6 +33,7 @@
"block.amethyst_block.fall",
"block.amethyst_block.hit",
"block.amethyst_block.place",
"block.amethyst_block.resonate",
"block.amethyst_block.step",
"block.amethyst_cluster.break",
"block.amethyst_cluster.fall",
@ -52,6 +53,19 @@
"block.anvil.place",
"block.anvil.step",
"block.anvil.use",
"entity.armadillo.eat",
"entity.armadillo.hurt",
"entity.armadillo.hurt_reduced",
"entity.armadillo.ambient",
"entity.armadillo.step",
"entity.armadillo.death",
"entity.armadillo.roll",
"entity.armadillo.land",
"entity.armadillo.scute_drop",
"entity.armadillo.unroll_finish",
"entity.armadillo.peek",
"entity.armadillo.unroll_start",
"entity.armadillo.brush",
"item.armor.equip_chain",
"item.armor.equip_diamond",
"item.armor.equip_elytra",
@ -61,6 +75,8 @@
"item.armor.equip_leather",
"item.armor.equip_netherite",
"item.armor.equip_turtle",
"item.armor.equip_wolf",
"item.armor.unequip_wolf",
"entity.armor_stand.break",
"entity.armor_stand.fall",
"entity.armor_stand.hit",
@ -152,6 +168,11 @@
"entity.blaze.shoot",
"entity.boat.paddle_land",
"entity.boat.paddle_water",
"entity.bogged.ambient",
"entity.bogged.death",
"entity.bogged.hurt",
"entity.bogged.shear",
"entity.bogged.step",
"block.bone_block.break",
"block.bone_block.fall",
"block.bone_block.hit",
@ -164,9 +185,25 @@
"item.bottle.empty",
"item.bottle.fill",
"item.bottle.fill_dragonbreath",
"entity.breeze.charge",
"entity.breeze.deflect",
"entity.breeze.inhale",
"entity.breeze.idle_ground",
"entity.breeze.idle_air",
"entity.breeze.shoot",
"entity.breeze.jump",
"entity.breeze.land",
"entity.breeze.slide",
"entity.breeze.death",
"entity.breeze.hurt",
"entity.breeze.whirl",
"entity.breeze.wind_burst",
"block.brewing_stand.brew",
"item.brush.brushing",
"item.brush.brush_sand_completed",
"item.brush.brushing.generic",
"item.brush.brushing.sand",
"item.brush.brushing.gravel",
"item.brush.brushing.sand.complete",
"item.brush.brushing.gravel.complete",
"block.bubble_column.bubble_pop",
"block.bubble_column.upwards_ambient",
"block.bubble_column.upwards_inside",
@ -282,6 +319,11 @@
"block.chorus_flower.death",
"block.chorus_flower.grow",
"item.chorus_fruit.teleport",
"block.cobweb.break",
"block.cobweb.step",
"block.cobweb.place",
"block.cobweb.hit",
"block.cobweb.fall",
"entity.cod.ambient",
"entity.cod.death",
"entity.cod.flop",
@ -296,11 +338,27 @@
"block.conduit.ambient.short",
"block.conduit.attack.target",
"block.conduit.deactivate",
"block.copper_bulb.break",
"block.copper_bulb.step",
"block.copper_bulb.place",
"block.copper_bulb.hit",
"block.copper_bulb.fall",
"block.copper_bulb.turn_on",
"block.copper_bulb.turn_off",
"block.copper.break",
"block.copper.step",
"block.copper.place",
"block.copper.hit",
"block.copper.fall",
"block.copper_door.close",
"block.copper_door.open",
"block.copper_grate.break",
"block.copper_grate.step",
"block.copper_grate.place",
"block.copper_grate.hit",
"block.copper_grate.fall",
"block.copper_trapdoor.close",
"block.copper_trapdoor.open",
"block.coral_block.break",
"block.coral_block.fall",
"block.coral_block.hit",
@ -311,6 +369,8 @@
"entity.cow.hurt",
"entity.cow.milk",
"entity.cow.step",
"block.crafter.craft",
"block.crafter.fail",
"entity.creeper.death",
"entity.creeper.hurt",
"entity.creeper.primed",
@ -327,6 +387,8 @@
"block.decorated_pot.break",
"block.decorated_pot.fall",
"block.decorated_pot.hit",
"block.decorated_pot.insert",
"block.decorated_pot.insert_fail",
"block.decorated_pot.step",
"block.decorated_pot.place",
"block.decorated_pot.shatter",
@ -364,6 +426,7 @@
"entity.donkey.death",
"entity.donkey.eat",
"entity.donkey.hurt",
"entity.donkey.jump",
"block.dripstone_block.break",
"block.dripstone_block.step",
"block.dripstone_block.place",
@ -477,6 +540,11 @@
"block.suspicious_sand.place",
"block.suspicious_sand.hit",
"block.suspicious_sand.fall",
"block.suspicious_gravel.break",
"block.suspicious_gravel.step",
"block.suspicious_gravel.place",
"block.suspicious_gravel.hit",
"block.suspicious_gravel.fall",
"block.froglight.break",
"block.froglight.fall",
"block.froglight.hit",
@ -589,6 +657,11 @@
"block.hanging_sign.fall",
"block.hanging_sign.hit",
"block.hanging_sign.place",
"block.heavy_core.break",
"block.heavy_core.fall",
"block.heavy_core.hit",
"block.heavy_core.place",
"block.heavy_core.step",
"block.nether_wood_hanging_sign.step",
"block.nether_wood_hanging_sign.break",
"block.nether_wood_hanging_sign.fall",
@ -599,6 +672,22 @@
"block.bamboo_wood_hanging_sign.fall",
"block.bamboo_wood_hanging_sign.hit",
"block.bamboo_wood_hanging_sign.place",
"block.trial_spawner.break",
"block.trial_spawner.step",
"block.trial_spawner.place",
"block.trial_spawner.hit",
"block.trial_spawner.fall",
"block.trial_spawner.spawn_mob",
"block.trial_spawner.about_to_spawn_item",
"block.trial_spawner.spawn_item",
"block.trial_spawner.spawn_item_begin",
"block.trial_spawner.detect_player",
"block.trial_spawner.ominous_activate",
"block.trial_spawner.ambient",
"block.trial_spawner.ambient_ominous",
"block.trial_spawner.open_shutter",
"block.trial_spawner.close_shutter",
"block.trial_spawner.eject_item",
"item.hoe.till",
"entity.hoglin.ambient",
"entity.hoglin.angry",
@ -710,6 +799,9 @@
"block.lodestone.hit",
"block.lodestone.fall",
"item.lodestone_compass.lock",
"item.mace.smash_air",
"item.mace.smash_ground",
"item.mace.smash_ground_heavy",
"entity.magma_cube.death",
"entity.magma_cube.hurt",
"entity.magma_cube.hurt_small",
@ -774,6 +866,7 @@
"entity.mule.death",
"entity.mule.eat",
"entity.mule.hurt",
"entity.mule.jump",
"music.creative",
"music.credits",
"music_disc.5",
@ -791,6 +884,10 @@
"music_disc.wait",
"music_disc.ward",
"music_disc.otherside",
"music_disc.relic",
"music_disc.creator",
"music_disc.creator_music_box",
"music_disc.precipice",
"music.dragon",
"music.end",
"music.game",
@ -803,7 +900,7 @@
"music.overworld.jagged_peaks",
"music.overworld.lush_caves",
"music.overworld.swamp",
"music.overworld.jungle_and_forest",
"music.overworld.forest",
"music.overworld.old_growth_taiga",
"music.overworld.meadow",
"music.overworld.cherry_grove",
@ -813,6 +910,12 @@
"music.nether.soul_sand_valley",
"music.overworld.stony_peaks",
"music.nether.warped_forest",
"music.overworld.flower_forest",
"music.overworld.desert",
"music.overworld.badlands",
"music.overworld.jungle",
"music.overworld.sparse_jungle",
"music.overworld.bamboo_jungle",
"music.under_water",
"block.nether_bricks.break",
"block.nether_bricks.step",
@ -836,7 +939,7 @@
"block.nether_wood_pressure_plate.click_on",
"block.nether_wood_fence_gate.close",
"block.nether_wood_fence_gate.open",
"minecraft:intentionally_empty",
"intentionally_empty",
"block.packed_mud.break",
"block.packed_mud.fall",
"block.packed_mud.hit",
@ -907,6 +1010,7 @@
"entity.ocelot.hurt",
"entity.ocelot.ambient",
"entity.ocelot.death",
"item.ominous_bottle.dispose",
"entity.painting.break",
"entity.painting.place",
"entity.panda.pre_sneeze",
@ -926,6 +1030,8 @@
"entity.parrot.fly",
"entity.parrot.hurt",
"entity.parrot.imitate.blaze",
"entity.parrot.imitate.bogged",
"entity.parrot.imitate.breeze",
"entity.parrot.imitate.creeper",
"entity.parrot.imitate.drowned",
"entity.parrot.imitate.elder_guardian",
@ -1012,6 +1118,7 @@
"entity.player.splash",
"entity.player.splash.high_speed",
"entity.player.swim",
"entity.player.teleport",
"entity.polar_bear.ambient",
"entity.polar_bear.ambient_baby",
"entity.polar_bear.death",
@ -1221,6 +1328,9 @@
"entity.sniffer.digging",
"entity.sniffer.digging_stop",
"entity.sniffer.happy",
"block.sniffer_egg.plop",
"block.sniffer_egg.crack",
"block.sniffer_egg.hatch",
"entity.snowball.throw",
"block.snow.break",
"block.snow.fall",
@ -1238,6 +1348,12 @@
"entity.spider.step",
"entity.splash_potion.break",
"entity.splash_potion.throw",
"block.sponge.break",
"block.sponge.fall",
"block.sponge.hit",
"block.sponge.place",
"block.sponge.step",
"block.sponge.absorb",
"item.spyglass.use",
"item.spyglass.stop_using",
"entity.squid.ambient",
@ -1288,6 +1404,16 @@
"block.tuff.place",
"block.tuff.hit",
"block.tuff.fall",
"block.tuff_bricks.break",
"block.tuff_bricks.fall",
"block.tuff_bricks.hit",
"block.tuff_bricks.place",
"block.tuff_bricks.step",
"block.polished_tuff.break",
"block.polished_tuff.fall",
"block.polished_tuff.hit",
"block.polished_tuff.place",
"block.polished_tuff.step",
"entity.turtle.ambient_land",
"entity.turtle.death",
"entity.turtle.death_baby",
@ -1309,6 +1435,20 @@
"ui.toast.challenge_complete",
"ui.toast.in",
"ui.toast.out",
"block.vault.activate",
"block.vault.ambient",
"block.vault.break",
"block.vault.close_shutter",
"block.vault.deactivate",
"block.vault.eject_item",
"block.vault.reject_rewarded_player",
"block.vault.fall",
"block.vault.hit",
"block.vault.insert_item",
"block.vault.insert_item_fail",
"block.vault.open_shutter",
"block.vault.place",
"block.vault.step",
"entity.vex.ambient",
"entity.vex.charge",
"entity.vex.death",
@ -1373,6 +1513,8 @@
"entity.warden.sonic_charge",
"entity.warden.step",
"entity.warden.tendril_clicks",
"block.hanging_sign.waxed_interact_fail",
"block.sign.waxed_interact_fail",
"block.water.ambient",
"weather.rain",
"weather.rain.above",
@ -1381,6 +1523,14 @@
"block.wet_grass.hit",
"block.wet_grass.place",
"block.wet_grass.step",
"block.wet_sponge.break",
"block.wet_sponge.dries",
"block.wet_sponge.fall",
"block.wet_sponge.hit",
"block.wet_sponge.place",
"block.wet_sponge.step",
"entity.wind_charge.wind_burst",
"entity.wind_charge.throw",
"entity.witch.ambient",
"entity.witch.celebrate",
"entity.witch.death",
@ -1397,6 +1547,10 @@
"entity.wither_skeleton.hurt",
"entity.wither_skeleton.step",
"entity.wither.spawn",
"item.wolf_armor.break",
"item.wolf_armor.crack",
"item.wolf_armor.damage",
"item.wolf_armor.repair",
"entity.wolf.ambient",
"entity.wolf.death",
"entity.wolf.growl",
@ -1452,5 +1606,8 @@
"entity.zombie_villager.cure",
"entity.zombie_villager.death",
"entity.zombie_villager.hurt",
"entity.zombie_villager.step"
"entity.zombie_villager.step",
"event.mob_effect.bad_omen",
"event.mob_effect.trial_omen",
"event.mob_effect.raid_omen"
]

View file

@ -1,13 +1,13 @@
{
"key.categories.voicechat": "Voice Chat",
"key.push_to_talk": "Push To Talk",
"key.push_to_talk": "Push to Talk",
"key.whisper": "Whisper",
"key.mute_microphone": "Mute Microphone",
"key.disable_voice_chat": "Disable Voice Chat",
"key.hide_icons": "Hide Voice Chat Icons",
"key.voice_chat_settings": "Voice Chat Settings",
"key.voice_chat": "Voice Chat GUI",
"key.voice_chat_group": "Voice Chat Group",
"key.voice_chat": "Voice Chat Menu",
"key.voice_chat_group": "Group Management",
"key.voice_chat_toggle_recording": "Toggle Recording",
"key.voice_chat_adjust_volumes": "Adjust Volumes",
"gui.voicechat.voice_chat.title": "Voice Chat",
@ -15,151 +15,198 @@
"gui.voicechat.adjust_volume.title": "Adjust Volumes",
"gui.voicechat.select_microphone.title": "Select Microphone",
"gui.voicechat.select_speaker.title": "Select Speaker",
"gui.voicechat.group.title": "Group Chat",
"gui.voicechat.group.title": "Group Management",
"gui.voicechat.join_create_group.title": "Join or Create Group",
"gui.voicechat.create_group.title": "Create a Group",
"gui.voicechat.create_group.title": "Create Group",
"gui.voicechat.enter_password.title": "Enter Group Password",
"message.voicechat.settings": "Settings",
"message.voicechat.group": "Group",
"message.voicechat.voice_chat_volume": "Voice chat volume: %s",
"message.voicechat.microphone_amplification": "Microphone amplification: %s",
"message.voicechat.mic_test_on": "Disable microphone testing",
"message.voicechat.mic_test_off": "Enable microphone testing",
"message.voicechat.mic_test_unavailable": "Microphone testing unavailable",
"message.voicechat.voice_chat_volume": "Voice Chat Volume: %s",
"message.voicechat.microphone_amplification": "Microphone Amplification: %s",
"message.voicechat.mic_test_unavailable": "Microphone testing unavailable!",
"message.voicechat.mic_test.disabled": "Press to enable microphone testing.",
"message.voicechat.mic_test.enabled": "Press to disable microphone testing.",
"message.voicechat.voice_chat_unavailable": "Voice chat unavailable",
"message.voicechat.voice_chat_not_connected": "Voice chat not connected",
"message.voicechat.activation_type": "Activation type: %s",
"message.voicechat.activation_type.ptt": "Push to talk",
"message.voicechat.activation_type": "Activation Method: %s",
"message.voicechat.activation_type.ptt": "Push to Talk",
"message.voicechat.activation_type.voice": "Voice",
"message.voicechat.voice_activation": "Voice activation threshold: %s dB",
"message.voicechat.voice_activation": "Voice Activation Threshold: %s dB",
"message.voicechat.voice_activation.disabled": "0 dB deactivates the microphone!",
"message.voicechat.adjust_volumes": "Adjust volumes",
"message.voicechat.adjust_volumes": "Adjust Volumes",
"message.voicechat.volume_amplification": "Amplification: %s",
"message.voicechat.muted": "Muted",
"message.voicechat.adjust_volume_player": "Adjust volume of %s",
"message.voicechat.no_microphone": "No microphone available",
"message.voicechat.no_speaker": "No speaker available",
"message.voicechat.select": "Select",
"message.voicechat.select_microphone": "Select microphone",
"message.voicechat.select_speaker": "Select speaker",
"message.voicechat.no_microphone": "No microphones available",
"message.voicechat.no_speaker": "No speakers available",
"message.voicechat.select_microphone": "Select Microphone",
"message.voicechat.select_speaker": "Select Speaker",
"message.voicechat.back": "Back",
"message.voicechat.client_not_connected": "Client not connected",
"message.voicechat.failed_to_send_ping": "Failed to send ping: %s",
"message.voicechat.sending_ping": "Sending ping...",
"message.voicechat.ping_sent_waiting": "Ping sent. Waiting for response...",
"message.voicechat.ping_received": "Got a response in %sms",
"message.voicechat.ping_received_attempt": "Got a response after %s attempts in %sms",
"message.voicechat.ping_received": "Got a response in %s ms",
"message.voicechat.ping_received_attempt": "Got a response in %s ms after %s attempts",
"message.voicechat.ping_retry": "No response. Retrying...",
"message.voicechat.ping_timed_out": "Request timed out after %s attempts",
"message.voicechat.icons_hidden": "Voice chat icons hidden",
"message.voicechat.icons_visible": "Voice chat icons visible",
"message.voicechat.incompatible_version": "Your voice chat version is not compatible with the servers version.\nPlease install version %s of %s.",
"message.voicechat.incompatible": "Your voice chat version is not compatible with the servers version.",
"message.voicechat.group_members": "Group members",
"message.voicechat.no_group_members": "This group is empty",
"message.voicechat.incompatible_version": "Your voice chat client version is not compatible with the server-side version.\nPlease install version %s of %s.",
"message.voicechat.incompatible": "Your voice chat client version is not compatible with the server-side version.",
"message.voicechat.group_members": "Group members:",
"message.voicechat.no_group_members": "Empty group",
"message.voicechat.join_create_group": "Join or Create Group",
"message.voicechat.group_name": "Group name",
"message.voicechat.leave_group": "Leave group",
"message.voicechat.group_name": "Group Name",
"message.voicechat.leave_group": "Press to leave the group.",
"message.voicechat.not_in_group": "You are not in a group",
"message.voicechat.invite": "%s invited you to the group %s. %s",
"message.voicechat.accept_invite": "ACCEPT",
"message.voicechat.accept_invite.hover": "Click to accept invitation",
"message.voicechat.join_successful": "Successfully joined %s",
"message.voicechat.accept_invite.hover": "Click to accept the invitation",
"message.voicechat.join_successful": "Successfully joined the group %s",
"message.voicechat.leave_successful": "Successfully left the group",
"message.voicechat.groups_disabled": "Groups are disabled on this server",
"message.voicechat.recording_disabled": "Recording is disabled on this server",
"message.voicechat.invite_successful": "Successfully invited %s",
"message.voicechat.processing_recording_session": "Processing recording session...",
"message.voicechat.processing_progress": "Processing %s%%",
"message.voicechat.save_session": "Saved session to '%s'",
"message.voicechat.save_session": "Saved session to %s",
"message.voicechat.save_session_failed": "Failed to save recording session: %s",
"message.voicechat.recording_started": "Started Recording",
"message.voicechat.recording_stopped": "Stopped Recording",
"message.voicechat.open_folder": "Open Folder",
"message.voicechat.storage_size": "Expected Storage Size %s",
"message.voicechat.saved_debug_report": "Saved voice chat debug report to %s",
"message.voicechat.open": "Open",
"message.voicechat.saved_debug_report_failed": "Failed to save voice chat debug report: %s",
"message.voicechat.open_folder": "Open folder",
"message.voicechat.storage_size": "Estimated disk space usage: %s.",
"message.voicechat.microphone_unavailable": "Microphone unavailable",
"message.voicechat.speaker_unavailable": "Speaker unavailable",
"message.voicechat.playback_unavailable": "Playback unavailable",
"message.voicechat.denoiser": "Noise suppression: %s",
"message.voicechat.enabled": "Enabled",
"message.voicechat.disabled": "Disabled",
"message.voicechat.denoiser": "Noise Suppression: %s",
"message.voicechat.denoiser.on": "ON",
"message.voicechat.denoiser.off": "OFF",
"message.voicechat.group_type_title": "%s (%s)",
"message.voicechat.group_title": "%s",
"message.voicechat.group_does_not_exist": "This group does not exist",
"message.voicechat.group_does_not_exist": "Such a group does not exist",
"message.voicechat.group_name_not_unique": "Group name is ambiguous",
"message.voicechat.create": "Create",
"message.voicechat.create_group": "Create a Group",
"message.voicechat.create_group_button": "Create a group",
"message.voicechat.create_group": "Create Group",
"message.voicechat.create_group_button": "Create Group",
"message.voicechat.optional_password": "Password (Optional)",
"message.voicechat.group_type": "Group type",
"message.voicechat.group_type": "Group Type",
"message.voicechat.group_type.normal": "Normal",
"message.voicechat.group_type.normal.description": "Players that are not in your group can't hear you, but you can hear them",
"message.voicechat.group_type.normal.description": "Players who are not in your group can't hear you, but you can hear them.",
"message.voicechat.group_type.open": "Open",
"message.voicechat.group_type.open.description": "Players that are not in your group can hear you and you can hear them",
"message.voicechat.group_type.open.description": "Players who are not in your group can hear you, and you can hear them too.",
"message.voicechat.group_type.isolated": "Isolated",
"message.voicechat.group_type.isolated.description": "Players that are not in your group can't hear you and you can't hear them",
"message.voicechat.join_group": "Join group",
"message.voicechat.group_type.isolated.description": "Players who are not in your group can't hear you, and you can't hear them either.",
"message.voicechat.join_group": "Join Group",
"message.voicechat.enter_group_password": "Enter Group Password",
"message.voicechat.password": "Password",
"message.voicechat.wrong_password": "Wrong Password",
"message.voicechat.wrong_password": "Incorrect Password",
"message.voicechat.server_port": "Voice chat server hosted on port %s",
"message.voicechat.macos_no_mic_permission": "No microphone permission. Please check Security & Privacy settings",
"message.voicechat.macos_unsupported_launcher": "Your launcher does not support MacOS microphone permissions",
"message.voicechat.macos_no_mic_permission": "No permission to access your microphone. Please open System Settings > Privacy & Security and check the access options there",
"message.voicechat.macos_unsupported_launcher": "Your launcher does not support macOS microphone permissions",
"message.voicechat.player_no_voicechat": "%s does not have %s installed",
"message.voicechat.mute.enabled": "Press to unmute your microphone",
"message.voicechat.mute.disabled": "Press to mute your microphone",
"message.voicechat.mute.disabled_ptt": "You can't mute when using push to talk",
"message.voicechat.disable.enabled": "Press to enable all voice chat sounds",
"message.voicechat.disable.disabled": "Press to disable all voice chat sounds",
"message.voicechat.disable.no_speaker": "No speaker available",
"message.voicechat.hide_icons.enabled": "Press to show all voice chat icons",
"message.voicechat.hide_icons.disabled": "Press to hide all voice chat icons",
"message.voicechat.show_group_hud.enabled": "Press to hide the group chat HUD",
"message.voicechat.show_group_hud.disabled": "Press to show the group chat HUD",
"message.voicechat.recording.disabled": "Press to start recording",
"message.voicechat.recording.enabled": "Press to stop recording",
"message.voicechat.mute.enabled": "Press to unmute your microphone.",
"message.voicechat.mute.disabled": "Press to mute your microphone.",
"message.voicechat.mute.disabled_ptt": "You can't mute your microphone when using push to talk.",
"message.voicechat.disable.enabled": "Press to enable all voice chat sounds.",
"message.voicechat.disable.disabled": "Press to disable all voice chat sounds.",
"message.voicechat.disable.no_speaker": "No speakers available.",
"message.voicechat.hide_icons.enabled": "Press to show all voice chat icons.",
"message.voicechat.hide_icons.disabled": "Press to hide all voice chat icons.",
"message.voicechat.show_group_hud.enabled": "Press to hide the group chat HUD.",
"message.voicechat.show_group_hud.disabled": "Press to show the group chat HUD.",
"message.voicechat.recording.disabled": "Press to start recording.",
"message.voicechat.recording.enabled": "Press to stop recording.",
"message.voicechat.no_speak_permission": "You do not have permission to speak",
"message.voicechat.no_listen_permission": "You do not have permission to hear the voice chat",
"message.voicechat.no_group_permission": "You do not have permission to join groups",
"message.voicechat.search_hint": "Search...",
"message.voicechat.search_empty": "Couldn't find any players with that name",
"message.voicechat.other_volume": "Other",
"message.voicechat.other_volume.description": "The volume of any sound not listed below",
"message.voicechat.other_volume.description": "The volume of any sound not listed below.",
"message.voicechat.more_members": "and %s more...",
"message.voicechat.no_groups": "No existing groups",
"message.voicechat.audio_type": "3D audio: %s",
"message.voicechat.audio_type": "3D Audio: %s",
"message.voicechat.audio_type.normal": "Normal",
"message.voicechat.audio_type.reduced": "Reduced",
"message.voicechat.audio_type.off": "Off",
"message.voicechat.invite_player": "Invite %s to your group",
"message.voicechat.audio_type.off": "OFF",
"message.voicechat.invite_player": "Press to invite %s to your group.",
"message.voicechat.press_to_reassign_key": "Press to assign a key.",
"message.voicechat.set_up": "Press %s to set up",
"message.voicechat.onboarding.reset": "Onboarding status has been reset",
"message.voicechat.onboarding.next": "Next",
"message.voicechat.onboarding.confirm": "Confirm",
"message.voicechat.onboarding.back": "Back",
"message.voicechat.onboarding.cancel": "Cancel",
"message.voicechat.onboarding.introduction.title": "Set Up %s",
"message.voicechat.onboarding.introduction.description": "This setup guide will help you configure your voice chat.\n\nYou can skip it if you know what you are doing.",
"message.voicechat.onboarding.introduction.skip": "I Know What I Am Doing - Skip",
"message.voicechat.onboarding.skip.title": "Skip Setup",
"message.voicechat.onboarding.skip.description": "Are you sure you want to skip this setup guide?\n\nIf you skip it, you will have to adjust all the settings manually!",
"message.voicechat.onboarding.microphone": "Select Microphone",
"message.voicechat.onboarding.speaker": "Select Speaker",
"message.voicechat.onboarding.activation.title": "Select Activation Method",
"message.voicechat.onboarding.activation": "Please choose the method to activate your microphone.",
"message.voicechat.onboarding.activation.ptt": "When selecting %s, you need to press and hold a button to speak.",
"message.voicechat.onboarding.activation.ptt.name": "Push to Talk",
"message.voicechat.onboarding.activation.voice": "%s automatically activates your microphone when a certain volume threshold is reached.",
"message.voicechat.onboarding.activation.voice.name": "Voice Activation",
"message.voicechat.onboarding.ptt.title": "Choose Push to Talk Keybind",
"message.voicechat.onboarding.ptt.description": "Please choose the key that should be used to activate your microphone.",
"message.voicechat.onboarding.ptt.button_description": "Press to assign",
"message.voicechat.onboarding.voice.title": "Adjust Microphone Activation Threshold",
"message.voicechat.onboarding.voice.description": "Enable microphone testing and make sure the volume is below the slider when you are silent and above it when you are speaking.",
"message.voicechat.onboarding.final": "Finish Setup",
"message.voicechat.onboarding.final.description.success": "You have successfully configured your voice chat!\n\nOpen the voice chat menu with %s.",
"message.voicechat.onboarding.final.description.ptt": "Hold %s while speaking to use the voice chat.",
"message.voicechat.onboarding.final.description.voice": "You will be muted after exiting this screen. Press %s to unmute.",
"message.voicechat.onboarding.final.description.configuration": "You can always change all the settings in the voice chat menu.\nKeybinds, however, can be changed just like any other keybind in the game.",
"message.voicechat.onboarding.final.finish_setup": "Finish Setup",
"resourcepack.voicechat.classic_icons": "Classic Icons",
"resourcepack.voicechat.classic_icons.description": "Classic Voice Chat Icons",
"resourcepack.voicechat.classic_icons.description": "Classic voice chat icons",
"resourcepack.voicechat.white_icons": "White Icons",
"resourcepack.voicechat.white_icons.description": "White Voice Chat Icons",
"resourcepack.voicechat.white_icons.description": "White voice chat icons",
"resourcepack.voicechat.black_icons": "Black Icons",
"resourcepack.voicechat.black_icons.description": "Black Voice Chat Icons",
"resourcepack.voicechat.black_icons.description": "Black voice chat icons",
"cloth_config.voicechat.settings": "Voice Chat Settings",
"cloth_config.voicechat.category.general": "General",
"cloth_config.voicechat.category.other": "Other",
"cloth_config.voicechat.category.audio": "Audio",
"cloth_config.voicechat.category.hud_icons": "HUD Icons",
"cloth_config.voicechat.category.group_chat_icons": "Group Chat Icons",
"cloth_config.voicechat.category.hud_icons": "Voice Chat HUD",
"cloth_config.voicechat.category.group_chat_icons": "Group Chat HUD",
"cloth_config.voicechat.category.ingame_menu": "In-Game Menu",
"cloth_config.voicechat.config.recording_destination": "Recording destination",
"cloth_config.voicechat.config.run_local_server": "Run in singleplayer/LAN worlds",
"cloth_config.voicechat.config.recording_destination.description": "The location where recordings should be saved.\nLeave blank to use the default location.",
"cloth_config.voicechat.config.run_local_server": "Run in singleplayer or LAN worlds",
"cloth_config.voicechat.config.run_local_server.description": "If the voice chat should work in singleplayer or in worlds shared over LAN.",
"cloth_config.voicechat.config.offline_player_volume_adjustment": "Offline player volume adjustment",
"cloth_config.voicechat.config.freecam_support": "Freecam support",
"cloth_config.voicechat.config.offline_player_volume_adjustment.description": "If the volume adjustment interface should also display offline players.",
"cloth_config.voicechat.config.freecam_mode": "Freecam mode",
"cloth_config.voicechat.config.freecam_mode.description": "How listening to other players should work when using freecam mods.\nCAMERA: You will hear the voice chat around your camera. Whether you will still be able to hear the voice chat when the camera is far away from your character depends on the voice chat broadcast range of the server.\nPLAYER: You will hear the voice chat around your character no matter where your camera is.",
"cloth_config.voicechat.config.freecam_mode.camera": "Camera",
"cloth_config.voicechat.config.freecam_mode.player": "Player",
"cloth_config.voicechat.config.mute_on_join": "Mute on join",
"cloth_config.voicechat.config.mute_on_join.description": "If enabled, you will be automatically muted when joining a world.",
"cloth_config.voicechat.config.audio_packet_threshold": "Audio packet threshold",
"cloth_config.voicechat.config.deactivation_delay": "Deactivation delay",
"cloth_config.voicechat.config.audio_packet_threshold.description": "The maximum number of audio packets that should be held back if a packet arrives out of order or is dropped.\nThis prevents audio packets that are only slightly out of order from being discarded.\nSet this to 0 to disable.",
"cloth_config.voicechat.config.voice_deactivation_delay": "Microphone deactivation delay",
"cloth_config.voicechat.config.voice_deactivation_delay.description": "The time it takes for the microphone to deactivate when using voice activation.\nA value of 1 means 20 milliseconds, 2=40 ms, 3=60 ms, and so on.",
"cloth_config.voicechat.config.output_buffer_size": "Output buffer size",
"cloth_config.voicechat.config.hud_icon_scale": "HUD icon scale",
"cloth_config.voicechat.config.hud_icon_x": "HUD icon X pos",
"cloth_config.voicechat.config.hud_icon_y": "HUD icon Y pos",
"cloth_config.voicechat.config.group_player_icon_orientation": "Group HUD icon orientation",
"cloth_config.voicechat.config.group_hud_icon_scale": "Group HUD icon scale",
"cloth_config.voicechat.config.group_player_icon_pos_x": "Group icon X position",
"cloth_config.voicechat.config.group_player_icon_pos_y": "Group icon Y position",
"cloth_config.voicechat.config.show_own_group_icon": "Show own group HUD icon"
"cloth_config.voicechat.config.output_buffer_size.description": "The size of the audio output buffer (in packets).\nHigher values mean a higher latency but less crackling.\nIncrease this value if you have an unstable internet connection.",
"cloth_config.voicechat.config.hud_icon_scale": "Voice chat HUD icon scale",
"cloth_config.voicechat.config.hud_icon_scale.description": "The scale of the icons in the voice chat HUD, such as microphone or connection status.",
"cloth_config.voicechat.config.hud_icon_pos_x": "Voice chat HUD icon X position",
"cloth_config.voicechat.config.hud_icon_pos_x.description": "The X position of the icons in the voice chat HUD.\nNegative values mean anchoring to the right instead.",
"cloth_config.voicechat.config.hud_icon_pos_y": "Voice chat HUD icon Y position",
"cloth_config.voicechat.config.hud_icon_pos_y.description": "The Y position of the icons in the voice chat HUD.\nNegative values mean anchoring to the bottom instead.",
"cloth_config.voicechat.config.group_player_icon_orientation": "Group chat HUD icon orientation",
"cloth_config.voicechat.config.group_player_icon_orientation.description": "The orientation of the player icons in the group chat HUD.",
"cloth_config.voicechat.config.group_player_icon_orientation.vertical": "Vertical",
"cloth_config.voicechat.config.group_player_icon_orientation.horizontal": "Horizontal",
"cloth_config.voicechat.config.group_hud_icon_scale": "Group chat HUD icon scale",
"cloth_config.voicechat.config.group_hud_icon_scale.description": "The scale of the player icons in the group chat HUD.",
"cloth_config.voicechat.config.group_player_icon_pos_x": "Group chat HUD icon X position",
"cloth_config.voicechat.config.group_player_icon_pos_x.description": "The X position of the player icons in the group chat HUD.\nNegative values mean anchoring to the right instead.",
"cloth_config.voicechat.config.group_player_icon_pos_y": "Group chat HUD icon Y position",
"cloth_config.voicechat.config.group_player_icon_pos_y.description": "The Y position of the player icons in the group chat HUD.\nNegative values mean anchoring to the bottom instead.",
"cloth_config.voicechat.config.show_own_group_icon": "Show yourself in the group chat HUD",
"cloth_config.voicechat.config.show_own_group_icon.description": "If your own player icon should be displayed in the group chat HUD when you are in a group."
}