Merge pull request #99 from TBMCPlugins/dev
Updated to Discord4J v3, permission injection, improvements
This commit is contained in:
commit
bc160a21b7
48 changed files with 2750 additions and 2422 deletions
447
pom.xml
447
pom.xml
|
@ -1,208 +1,225 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>com.github.TBMCPlugins</groupId>
|
<groupId>com.github.TBMCPlugins</groupId>
|
||||||
<artifactId>DiscordPlugin</artifactId>
|
<artifactId>DiscordPlugin</artifactId>
|
||||||
<version>master-SNAPSHOT</version>
|
<version>master-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>DiscordPlugin</name>
|
<name>DiscordPlugin</name>
|
||||||
<url>http://maven.apache.org</url>
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<!-- <sourceDirectory>target/generated-sources/delombok</sourceDirectory>
|
<!-- <sourceDirectory>target/generated-sources/delombok</sourceDirectory>
|
||||||
<testSourceDirectory>target/generated-test-sources/delombok</testSourceDirectory> -->
|
<testSourceDirectory>target/generated-test-sources/delombok</testSourceDirectory> -->
|
||||||
<sourceDirectory>src/main/java</sourceDirectory>
|
<sourceDirectory>src/main/java</sourceDirectory>
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
<resource>
|
||||||
<directory>src</directory>
|
<directory>src</directory>
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>**/*.java</exclude>
|
<exclude>**/*.java</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</resource>
|
</resource>
|
||||||
<resource>
|
<resource>
|
||||||
<directory>src/main/resources</directory>
|
<directory>src/main/resources</directory>
|
||||||
<includes>
|
<includes>
|
||||||
<include>*.properties</include>
|
<include>*.properties</include>
|
||||||
<include>*.yml</include>
|
<include>*.yml</include>
|
||||||
<include>*.csv</include>
|
<include>*.csv</include>
|
||||||
<include>*.txt</include>
|
<include>*.txt</include>
|
||||||
</includes>
|
</includes>
|
||||||
<filtering>true</filtering>
|
<filtering>true</filtering>
|
||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
<finalName>DiscordPlugin</finalName>
|
<finalName>DiscordPlugin</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.6.2</version>
|
<version>3.6.2</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>1.8</source>
|
<source>1.8</source>
|
||||||
<target>1.8</target>
|
<target>1.8</target>
|
||||||
</configuration>
|
<!-- <compilerArgs>
|
||||||
</plugin>
|
<arg>-processor</arg>
|
||||||
<plugin>
|
<arg>buttondevteam.buttonproc.ButtonProcessor, lombok.core.AnnotationProcessor</arg>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
</compilerArgs> -->
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<!-- <annotationProcessors>
|
||||||
<version>2.4.2</version>
|
<annotationProcessor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</annotationProcessor>
|
||||||
<executions>
|
<annotationProcessor>buttondevteam.buttonproc.ButtonProcessor</annotationProcessor>
|
||||||
<execution>
|
</annotationProcessors> -->
|
||||||
<phase>package</phase>
|
</configuration>
|
||||||
<goals>
|
</plugin>
|
||||||
<goal>shade</goal>
|
<plugin>
|
||||||
</goals>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<configuration>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<artifactSet>
|
<version>2.4.2</version>
|
||||||
<excludes>
|
<executions>
|
||||||
<exclude>org.spigotmc:spigot-api</exclude>
|
<execution>
|
||||||
<exclude>com.github.TBMCPlugins.ButtonCore:ButtonCore</exclude>
|
<phase>package</phase>
|
||||||
<exclude>net.ess3:Essentials</exclude>
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<artifactSet>
|
||||||
|
<excludes>
|
||||||
|
<exclude>org.spigotmc:spigot-api</exclude>
|
||||||
|
<exclude>com.github.TBMCPlugins.ButtonCore:ButtonCore</exclude>
|
||||||
|
<exclude>net.ess3:Essentials</exclude>
|
||||||
</excludes> <!-- http://stackoverflow.com/questions/28458058/maven-shade-plugin-exclude-a-dependency-and-all-its-transitive-dependencies -->
|
</excludes> <!-- http://stackoverflow.com/questions/28458058/maven-shade-plugin-exclude-a-dependency-and-all-its-transitive-dependencies -->
|
||||||
</artifactSet>
|
</artifactSet>
|
||||||
</configuration>
|
<minimizeJar>true</minimizeJar>
|
||||||
</execution>
|
<relocations>
|
||||||
</executions>
|
<relocation>
|
||||||
</plugin>
|
<pattern>io.netty</pattern>
|
||||||
<plugin>
|
<shadedPattern>btndvtm.dp.io.netty</shadedPattern>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<excludes>
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
</excludes>
|
||||||
<version>3.0.1</version>
|
</relocation>
|
||||||
<executions>
|
</relocations>
|
||||||
<execution>
|
</configuration>
|
||||||
<id>copy</id>
|
</execution>
|
||||||
<phase>compile</phase>
|
</executions>
|
||||||
<goals>
|
</plugin>
|
||||||
<goal>copy-resources</goal>
|
<plugin>
|
||||||
</goals>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<configuration>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
<outputDirectory>target</outputDirectory>
|
<version>3.0.1</version>
|
||||||
<resources>
|
<executions>
|
||||||
<resource>
|
<execution>
|
||||||
<directory>resources</directory>
|
<id>copy</id>
|
||||||
</resource>
|
<phase>compile</phase>
|
||||||
</resources>
|
<goals>
|
||||||
</configuration>
|
<goal>copy-resources</goal>
|
||||||
</execution>
|
</goals>
|
||||||
</executions>
|
<configuration>
|
||||||
</plugin>
|
<outputDirectory>target</outputDirectory>
|
||||||
<!-- <plugin> <groupId>org.projectlombok</groupId> <artifactId>lombok-maven-plugin</artifactId>
|
<resources>
|
||||||
<version>1.16.16.0</version> <executions> <execution> <id>delombok</id> <phase>generate-sources</phase>
|
<resource>
|
||||||
<goals> <goal>delombok</goal> </goals> <configuration> <addOutputDirectory>false</addOutputDirectory>
|
<directory>resources</directory>
|
||||||
<sourceDirectory>src/main/java</sourceDirectory> <verbose>true</verbose>
|
</resource>
|
||||||
</configuration> </execution> <execution> <id>test-delombok</id> <phase>generate-test-sources</phase>
|
</resources>
|
||||||
<goals> <goal>testDelombok</goal> </goals> <configuration> <addOutputDirectory>false</addOutputDirectory>
|
</configuration>
|
||||||
<sourceDirectory>src/test/java</sourceDirectory> </configuration> </execution>
|
</execution>
|
||||||
</executions> </plugin> -->
|
</executions>
|
||||||
<plugin>
|
</plugin>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<!-- <plugin> <groupId>org.projectlombok</groupId> <artifactId>lombok-maven-plugin</artifactId>
|
||||||
<configuration>
|
<version>1.16.16.0</version> <executions> <execution> <id>delombok</id> <phase>generate-sources</phase>
|
||||||
<useSystemClassLoader>false
|
<goals> <goal>delombok</goal> </goals> <configuration> <addOutputDirectory>false</addOutputDirectory>
|
||||||
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
|
<sourceDirectory>src/main/java</sourceDirectory> <verbose>true</verbose>
|
||||||
</configuration>
|
</configuration> </execution> <execution> <id>test-delombok</id> <phase>generate-test-sources</phase>
|
||||||
</plugin>
|
<goals> <goal>testDelombok</goal> </goals> <configuration> <addOutputDirectory>false</addOutputDirectory>
|
||||||
</plugins>
|
<sourceDirectory>src/test/java</sourceDirectory> </configuration> </execution>
|
||||||
</build>
|
</executions> </plugin> -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.4.2</version>
|
||||||
|
<configuration>
|
||||||
|
<useSystemClassLoader>false
|
||||||
|
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<branch>
|
<branch>
|
||||||
master
|
master
|
||||||
</branch> <!-- Should be master if building ButtonCore locally - the CI will overwrite it (see below) -->
|
</branch> <!-- Should be master if building ButtonCore locally - the CI will overwrite it (see below) -->
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>spigot-repo</id>
|
<id>spigot-repo</id>
|
||||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository> <!-- This repo fixes issues with transitive dependencies -->
|
<repository> <!-- This repo fixes issues with transitive dependencies -->
|
||||||
<id>jcenter</id>
|
<id>jcenter</id>
|
||||||
<url>http://jcenter.bintray.com</url>
|
<url>http://jcenter.bintray.com</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>jitpack.io</id>
|
<id>jitpack.io</id>
|
||||||
<url>https://jitpack.io</url>
|
<url>https://jitpack.io</url>
|
||||||
</repository>
|
</repository>
|
||||||
<!-- <repository>
|
<!-- <repository>
|
||||||
<id>vault-repo</id>
|
<id>vault-repo</id>
|
||||||
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
|
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
|
||||||
</repository> -->
|
</repository> -->
|
||||||
<repository>
|
<repository>
|
||||||
<id>Essentials</id>
|
<id>Essentials</id>
|
||||||
<url>http://repo.ess3.net/content/repositories/essrel/</url>
|
<url>http://repo.ess3.net/content/repositories/essrel/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>projectlombok.org</id>
|
<id>projectlombok.org</id>
|
||||||
<url>http://projectlombok.org/mavenrepo</url>
|
<url>http://projectlombok.org/mavenrepo</url>
|
||||||
</repository>
|
</repository>
|
||||||
<!-- <repository>
|
<!-- <repository>
|
||||||
<id>pex-repo</id>
|
<id>pex-repo</id>
|
||||||
<url>http://pex-repo.aoeu.xyz</url>
|
<url>http://pex-repo.aoeu.xyz</url>
|
||||||
</repository> -->
|
</repository> -->
|
||||||
</repositories>
|
<!-- <repository>
|
||||||
|
<id>Reactor-Tools</id>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
</repository> -->
|
||||||
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>3.8.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.12-R0.1-SNAPSHOT</version>
|
<version>1.12-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot</artifactId>
|
<artifactId>spigot</artifactId>
|
||||||
<version>1.12.2-R0.1-SNAPSHOT</version>
|
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/com.discord4j/Discord4J -->
|
<!-- https://mvnrepository.com/artifact/com.discord4j/Discord4J -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.discord4j</groupId>
|
<groupId>com.discord4j</groupId>
|
||||||
<artifactId>Discord4J</artifactId>
|
<artifactId>discord4j-core</artifactId>
|
||||||
<version>2.10.1</version>
|
<version>3.0.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 -->
|
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-jdk14</artifactId>
|
<artifactId>slf4j-jdk14</artifactId>
|
||||||
<version>1.7.21</version>
|
<version>1.7.21</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.TBMCPlugins.ButtonCore</groupId>
|
<groupId>com.github.TBMCPlugins.ButtonCore</groupId>
|
||||||
<artifactId>ButtonCore</artifactId>
|
<artifactId>ButtonCore</artifactId>
|
||||||
<version>${branch}-SNAPSHOT</version>
|
<version>${branch}-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.milkbowl</groupId> <!-- net.milkbowl.vault -->
|
|
||||||
<artifactId>VaultAPI</artifactId>
|
|
||||||
<version>master-SNAPSHOT</version> <!-- 1.6 -->
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.ess3</groupId>
|
|
||||||
<artifactId>Essentials</artifactId>
|
|
||||||
<version>2.13.1</version>
|
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.xaanit</groupId>
|
<groupId>com.github.milkbowl</groupId> <!-- net.milkbowl.vault -->
|
||||||
<artifactId>D4J-OAuth</artifactId>
|
<artifactId>VaultAPI</artifactId>
|
||||||
<version>master-SNAPSHOT</version>
|
<version>master-SNAPSHOT</version> <!-- 1.6 -->
|
||||||
</dependency>
|
<scope>provided</scope>
|
||||||
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>net.ess3</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>Essentials</artifactId>
|
||||||
<version>1.16.16</version>
|
<version>2.13.1</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.16.16</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
<!-- <dependency>
|
<!-- <dependency>
|
||||||
<groupId>ru.tehkode</groupId>
|
<groupId>ru.tehkode</groupId>
|
||||||
<artifactId>PermissionsEx</artifactId>
|
<artifactId>PermissionsEx</artifactId>
|
||||||
|
@ -215,32 +232,44 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency> -->
|
</dependency> -->
|
||||||
<!-- https://mvnrepository.com/artifact/org.objenesis/objenesis -->
|
<!-- https://mvnrepository.com/artifact/org.objenesis/objenesis -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.objenesis</groupId>
|
<groupId>org.objenesis</groupId>
|
||||||
<artifactId>objenesis</artifactId>
|
<artifactId>objenesis</artifactId>
|
||||||
<version>2.6</version>
|
<version>2.6</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.vdurmont</groupId>
|
<groupId>com.vdurmont</groupId>
|
||||||
<artifactId>emoji-java</artifactId>
|
<artifactId>emoji-java</artifactId>
|
||||||
<version>4.0.0</version>
|
<version>4.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
<!-- https://mvnrepository.com/artifact/io.projectreactor.tools/blockhound -->
|
||||||
|
<!-- <dependency>
|
||||||
|
<groupId>io.projectreactor.tools</groupId>
|
||||||
|
<artifactId>blockhound</artifactId>
|
||||||
|
<version>1.0.0.M3</version>
|
||||||
|
</dependency> -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.lucko</groupId>
|
||||||
|
<artifactId>LuckPerms</artifactId>
|
||||||
|
<version>v4.4</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>ci</id>
|
<id>ci</id>
|
||||||
<activation>
|
<activation>
|
||||||
<property>
|
<property>
|
||||||
<name>env.TRAVIS_BRANCH</name>
|
<name>env.TRAVIS_BRANCH</name>
|
||||||
</property>
|
</property>
|
||||||
</activation>
|
</activation>
|
||||||
<properties>
|
<properties>
|
||||||
<!-- Override only if necessary -->
|
<!-- Override only if necessary -->
|
||||||
<branch>${env.TRAVIS_BRANCH}</branch>
|
<branch>${env.TRAVIS_BRANCH}</branch>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
package buttondevteam.discordplugin;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.bukkit.event.Cancellable;
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class AsyncDiscordEvent<T extends sx.blah.discord.api.events.Event> extends Event implements Cancellable {
|
|
||||||
private final @Getter T event;
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private boolean cancelled;
|
|
||||||
|
|
||||||
private static final HandlerList handlers = new HandlerList();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandlerList getHandlers() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,14 @@
|
||||||
package buttondevteam.discordplugin;
|
package buttondevteam.discordplugin;
|
||||||
|
|
||||||
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
||||||
|
import discord4j.core.object.entity.Message;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.scheduler.BukkitScheduler;
|
import org.bukkit.scheduler.BukkitScheduler;
|
||||||
import sx.blah.discord.api.internal.json.objects.EmbedObject;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.util.EmbedBuilder;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.awt.*;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class ChromaBot {
|
public class ChromaBot {
|
||||||
/**
|
/**
|
||||||
|
@ -33,113 +32,26 @@ public class ChromaBot {
|
||||||
instance = null;
|
instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a message to the chat channel and private chats.
|
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
* The message to send, duh
|
|
||||||
*/
|
|
||||||
public void sendMessage(String message) {
|
|
||||||
MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message to the chat channels and private chats.
|
* Send a message to the chat channels and private chats.
|
||||||
*
|
*
|
||||||
* @param message
|
* @param message
|
||||||
* The message to send, duh
|
* The message to send, duh (use {@link MessageChannel#createMessage(String)})
|
||||||
* @param embed
|
|
||||||
* Custom fancy stuff, use {@link EmbedBuilder} to create one
|
|
||||||
*/
|
*/
|
||||||
public void sendMessage(String message, EmbedObject embed) {
|
public void sendMessage(Function<Mono<MessageChannel>, Mono<Message>> message) {
|
||||||
MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed));
|
MCChatUtils.forAllMCChat(ch -> message.apply(ch).subscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message to the chat channels, private chats and custom chats.
|
* Send a message to the chat channels, private chats and custom chats.
|
||||||
*
|
*
|
||||||
* @param message The message to send, duh
|
* @param message The message to send, duh
|
||||||
* @param embed Custom fancy stuff, use {@link EmbedBuilder} to create one
|
|
||||||
* @param toggle The toggle type for channelcon
|
* @param toggle The toggle type for channelcon
|
||||||
*/
|
*/
|
||||||
public void sendMessageCustomAsWell(String message, EmbedObject embed, @Nullable ChannelconBroadcast toggle) {
|
public void sendMessageCustomAsWell(Function<Mono<MessageChannel>, Mono<Message>> message, @Nullable ChannelconBroadcast toggle) {
|
||||||
MCChatUtils.forCustomAndAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed), toggle, false);
|
MCChatUtils.forCustomAndAllMCChat(ch -> message.apply(ch).subscribe(), toggle, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a message to an arbitrary channel. This will not send it to the private chats.
|
|
||||||
*
|
|
||||||
* @param channel
|
|
||||||
* The channel to send to, use the channel variables in {@link DiscordPlugin}
|
|
||||||
* @param message
|
|
||||||
* The message to send, duh
|
|
||||||
* @param embed
|
|
||||||
* Custom fancy stuff, use {@link EmbedBuilder} to create one
|
|
||||||
*/
|
|
||||||
public void sendMessage(IChannel channel, String message, EmbedObject embed) {
|
|
||||||
DiscordPlugin.sendMessageToChannel(channel, message, embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a fancy message to the chat channels. This will show a bold text with a colored line.
|
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
* The message to send, duh
|
|
||||||
* @param color
|
|
||||||
* The color of the line before the text
|
|
||||||
*/
|
|
||||||
public void sendMessage(String message, Color color) {
|
|
||||||
MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message,
|
|
||||||
new EmbedBuilder().withTitle(message).withColor(color).build()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a fancy message to the chat channels. This will show a bold text with a colored line.
|
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
* The message to send, duh
|
|
||||||
* @param color
|
|
||||||
* The color of the line before the text
|
|
||||||
* @param mcauthor
|
|
||||||
* The name of the Minecraft player who is the author of this message
|
|
||||||
*/
|
|
||||||
public void sendMessage(String message, Color color, String mcauthor) {
|
|
||||||
MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message,
|
|
||||||
DPUtils.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), mcauthor).build()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a fancy message to the chat channels. This will show a bold text with a colored line.
|
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
* The message to send, duh
|
|
||||||
* @param color
|
|
||||||
* The color of the line before the text
|
|
||||||
* @param authorname
|
|
||||||
* The name of the author of this message
|
|
||||||
* @param authorimg
|
|
||||||
* The URL of the avatar image for this message's author
|
|
||||||
*/
|
|
||||||
public void sendMessage(String message, Color color, String authorname, String authorimg) {
|
|
||||||
MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder()
|
|
||||||
.withTitle(message).withColor(color).withAuthorName(authorname).withAuthorIcon(authorimg).build()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a message to the chat channels. This will show a bold text with a colored line.
|
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
* The message to send, duh
|
|
||||||
* @param color
|
|
||||||
* The color of the line before the text
|
|
||||||
* @param sender
|
|
||||||
* The player who sends this message
|
|
||||||
*/
|
|
||||||
public void sendMessage(String message, Color color, Player sender) {
|
|
||||||
MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils
|
|
||||||
.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), sender.getName()).build()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updatePlayerList() {
|
public void updatePlayerList() {
|
||||||
MCChatUtils.updatePlayerList();
|
MCChatUtils.updatePlayerList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,128 +4,82 @@ import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
import buttondevteam.lib.architecture.IHaveConfig;
|
import buttondevteam.lib.architecture.IHaveConfig;
|
||||||
|
import buttondevteam.lib.architecture.ReadOnlyConfigData;
|
||||||
|
import discord4j.core.object.entity.Guild;
|
||||||
|
import discord4j.core.object.entity.Message;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.Role;
|
||||||
|
import discord4j.core.object.util.Snowflake;
|
||||||
|
import discord4j.core.spec.EmbedCreateSpec;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IGuild;
|
|
||||||
import sx.blah.discord.handle.obj.IIDLinkedObject;
|
|
||||||
import sx.blah.discord.handle.obj.IRole;
|
|
||||||
import sx.blah.discord.util.EmbedBuilder;
|
|
||||||
import sx.blah.discord.util.RequestBuffer;
|
|
||||||
import sx.blah.discord.util.RequestBuffer.IRequest;
|
|
||||||
import sx.blah.discord.util.RequestBuffer.IVoidRequest;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
public final class DPUtils {
|
public final class DPUtils {
|
||||||
|
|
||||||
public static EmbedBuilder embedWithHead(EmbedBuilder builder, String playername) {
|
public static EmbedCreateSpec embedWithHead(EmbedCreateSpec ecs, String displayname, String playername, String profileUrl) {
|
||||||
return builder.withAuthorIcon("https://minotar.net/avatar/" + playername + "/32.png");
|
return ecs.setAuthor(displayname, profileUrl, "https://minotar.net/avatar/" + playername + "/32.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes §[char] colour codes from strings & escapes them for Discord <br>
|
|
||||||
* Ensure that this method only gets called once (escaping)
|
|
||||||
*/
|
|
||||||
public static String sanitizeString(String string) {
|
|
||||||
return escape(sanitizeStringNoEscape(string));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes §[char] colour codes from strings
|
|
||||||
*/
|
|
||||||
public static String sanitizeStringNoEscape(String string) {
|
|
||||||
String sanitizedString = "";
|
|
||||||
boolean random = false;
|
|
||||||
for (int i = 0; i < string.length(); i++) {
|
|
||||||
if (string.charAt(i) == '§') {
|
|
||||||
i++;// Skips the data value, the 4 in "§4Alisolarflare"
|
|
||||||
random = string.charAt(i) == 'k';
|
|
||||||
} else {
|
|
||||||
if (!random) // Skip random/obfuscated characters
|
|
||||||
sanitizedString += string.charAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sanitizedString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode.
|
* Removes §[char] colour codes from strings & escapes them for Discord <br>
|
||||||
|
* Ensure that this method only gets called once (escaping)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public static String sanitizeString(String string) {
|
||||||
public static <T> T perform(IRequest<T> action, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException {
|
return escape(sanitizeStringNoEscape(string));
|
||||||
if (DiscordPlugin.SafeMode)
|
}
|
||||||
return null;
|
|
||||||
if (Bukkit.isPrimaryThread()) // TODO: Ignore shutdown message <--
|
|
||||||
// throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
|
|
||||||
getLogger().warning("Waiting for a Discord request on the main thread!");
|
|
||||||
return RequestBuffer.request(action).get(timeout, unit); // Let the pros handle this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static <T> T perform(IRequest<T> action) {
|
|
||||||
if (DiscordPlugin.SafeMode)
|
|
||||||
return null;
|
|
||||||
if (Bukkit.isPrimaryThread()) // TODO: Ignore shutdown message <--
|
|
||||||
// throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
|
|
||||||
getLogger().warning("Waiting for a Discord request on the main thread!");
|
|
||||||
return RequestBuffer.request(action).get(); // Let the pros handle this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs Discord actions, retrying when ratelimited.
|
* Removes §[char] colour codes from strings
|
||||||
*/
|
*/
|
||||||
public static Void perform(IVoidRequest action) {
|
public static String sanitizeStringNoEscape(String string) {
|
||||||
if (DiscordPlugin.SafeMode)
|
StringBuilder sanitizedString = new StringBuilder();
|
||||||
return null;
|
boolean random = false;
|
||||||
if (Bukkit.isPrimaryThread())
|
for (int i = 0; i < string.length(); i++) {
|
||||||
throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
|
if (string.charAt(i) == '§') {
|
||||||
return RequestBuffer.request(action).get(); // Let the pros handle this
|
i++;// Skips the data value, the 4 in "§4Alisolarflare"
|
||||||
|
random = string.charAt(i) == 'k';
|
||||||
|
} else {
|
||||||
|
if (!random) // Skip random/obfuscated characters
|
||||||
|
sanitizedString.append(string.charAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sanitizedString.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void performNoWait(IVoidRequest action) {
|
private static String escape(String message) {
|
||||||
if (DiscordPlugin.SafeMode)
|
return message.replaceAll("([*_~])", Matcher.quoteReplacement("\\") + "$1");
|
||||||
return;
|
|
||||||
RequestBuffer.request(action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> void performNoWait(IRequest<T> action) {
|
|
||||||
if (DiscordPlugin.SafeMode)
|
|
||||||
return;
|
|
||||||
RequestBuffer.request(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String escape(String message) {
|
|
||||||
return message.replaceAll("([*_~])", Matcher.quoteReplacement("\\")+"$1");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Logger getLogger() {
|
public static Logger getLogger() {
|
||||||
if (DiscordPlugin.plugin == null || DiscordPlugin.plugin.getLogger() == null)
|
if (DiscordPlugin.plugin == null || DiscordPlugin.plugin.getLogger() == null)
|
||||||
return Logger.getLogger("DiscordPlugin");
|
return Logger.getLogger("DiscordPlugin");
|
||||||
return DiscordPlugin.plugin.getLogger();
|
return DiscordPlugin.plugin.getLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigData<IChannel> channelData(IHaveConfig config, String key, long defID) {
|
public static ReadOnlyConfigData<Mono<MessageChannel>> channelData(IHaveConfig config, String key, long defID) {
|
||||||
return config.getDataPrimDef(key, defID, id -> DiscordPlugin.dc.getChannelByID((long) id), IIDLinkedObject::getLongID); //We can afford to search for the channel in the cache once (instead of using mainServer)
|
return config.getReadOnlyDataPrimDef(key, defID, id -> getMessageChannel(key, Snowflake.of((Long) id)), ch -> defID); //We can afford to search for the channel in the cache once (instead of using mainServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigData<IRole> roleData(IHaveConfig config, String key, String defName) {
|
public static ReadOnlyConfigData<Mono<Role>> roleData(IHaveConfig config, String key, String defName) {
|
||||||
return roleData(config, key, defName, DiscordPlugin.mainServer);
|
return roleData(config, key, defName, Mono.just(DiscordPlugin.mainServer));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigData<IRole> roleData(IHaveConfig config, String key, String defName, IGuild guild) {
|
/**
|
||||||
return config.getDataPrimDef(key, defName, name -> {
|
* Needs to be a {@link ConfigData} for checking if it's set
|
||||||
if (!(name instanceof String)) return null;
|
*/
|
||||||
val roles = guild.getRolesByName((String) name);
|
public static ReadOnlyConfigData<Mono<Role>> roleData(IHaveConfig config, String key, String defName, Mono<Guild> guild) {
|
||||||
return roles.size() > 0 ? roles.get(0) : null;
|
return config.getReadOnlyDataPrimDef(key, defName, name -> {
|
||||||
}, IIDLinkedObject::getLongID);
|
if (!(name instanceof String)) return Mono.empty();
|
||||||
|
return guild.flatMapMany(Guild::getRoles).filter(r -> r.getName().equals(name)).next();
|
||||||
|
}, r -> defName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigData<Snowflake> snowflakeData(IHaveConfig config, String key, long defID) {
|
||||||
|
return config.getDataPrimDef(key, defID, id -> Snowflake.of((long) id), Snowflake::asLong);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,10 +88,8 @@ public final class DPUtils {
|
||||||
* @return The string for mentioning the channel
|
* @return The string for mentioning the channel
|
||||||
*/
|
*/
|
||||||
public static String botmention() {
|
public static String botmention() {
|
||||||
IChannel channel;
|
if (DiscordPlugin.plugin == null) return "#bot";
|
||||||
if (DiscordPlugin.plugin == null
|
return channelMention(DiscordPlugin.plugin.commandChannel().get());
|
||||||
|| (channel = DiscordPlugin.plugin.CommandChannel().get()) == null) return "#bot";
|
|
||||||
return channel.mention();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,23 +101,66 @@ public final class DPUtils {
|
||||||
*/
|
*/
|
||||||
public static boolean disableIfConfigError(@Nullable Component<DiscordPlugin> component, ConfigData<?>... configs) {
|
public static boolean disableIfConfigError(@Nullable Component<DiscordPlugin> component, ConfigData<?>... configs) {
|
||||||
for (val config : configs) {
|
for (val config : configs) {
|
||||||
if (config.get() == null) {
|
Object v = config.get();
|
||||||
String path = null;
|
if (disableIfConfigErrorRes(component, config, v))
|
||||||
try {
|
|
||||||
if (component != null)
|
|
||||||
Component.setComponentEnabled(component, false);
|
|
||||||
val f = ConfigData.class.getDeclaredField("path");
|
|
||||||
f.setAccessible(true); //Hacking my own plugin
|
|
||||||
path = (String) f.get(config);
|
|
||||||
} catch (Exception e) {
|
|
||||||
TBMCCoreAPI.SendException("Failed to disable component after config error!", e);
|
|
||||||
}
|
|
||||||
getLogger().warning("The config value " + path + " isn't set correctly " + (component == null ? "in global settings!" : "for component " + component.getClass().getSimpleName() + "!"));
|
|
||||||
getLogger().warning("Set the correct ID in the config" + (component == null ? "" : " or disable this component") + " to remove this message.");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the component if one of the given configs return null. Useful for channel/role configs.
|
||||||
|
*
|
||||||
|
* @param component The component to disable if needed
|
||||||
|
* @param config The (snowflake) config to check for null
|
||||||
|
* @param result The result of getting the value
|
||||||
|
* @return Whether the component got disabled and a warning logged
|
||||||
|
*/
|
||||||
|
public static boolean disableIfConfigErrorRes(@Nullable Component<DiscordPlugin> component, ConfigData<?> config, Object result) {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
if (result == null || (result instanceof Mono<?> && !((Mono<?>) result).hasElement().block())) {
|
||||||
|
String path = null;
|
||||||
|
try {
|
||||||
|
if (component != null)
|
||||||
|
Component.setComponentEnabled(component, false);
|
||||||
|
path = config.getPath();
|
||||||
|
} catch (Exception e) {
|
||||||
|
TBMCCoreAPI.SendException("Failed to disable component after config error!", e);
|
||||||
|
}
|
||||||
|
getLogger().warning("The config value " + path + " isn't set correctly " + (component == null ? "in global settings!" : "for component " + component.getClass().getSimpleName() + "!"));
|
||||||
|
getLogger().warning("Set the correct ID in the config" + (component == null ? "" : " or disable this component") + " to remove this message.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mono<Message> reply(Message original, @Nullable MessageChannel channel, String message) {
|
||||||
|
Mono<MessageChannel> ch;
|
||||||
|
if (channel == null)
|
||||||
|
ch = original.getChannel();
|
||||||
|
else
|
||||||
|
ch = Mono.just(channel);
|
||||||
|
return ch.flatMap(chan -> chan.createMessage((original.getAuthor().isPresent()
|
||||||
|
? original.getAuthor().get().getMention() + ", " : "") + message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String nickMention(Snowflake userId) {
|
||||||
|
return "<@!" + userId.asString() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String channelMention(Snowflake channelId) {
|
||||||
|
return "<#" + channelId.asString() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mono<MessageChannel> getMessageChannel(String key, Snowflake id) {
|
||||||
|
return DiscordPlugin.dc.getChannelById(id).onErrorResume(e -> {
|
||||||
|
getLogger().warning("Failed to get channel data for " + key + "=" + id + " - " + e.getMessage());
|
||||||
|
return Mono.empty();
|
||||||
|
}).filter(ch -> ch instanceof MessageChannel).cast(MessageChannel.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mono<MessageChannel> getMessageChannel(ConfigData<Snowflake> config) {
|
||||||
|
return getMessageChannel(config.getPath(), config.get());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
package buttondevteam.discordplugin;
|
package buttondevteam.discordplugin;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
||||||
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
|
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
|
||||||
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
import lombok.Setter;
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlayer<DiscordConnectedPlayer> {
|
public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlayer<DiscordConnectedPlayer> {
|
||||||
private static int nextEntityId = 10000;
|
private static int nextEntityId = 10000;
|
||||||
private @Getter VanillaCommandListener<DiscordConnectedPlayer> vanillaCmdListener;
|
private @Getter VanillaCommandListener<DiscordConnectedPlayer> vanillaCmdListener;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean loggedIn = false;
|
||||||
|
|
||||||
public DiscordConnectedPlayer(IUser user, IChannel channel, UUID uuid, String mcname) {
|
public DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) {
|
||||||
super(user, channel, nextEntityId++, uuid, mcname);
|
super(user, channel, nextEntityId++, uuid, mcname, module);
|
||||||
vanillaCmdListener = new VanillaCommandListener<>(this);
|
vanillaCmdListener = new VanillaCommandListener<>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ public class DiscordPlayer extends ChromaGamerBase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if player has the private Minecraft chat enabled. For setting the value, see
|
* Returns true if player has the private Minecraft chat enabled. For setting the value, see
|
||||||
* {@link MCChatPrivate#privateMCChat(sx.blah.discord.handle.obj.IChannel, boolean, sx.blah.discord.handle.obj.IUser, DiscordPlayer)}
|
* {@link MCChatPrivate#privateMCChat(sx.blah.discord.handle.obj.MessageChannel, boolean, sx.blah.discord.handle.obj.User, DiscordPlayer)}
|
||||||
*/
|
*/
|
||||||
public boolean isMinecraftChatEnabled() {
|
public boolean isMinecraftChatEnabled() {
|
||||||
return MCChatPrivate.isMinecraftChatEnabled(this);
|
return MCChatPrivate.isMinecraftChatEnabled(this);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package buttondevteam.discordplugin;
|
package buttondevteam.discordplugin;
|
||||||
|
|
||||||
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.advancement.Advancement;
|
import org.bukkit.advancement.Advancement;
|
||||||
|
@ -26,8 +28,6 @@ import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
import org.bukkit.scoreboard.Scoreboard;
|
import org.bukkit.scoreboard.Scoreboard;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -38,7 +38,7 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<
|
||||||
protected Player player;
|
protected Player player;
|
||||||
private @Getter VanillaCommandListener<DiscordPlayerSender> vanillaCmdListener;
|
private @Getter VanillaCommandListener<DiscordPlayerSender> vanillaCmdListener;
|
||||||
|
|
||||||
public DiscordPlayerSender(IUser user, IChannel channel, Player player) {
|
public DiscordPlayerSender(User user, MessageChannel channel, Player player) {
|
||||||
super(user, channel);
|
super(user, channel);
|
||||||
this.player = player;
|
this.player = player;
|
||||||
vanillaCmdListener = new VanillaCommandListener<DiscordPlayerSender>(this);
|
vanillaCmdListener = new VanillaCommandListener<DiscordPlayerSender>(this);
|
||||||
|
|
|
@ -10,16 +10,27 @@ import buttondevteam.discordplugin.listeners.MCListener;
|
||||||
import buttondevteam.discordplugin.mcchat.MCChatPrivate;
|
import buttondevteam.discordplugin.mcchat.MCChatPrivate;
|
||||||
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
||||||
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
||||||
import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase;
|
import buttondevteam.discordplugin.mccommands.DiscordMCCommand;
|
||||||
import buttondevteam.discordplugin.mccommands.ResetMCCommand;
|
|
||||||
import buttondevteam.discordplugin.role.GameRoleModule;
|
import buttondevteam.discordplugin.role.GameRoleModule;
|
||||||
|
import buttondevteam.discordplugin.util.Timings;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.architecture.ButtonPlugin;
|
import buttondevteam.lib.architecture.ButtonPlugin;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
import buttondevteam.lib.architecture.IHaveConfig;
|
||||||
import buttondevteam.lib.player.ChromaGamerBase;
|
import buttondevteam.lib.player.ChromaGamerBase;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
|
import discord4j.core.DiscordClient;
|
||||||
|
import discord4j.core.DiscordClientBuilder;
|
||||||
|
import discord4j.core.event.domain.guild.GuildCreateEvent;
|
||||||
|
import discord4j.core.event.domain.lifecycle.ReadyEvent;
|
||||||
|
import discord4j.core.object.entity.Guild;
|
||||||
|
import discord4j.core.object.entity.Role;
|
||||||
|
import discord4j.core.object.presence.Activity;
|
||||||
|
import discord4j.core.object.presence.Presence;
|
||||||
|
import discord4j.core.object.reaction.ReactionEmoji;
|
||||||
|
import discord4j.core.object.util.Snowflake;
|
||||||
|
import discord4j.store.jdk.JdkStoreService;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import net.milkbowl.vault.permission.Permission;
|
import net.milkbowl.vault.permission.Permission;
|
||||||
|
@ -27,310 +38,254 @@ import org.bukkit.Bukkit;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.api.ClientBuilder;
|
|
||||||
import sx.blah.discord.api.IDiscordClient;
|
|
||||||
import sx.blah.discord.api.events.IListener;
|
|
||||||
import sx.blah.discord.api.internal.json.objects.EmbedObject;
|
|
||||||
import sx.blah.discord.handle.impl.events.ReadyEvent;
|
|
||||||
import sx.blah.discord.handle.impl.obj.ReactionEmoji;
|
|
||||||
import sx.blah.discord.handle.obj.*;
|
|
||||||
import sx.blah.discord.util.EmbedBuilder;
|
|
||||||
import sx.blah.discord.util.RequestBuffer;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class DiscordPlugin extends ButtonPlugin implements IListener<ReadyEvent> {
|
@ButtonPlugin.ConfigOpts(disableConfigGen = true)
|
||||||
public static IDiscordClient dc;
|
public class DiscordPlugin extends ButtonPlugin {
|
||||||
public static DiscordPlugin plugin;
|
public static DiscordClient dc;
|
||||||
public static boolean SafeMode = true;
|
public static DiscordPlugin plugin;
|
||||||
|
public static boolean SafeMode = true;
|
||||||
@Getter
|
@Getter
|
||||||
private Command2DC manager;
|
private Command2DC manager;
|
||||||
|
|
||||||
public ConfigData<Character> Prefix() {
|
private ConfigData<Character> prefix() {
|
||||||
return getIConfig().getData("prefix", '/', str -> ((String) str).charAt(0), Object::toString);
|
return getIConfig().getData("prefix", '/', str -> ((String) str).charAt(0), Object::toString);
|
||||||
}
|
|
||||||
|
|
||||||
public static char getPrefix() {
|
|
||||||
if (plugin == null) return '/';
|
|
||||||
return plugin.Prefix().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigData<IGuild> MainServer() {
|
|
||||||
return getIConfig().getDataPrimDef("mainServer", 219529124321034241L, id -> dc.getGuildByID((long) id), IIDLinkedObject::getLongID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigData<IChannel> CommandChannel() {
|
public static char getPrefix() {
|
||||||
return DPUtils.channelData(getIConfig(), "commandChannel", 239519012529111040L);
|
if (plugin == null) return '/';
|
||||||
|
return plugin.prefix().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigData<IRole> ModRole() {
|
private ConfigData<Optional<Guild>> mainServer() {
|
||||||
|
return getIConfig().getDataPrimDef("mainServer", 0L,
|
||||||
|
id -> {
|
||||||
|
//It attempts to get the default as well
|
||||||
|
if ((long) id == 0L)
|
||||||
|
return Optional.empty(); //Hack?
|
||||||
|
return dc.getGuildById(Snowflake.of((long) id))
|
||||||
|
.onErrorResume(t -> Mono.fromRunnable(() -> getLogger().warning("Failed to get guild: " + t.getMessage()))).blockOptional();
|
||||||
|
},
|
||||||
|
g -> g.map(gg -> gg.getId().asLong()).orElse(0L));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigData<Snowflake> commandChannel() {
|
||||||
|
return DPUtils.snowflakeData(getIConfig(), "commandChannel", 239519012529111040L);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the role doesn't exist, then it will only allow for the owner.
|
||||||
|
*/
|
||||||
|
public ConfigData<Mono<Role>> modRole() {
|
||||||
return DPUtils.roleData(getIConfig(), "modRole", "Moderator");
|
return DPUtils.roleData(getIConfig(), "modRole", "Moderator");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void pluginEnable() {
|
* The invite link to show by /discord invite. If empty, it defaults to the first invite if the bot has access.
|
||||||
try {
|
*/
|
||||||
getLogger().info("Initializing...");
|
public ConfigData<String> inviteLink() {
|
||||||
plugin = this;
|
return getIConfig().getData("inviteLink", "");
|
||||||
manager = new Command2DC();
|
}
|
||||||
ClientBuilder cb = new ClientBuilder();
|
|
||||||
File tokenFile = new File("TBMC", "Token.txt");
|
|
||||||
if (tokenFile.exists()) //Legacy support
|
|
||||||
//noinspection UnstableApiUsage
|
|
||||||
cb.withToken(Files.readFirstLine(tokenFile, StandardCharsets.UTF_8));
|
|
||||||
else {
|
|
||||||
File privateFile = new File(getDataFolder(), "private.yml");
|
|
||||||
val conf = YamlConfiguration.loadConfiguration(privateFile);
|
|
||||||
String token = conf.getString("token");
|
|
||||||
if (token == null) {
|
|
||||||
conf.set("token", "Token goes here");
|
|
||||||
conf.save(privateFile);
|
|
||||||
|
|
||||||
getLogger().severe("Token not found! Set it in private.yml");
|
@Override
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
public void pluginEnable() {
|
||||||
return;
|
try {
|
||||||
} else
|
getLogger().info("Initializing...");
|
||||||
cb.withToken(token);
|
plugin = this;
|
||||||
}
|
manager = new Command2DC();
|
||||||
dc = cb.login();
|
String token;
|
||||||
dc.getDispatcher().registerListener(this);
|
File tokenFile = new File("TBMC", "Token.txt");
|
||||||
} catch (Exception e) {
|
if (tokenFile.exists()) //Legacy support
|
||||||
e.printStackTrace();
|
//noinspection UnstableApiUsage
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
token = Files.readFirstLine(tokenFile, StandardCharsets.UTF_8);
|
||||||
}
|
else {
|
||||||
}
|
File privateFile = new File(getDataFolder(), "private.yml");
|
||||||
|
val conf = YamlConfiguration.loadConfiguration(privateFile);
|
||||||
|
token = conf.getString("token");
|
||||||
|
if (token == null || token.equalsIgnoreCase("Token goes here")) {
|
||||||
|
conf.set("token", "Token goes here");
|
||||||
|
conf.save(privateFile);
|
||||||
|
|
||||||
public static IGuild mainServer;
|
getLogger().severe("Token not found! Set it in private.yml");
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val cb = new DiscordClientBuilder(token);
|
||||||
|
cb.setInitialPresence(Presence.doNotDisturb(Activity.playing("booting")));
|
||||||
|
cb.setStoreService(new JdkStoreService()); //The default doesn't work for some reason - it's waaay faster now
|
||||||
|
dc = cb.build();
|
||||||
|
dc.getEventDispatcher().on(ReadyEvent.class) // Listen for ReadyEvent(s)
|
||||||
|
.map(event -> event.getGuilds().size()) // Get how many guilds the bot is in
|
||||||
|
.flatMap(size -> dc.getEventDispatcher()
|
||||||
|
.on(GuildCreateEvent.class) // Listen for GuildCreateEvent(s)
|
||||||
|
.take(size) // Take only the first `size` GuildCreateEvent(s) to be received
|
||||||
|
.collectList()) // Take all received GuildCreateEvents and make it a List
|
||||||
|
.subscribe(this::handleReady); /* All guilds have been received, client is fully connected */
|
||||||
|
dc.login().subscribe();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static volatile BukkitTask task;
|
public static Guild mainServer;
|
||||||
private static volatile boolean sent = false;
|
|
||||||
|
|
||||||
@Override
|
private void handleReady(List<GuildCreateEvent> event) {
|
||||||
public void handle(ReadyEvent event) {
|
try {
|
||||||
try {
|
mainServer = mainServer().get().orElse(null); //Shouldn't change afterwards
|
||||||
dc.changePresence(StatusType.DND, ActivityType.PLAYING, "booting");
|
if (mainServer == null) {
|
||||||
val tries = new AtomicInteger();
|
if (event.size() == 0) {
|
||||||
task = Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> {
|
getLogger().severe("Main server not found! Invite the bot and do /discord reset");
|
||||||
tries.incrementAndGet();
|
saveConfig(); //Put default there
|
||||||
if (tries.get() > 10) { //5 seconds
|
return; //We should have all guilds by now, no need to retry
|
||||||
task.cancel();
|
}
|
||||||
getLogger().severe("Main server not found! Invite the bot and do /discord reset");
|
mainServer = event.get(0).getGuild();
|
||||||
//getIConfig().getConfig().set("mainServer", 219529124321034241L); //Needed because it won't save as long as it's null - made it save
|
getLogger().warning("Main server set to first one: " + mainServer.getName());
|
||||||
saveConfig(); //Put default there
|
mainServer().set(Optional.of(mainServer)); //Save in config
|
||||||
return;
|
}
|
||||||
}
|
SafeMode = false;
|
||||||
mainServer = MainServer().get(); //Shouldn't change afterwards
|
DPUtils.disableIfConfigErrorRes(null, commandChannel(), DPUtils.getMessageChannel(commandChannel()));
|
||||||
if (mainServer == null) {
|
DPUtils.disableIfConfigError(null, modRole()); //Won't disable, just prints the warning here
|
||||||
val guilds = dc.getGuilds();
|
|
||||||
if (guilds.size() == 0)
|
|
||||||
return; //If there are no guilds in cache, retry
|
|
||||||
mainServer = guilds.get(0);
|
|
||||||
getLogger().warning("Main server set to first one: " + mainServer.getName());
|
|
||||||
MainServer().set(mainServer); //Save in config
|
|
||||||
}
|
|
||||||
if (!TBMCCoreAPI.IsTestServer()) { //Don't change conditions here, see mainServer=devServer=null in onDisable()
|
|
||||||
dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "Minecraft");
|
|
||||||
} else {
|
|
||||||
dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "testing");
|
|
||||||
}
|
|
||||||
SafeMode = false;
|
|
||||||
if (task != null)
|
|
||||||
task.cancel();
|
|
||||||
if (!sent) {
|
|
||||||
DPUtils.disableIfConfigError(null, CommandChannel(), ModRole()); //Won't disable, just prints the warning here
|
|
||||||
|
|
||||||
Component.registerComponent(this, new GeneralEventBroadcasterModule());
|
Component.registerComponent(this, new GeneralEventBroadcasterModule());
|
||||||
Component.registerComponent(this, new MinecraftChatModule());
|
Component.registerComponent(this, new MinecraftChatModule());
|
||||||
Component.registerComponent(this, new ExceptionListenerModule());
|
Component.registerComponent(this, new ExceptionListenerModule());
|
||||||
Component.registerComponent(this, new GameRoleModule()); //Needs the mainServer to be set
|
Component.registerComponent(this, new GameRoleModule()); //Needs the mainServer to be set
|
||||||
Component.registerComponent(this, new AnnouncerModule());
|
Component.registerComponent(this, new AnnouncerModule());
|
||||||
Component.registerComponent(this, new FunModule());
|
Component.registerComponent(this, new FunModule());
|
||||||
new ChromaBot(this).updatePlayerList(); //Initialize ChromaBot - The MCCHatModule is tested to be enabled
|
new ChromaBot(this).updatePlayerList(); //Initialize ChromaBot - The MCCHatModule is tested to be enabled
|
||||||
|
|
||||||
getManager().registerCommand(new VersionCommand());
|
getManager().registerCommand(new VersionCommand());
|
||||||
getManager().registerCommand(new UserinfoCommand());
|
getManager().registerCommand(new UserinfoCommand());
|
||||||
getManager().registerCommand(new HelpCommand());
|
getManager().registerCommand(new HelpCommand());
|
||||||
getManager().registerCommand(new DebugCommand());
|
getManager().registerCommand(new DebugCommand());
|
||||||
getManager().registerCommand(new ConnectCommand());
|
getManager().registerCommand(new ConnectCommand());
|
||||||
if (ResetMCCommand.resetting) //These will only execute if the chat is enabled
|
if (DiscordMCCommand.resetting) //These will only execute if the chat is enabled
|
||||||
ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.CYAN)
|
ChromaBot.getInstance().sendMessageCustomAsWell(chan -> chan.flatMap(ch -> ch.createEmbed(ecs -> ecs.setColor(Color.CYAN)
|
||||||
.withTitle("Discord plugin restarted - chat connected.").build(), ChannelconBroadcast.RESTART); //Really important to note the chat, hmm
|
.setTitle("Discord plugin restarted - chat connected."))), ChannelconBroadcast.RESTART); //Really important to note the chat, hmm
|
||||||
else if (getConfig().getBoolean("serverup", false)) {
|
else if (getConfig().getBoolean("serverup", false)) {
|
||||||
ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.YELLOW)
|
ChromaBot.getInstance().sendMessageCustomAsWell(chan -> chan.flatMap(ch -> ch.createEmbed(ecs -> ecs.setColor(Color.YELLOW)
|
||||||
.withTitle("Server recovered from a crash - chat connected.").build(), ChannelconBroadcast.RESTART);
|
.setTitle("Server recovered from a crash - chat connected."))), ChannelconBroadcast.RESTART);
|
||||||
val thr = new Throwable(
|
val thr = new Throwable(
|
||||||
"The server shut down unexpectedly. See the log of the previous run for more details.");
|
"The server shut down unexpectedly. See the log of the previous run for more details.");
|
||||||
thr.setStackTrace(new StackTraceElement[0]);
|
thr.setStackTrace(new StackTraceElement[0]);
|
||||||
TBMCCoreAPI.SendException("The server crashed!", thr);
|
TBMCCoreAPI.SendException("The server crashed!", thr);
|
||||||
} else
|
} else
|
||||||
ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.GREEN)
|
ChromaBot.getInstance().sendMessageCustomAsWell(chan -> chan.flatMap(ch -> ch.createEmbed(ecs -> ecs.setColor(Color.GREEN)
|
||||||
.withTitle("Server started - chat connected.").build(), ChannelconBroadcast.RESTART);
|
.setTitle("Server started - chat connected."))), ChannelconBroadcast.RESTART);
|
||||||
|
|
||||||
ResetMCCommand.resetting = false; //This is the last event handling this flag
|
DiscordMCCommand.resetting = false; //This is the last event handling this flag
|
||||||
|
|
||||||
getConfig().set("serverup", true);
|
getConfig().set("serverup", true);
|
||||||
saveConfig();
|
saveConfig();
|
||||||
sent = true;
|
if (TBMCCoreAPI.IsTestServer() && !Objects.requireNonNull(dc.getSelf().block()).getUsername().toLowerCase().contains("test")) {
|
||||||
if (TBMCCoreAPI.IsTestServer() && !dc.getOurUser().getName().toLowerCase().contains("test")) {
|
TBMCCoreAPI.SendException(
|
||||||
TBMCCoreAPI.SendException(
|
"Won't load because we're in testing mode and not using a separate account.",
|
||||||
"Won't load because we're in testing mode and not using a separate account.",
|
new Exception(
|
||||||
new Exception(
|
"The plugin refuses to load until you change the token to a testing account. (The account needs to have \"test\" in its name.)"
|
||||||
"The plugin refuses to load until you change the token to a testing account. (The account needs to have \"test\" in it's name.)"));
|
+ "\nYou can disable test mode in ThorpeCore config."));
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
}
|
}
|
||||||
TBMCCoreAPI.SendUnsentExceptions();
|
TBMCCoreAPI.SendUnsentExceptions();
|
||||||
TBMCCoreAPI.SendUnsentDebugMessages();
|
TBMCCoreAPI.SendUnsentDebugMessages();
|
||||||
}
|
|
||||||
}, 0, 10);
|
CommonListeners.register(dc.getEventDispatcher());
|
||||||
for (IListener<?> listener : CommonListeners.getListeners())
|
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this);
|
||||||
dc.getDispatcher().registerListener(listener);
|
getCommand2MC().registerCommand(new DiscordMCCommand());
|
||||||
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this);
|
TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class);
|
||||||
TBMCChatAPI.AddCommands(this, DiscordMCCommandBase.class);
|
ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase
|
||||||
TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class);
|
? ((DiscordSenderBase) sender).getChromaUser() : null));
|
||||||
ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase
|
setupProviders();
|
||||||
? ((DiscordSenderBase) sender).getChromaUser() : null));
|
|
||||||
setupProviders();
|
IHaveConfig.pregenConfig(this, null);
|
||||||
} catch (Exception e) {
|
if (!TBMCCoreAPI.IsTestServer()) {
|
||||||
TBMCCoreAPI.SendException("An error occured while enabling DiscordPlugin!", e);
|
dc.updatePresence(Presence.online(Activity.playing("Minecraft"))).subscribe();
|
||||||
}
|
} else {
|
||||||
}
|
dc.updatePresence(Presence.online(Activity.playing("testing"))).subscribe();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
TBMCCoreAPI.SendException("An error occurred while enabling DiscordPlugin!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Always true, except when running "stop" from console
|
* Always true, except when running "stop" from console
|
||||||
*/
|
*/
|
||||||
public static boolean Restart;
|
public static boolean Restart;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pluginPreDisable() {
|
public void pluginPreDisable() {
|
||||||
if (ChromaBot.getInstance() == null) return; //Failed to load
|
if (ChromaBot.getInstance() == null) return; //Failed to load
|
||||||
EmbedObject embed;
|
Timings timings = new Timings();
|
||||||
if (ResetMCCommand.resetting)
|
timings.printElapsed("Disable start");
|
||||||
embed = new EmbedBuilder().withColor(Color.ORANGE).withTitle("Discord plugin restarting").build();
|
MCChatUtils.forCustomAndAllMCChat(chan -> chan.flatMap(ch -> ch.createEmbed(ecs -> {
|
||||||
else
|
timings.printElapsed("Sending message to " + ch.getMention());
|
||||||
embed = new EmbedBuilder().withColor(Restart ? Color.ORANGE : Color.RED)
|
if (DiscordMCCommand.resetting)
|
||||||
.withTitle(Restart ? "Server restarting" : "Server stopping")
|
ecs.setColor(Color.ORANGE).setTitle("Discord plugin restarting");
|
||||||
.withDescription(
|
else
|
||||||
Bukkit.getOnlinePlayers().size() > 0
|
ecs.setColor(Restart ? Color.ORANGE : Color.RED)
|
||||||
? (DPUtils
|
.setTitle(Restart ? "Server restarting" : "Server stopping")
|
||||||
.sanitizeString(Bukkit.getOnlinePlayers().stream()
|
.setDescription(
|
||||||
.map(Player::getDisplayName).collect(Collectors.joining(", ")))
|
Bukkit.getOnlinePlayers().size() > 0
|
||||||
+ (Bukkit.getOnlinePlayers().size() == 1 ? " was " : " were ")
|
? (DPUtils
|
||||||
+ "kicked the hell out.") //TODO: Make configurable
|
.sanitizeString(Bukkit.getOnlinePlayers().stream()
|
||||||
: "") //If 'restart' is disabled then this isn't shown even if joinleave is enabled
|
.map(Player::getDisplayName).collect(Collectors.joining(", ")))
|
||||||
.build();
|
+ (Bukkit.getOnlinePlayers().size() == 1 ? " was " : " were ")
|
||||||
MCChatUtils.forCustomAndAllMCChat(ch -> {
|
+ "kicked the hell out.") //TODO: Make configurable
|
||||||
try {
|
: ""); //If 'restart' is disabled then this isn't shown even if joinleave is enabled
|
||||||
DiscordPlugin.sendMessageToChannelWait(ch, "",
|
})).subscribe(), ChannelconBroadcast.RESTART, false);
|
||||||
embed, 5, TimeUnit.SECONDS);
|
timings.printElapsed("Updating player list");
|
||||||
} catch (TimeoutException | InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}, ChannelconBroadcast.RESTART, false);
|
|
||||||
ChromaBot.getInstance().updatePlayerList();
|
ChromaBot.getInstance().updatePlayerList();
|
||||||
|
timings.printElapsed("Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pluginDisable() {
|
public void pluginDisable() {
|
||||||
|
Timings timings = new Timings();
|
||||||
|
timings.printElapsed("Actual disable start (logout)");
|
||||||
MCChatPrivate.logoutAll();
|
MCChatPrivate.logoutAll();
|
||||||
|
timings.printElapsed("Config setup");
|
||||||
getConfig().set("serverup", false);
|
getConfig().set("serverup", false);
|
||||||
if (ChromaBot.getInstance() == null) return; //Failed to load
|
if (ChromaBot.getInstance() == null) return; //Failed to load
|
||||||
|
|
||||||
saveConfig();
|
saveConfig();
|
||||||
try {
|
try {
|
||||||
SafeMode = true; // Stop interacting with Discord
|
SafeMode = true; // Stop interacting with Discord
|
||||||
ChromaBot.delete();
|
ChromaBot.delete();
|
||||||
dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing
|
timings.printElapsed("Updating presence...");
|
||||||
dc.logout();
|
dc.updatePresence(Presence.idle(Activity.playing("Chromacraft"))).block(); //No longer using the same account for testing
|
||||||
//Configs are emptied so channels and servers are fetched again
|
timings.printElapsed("Logging out...");
|
||||||
sent = false;
|
dc.logout().block();
|
||||||
} catch (Exception e) {
|
//Configs are emptied so channels and servers are fetched again
|
||||||
TBMCCoreAPI.SendException("An error occured while disabling DiscordPlugin!", e);
|
} catch (Exception e) {
|
||||||
}
|
TBMCCoreAPI.SendException("An error occured while disabling DiscordPlugin!", e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static final ReactionEmoji DELIVERED_REACTION = ReactionEmoji.of("✅");
|
public static final ReactionEmoji DELIVERED_REACTION = ReactionEmoji.unicode("✅");
|
||||||
|
|
||||||
public static void sendMessageToChannel(IChannel channel, String message) {
|
public static Permission perms;
|
||||||
sendMessageToChannel(channel, message, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sendMessageToChannel(IChannel channel, String message, EmbedObject embed) {
|
private boolean setupProviders() {
|
||||||
try {
|
try {
|
||||||
sendMessageToChannel(channel, message, embed, false);
|
Class.forName("net.milkbowl.vault.permission.Permission");
|
||||||
} catch (TimeoutException | InterruptedException e) {
|
Class.forName("net.milkbowl.vault.chat.Chat");
|
||||||
e.printStackTrace(); //Shouldn't happen, as we're not waiting on the result
|
} catch (ClassNotFoundException e) {
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMessage sendMessageToChannelWait(IChannel channel, String message) throws TimeoutException, InterruptedException {
|
RegisteredServiceProvider<Permission> permsProvider = Bukkit.getServer().getServicesManager()
|
||||||
return sendMessageToChannelWait(channel, message, null);
|
.getRegistration(Permission.class);
|
||||||
}
|
perms = permsProvider.getProvider();
|
||||||
|
return perms != null;
|
||||||
public static IMessage sendMessageToChannelWait(IChannel channel, String message, EmbedObject embed) throws TimeoutException, InterruptedException {
|
}
|
||||||
return sendMessageToChannel(channel, message, embed, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMessage sendMessageToChannelWait(IChannel channel, String message, EmbedObject embed, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException {
|
|
||||||
return sendMessageToChannel(channel, message, embed, true, timeout, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IMessage sendMessageToChannel(IChannel channel, String message, EmbedObject embed, boolean wait) throws TimeoutException, InterruptedException {
|
|
||||||
return sendMessageToChannel(channel, message, embed, wait, -1, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IMessage sendMessageToChannel(IChannel channel, String message, EmbedObject embed, boolean wait, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException {
|
|
||||||
if (message.length() > 1980) {
|
|
||||||
message = message.substring(0, 1980);
|
|
||||||
DPUtils.getLogger()
|
|
||||||
.warning("Message was too long to send to discord and got truncated. In " + channel.getName());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
MCChatUtils.resetLastMessage(channel); // If this is a chat message, it'll be set again
|
|
||||||
final String content = message;
|
|
||||||
RequestBuffer.IRequest<IMessage> r = () -> embed == null ? channel.sendMessage(content)
|
|
||||||
: channel.sendMessage(content, embed, false);
|
|
||||||
if (wait) {
|
|
||||||
if (unit != null)
|
|
||||||
return DPUtils.perform(r, timeout, unit);
|
|
||||||
else
|
|
||||||
return DPUtils.perform(r);
|
|
||||||
} else {
|
|
||||||
if (unit != null)
|
|
||||||
plugin.getLogger().warning("Tried to set timeout for non-waiting call.");
|
|
||||||
else
|
|
||||||
DPUtils.performNoWait(r);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (TimeoutException | InterruptedException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
DPUtils.getLogger().warning(
|
|
||||||
"Failed to deliver message to Discord! Channel: " + channel.getName() + " Message: " + message);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Permission perms;
|
|
||||||
|
|
||||||
public boolean setupProviders() {
|
|
||||||
try {
|
|
||||||
Class.forName("net.milkbowl.vault.permission.Permission");
|
|
||||||
Class.forName("net.milkbowl.vault.chat.Chat");
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisteredServiceProvider<Permission> permsProvider = Bukkit.getServer().getServicesManager()
|
|
||||||
.getRegistration(Permission.class);
|
|
||||||
perms = permsProvider.getProvider();
|
|
||||||
return perms != null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
package buttondevteam.discordplugin;
|
|
||||||
|
|
||||||
import sx.blah.discord.util.DiscordException;
|
|
||||||
import sx.blah.discord.util.MissingPermissionsException;
|
|
||||||
import sx.blah.discord.util.RateLimitException;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface DiscordRunnable {
|
|
||||||
public abstract void run() throws DiscordException, RateLimitException, MissingPermissionsException;
|
|
||||||
}
|
|
|
@ -1,5 +1,9 @@
|
||||||
package buttondevteam.discordplugin;
|
package buttondevteam.discordplugin;
|
||||||
|
|
||||||
|
import discord4j.core.object.entity.Member;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
@ -8,8 +12,6 @@ import org.bukkit.permissions.Permission;
|
||||||
import org.bukkit.permissions.PermissionAttachment;
|
import org.bukkit.permissions.PermissionAttachment;
|
||||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -18,12 +20,13 @@ public class DiscordSender extends DiscordSenderBase implements CommandSender {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public DiscordSender(IUser user, IChannel channel) {
|
public DiscordSender(User user, MessageChannel channel) {
|
||||||
super(user, channel);
|
super(user, channel);
|
||||||
name = user == null ? "Discord user" : user.getDisplayName(DiscordPlugin.mainServer);
|
val def = "Discord user";
|
||||||
|
name = user == null ? def : user.asMember(DiscordPlugin.mainServer.getId()).blockOptional().map(Member::getDisplayName).orElse(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscordSender(IUser user, IChannel channel, String name) {
|
public DiscordSender(User user, MessageChannel channel, String name) {
|
||||||
super(user, channel);
|
super(user, channel);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
package buttondevteam.discordplugin;
|
package buttondevteam.discordplugin;
|
||||||
|
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
public abstract class DiscordSenderBase implements CommandSender {
|
public abstract class DiscordSenderBase implements CommandSender {
|
||||||
/**
|
/**
|
||||||
* May be null.
|
* May be null.
|
||||||
*/
|
*/
|
||||||
protected IUser user;
|
protected User user;
|
||||||
protected IChannel channel;
|
protected MessageChannel channel;
|
||||||
|
|
||||||
protected DiscordSenderBase(IUser user, IChannel channel) {
|
protected DiscordSenderBase(User user, MessageChannel channel) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,11 @@ public abstract class DiscordSenderBase implements CommandSender {
|
||||||
*
|
*
|
||||||
* @return The user or null.
|
* @return The user or null.
|
||||||
*/
|
*/
|
||||||
public IUser getUser() {
|
public User getUser() {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IChannel getChannel() {
|
public MessageChannel getChannel() {
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ public abstract class DiscordSenderBase implements CommandSender {
|
||||||
* @return A Chroma user of Discord or a Discord user of Chroma
|
* @return A Chroma user of Discord or a Discord user of Chroma
|
||||||
*/
|
*/
|
||||||
public DiscordPlayer getChromaUser() {
|
public DiscordPlayer getChromaUser() {
|
||||||
if (chromaUser == null) chromaUser = DiscordPlayer.getUser(user.getStringID(), DiscordPlayer.class);
|
if (chromaUser == null) chromaUser = DiscordPlayer.getUser(user.getId().asString(), DiscordPlayer.class);
|
||||||
return chromaUser;
|
return chromaUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +58,7 @@ public abstract class DiscordSenderBase implements CommandSender {
|
||||||
msgtosend += "\n" + sendmsg;
|
msgtosend += "\n" + sendmsg;
|
||||||
if (sendtask == null)
|
if (sendtask == null)
|
||||||
sendtask = Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
|
sendtask = Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
|
||||||
DiscordPlugin.sendMessageToChannel(channel,
|
channel.createMessage((!broadcast && user != null ? user.getMention() + "\n" : "") + msgtosend.trim()).subscribe();
|
||||||
(!broadcast && user != null ? user.mention() + "\n" : "") + msgtosend.trim());
|
|
||||||
sendtask = null;
|
sendtask = null;
|
||||||
msgtosend = "";
|
msgtosend = "";
|
||||||
}, 4); // Waits a 0.2 second to gather all/most of the different messages
|
}, 4); // Waits a 0.2 second to gather all/most of the different messages
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package buttondevteam.discordplugin;
|
|
||||||
|
|
||||||
import sx.blah.discord.handle.obj.IDiscordObject;
|
|
||||||
import sx.blah.discord.util.DiscordException;
|
|
||||||
import sx.blah.discord.util.MissingPermissionsException;
|
|
||||||
import sx.blah.discord.util.RateLimitException;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface DiscordSupplier<T extends IDiscordObject<T>> {
|
|
||||||
public abstract T get() throws DiscordException, RateLimitException, MissingPermissionsException;
|
|
||||||
}
|
|
|
@ -6,40 +6,48 @@ import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
|
import buttondevteam.lib.architecture.ReadOnlyConfigData;
|
||||||
import buttondevteam.lib.player.ChromaGamerBase;
|
import buttondevteam.lib.player.ChromaGamerBase;
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
import discord4j.core.object.entity.Message;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
import reactor.core.publisher.Flux;
|
||||||
import sx.blah.discord.handle.obj.IMessage;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AnnouncerModule extends Component<DiscordPlugin> {
|
public class AnnouncerModule extends Component<DiscordPlugin> {
|
||||||
public ConfigData<IChannel> channel() {
|
/**
|
||||||
|
* Channel to post new posts.
|
||||||
|
*/
|
||||||
|
public ReadOnlyConfigData<Mono<MessageChannel>> channel() {
|
||||||
return DPUtils.channelData(getConfig(), "channel", 239519012529111040L);
|
return DPUtils.channelData(getConfig(), "channel", 239519012529111040L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigData<IChannel> modChannel() {
|
/**
|
||||||
|
* Channel where distinguished (moderator) posts go.
|
||||||
|
*/
|
||||||
|
public ReadOnlyConfigData<Mono<MessageChannel>> modChannel() {
|
||||||
return DPUtils.channelData(getConfig(), "modChannel", 239519012529111040L);
|
return DPUtils.channelData(getConfig(), "modChannel", 239519012529111040L);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to 0 or >50 to disable
|
* Automatically unpins all messages except the last few. Set to 0 or >50 to disable
|
||||||
*/
|
*/
|
||||||
public ConfigData<Short> keepPinned() {
|
public ConfigData<Short> keepPinned() {
|
||||||
return getConfig().getData("keepPinned", (short) 40);
|
return getConfig().getData("keepPinned", (short) 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigData<Long> lastannouncementtime() {
|
private ConfigData<Long> lastAnnouncementTime() {
|
||||||
return getConfig().getData("lastAnnouncementTime", 0L);
|
return getConfig().getData("lastAnnouncementTime", 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigData<Long> lastseentime() {
|
private ConfigData<Long> lastSeenTime() {
|
||||||
return getConfig().getData("lastSeenTime", 0L);
|
return getConfig().getData("lastSeenTime", 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,24 +58,15 @@ public class AnnouncerModule extends Component<DiscordPlugin> {
|
||||||
protected void enable() {
|
protected void enable() {
|
||||||
if (DPUtils.disableIfConfigError(this, channel(), modChannel())) return;
|
if (DPUtils.disableIfConfigError(this, channel(), modChannel())) return;
|
||||||
stop = false; //If not the first time
|
stop = false; //If not the first time
|
||||||
DPUtils.performNoWait(() -> {
|
val keepPinned = keepPinned().get();
|
||||||
try {
|
if (keepPinned == 0) return;
|
||||||
val keepPinned = keepPinned().get();
|
Flux<Message> msgs = channel().get().flatMapMany(MessageChannel::getPinnedMessages);
|
||||||
if (keepPinned == 0) return;
|
msgs.subscribe(Message::unpin);
|
||||||
val channel = channel().get();
|
|
||||||
List<IMessage> msgs = channel.getPinnedMessages();
|
|
||||||
for (int i = msgs.size() - 1; i >= keepPinned; i--) { // Unpin all pinned messages except the newest 10
|
|
||||||
channel.unpin(msgs.get(i));
|
|
||||||
Thread.sleep(10);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException ignore) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
val yc = YamlConfiguration.loadConfiguration(new File("plugins/DiscordPlugin", "config.yml")); //Name change
|
val yc = YamlConfiguration.loadConfiguration(new File("plugins/DiscordPlugin", "config.yml")); //Name change
|
||||||
if (lastannouncementtime().get() == 0) //Load old data
|
if (lastAnnouncementTime().get() == 0) //Load old data
|
||||||
lastannouncementtime().set(yc.getLong("lastannouncementtime"));
|
lastAnnouncementTime().set(yc.getLong("lastannouncementtime"));
|
||||||
if (lastseentime().get() == 0)
|
if (lastSeenTime().get() == 0)
|
||||||
lastseentime().set(yc.getLong("lastseentime"));
|
lastSeenTime().set(yc.getLong("lastseentime"));
|
||||||
new Thread(this::AnnouncementGetterThreadMethod).start();
|
new Thread(this::AnnouncementGetterThreadMethod).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ public class AnnouncerModule extends Component<DiscordPlugin> {
|
||||||
.get("children").getAsJsonArray();
|
.get("children").getAsJsonArray();
|
||||||
StringBuilder msgsb = new StringBuilder();
|
StringBuilder msgsb = new StringBuilder();
|
||||||
StringBuilder modmsgsb = new StringBuilder();
|
StringBuilder modmsgsb = new StringBuilder();
|
||||||
long lastanntime = lastannouncementtime().get();
|
long lastanntime = lastAnnouncementTime().get();
|
||||||
for (int i = json.size() - 1; i >= 0; i--) {
|
for (int i = json.size() - 1; i >= 0; i--) {
|
||||||
JsonObject item = json.get(i).getAsJsonObject();
|
JsonObject item = json.get(i).getAsJsonObject();
|
||||||
final JsonObject data = item.get("data").getAsJsonObject();
|
final JsonObject data = item.get("data").getAsJsonObject();
|
||||||
|
@ -101,9 +100,9 @@ public class AnnouncerModule extends Component<DiscordPlugin> {
|
||||||
distinguished = distinguishedjson.getAsString();
|
distinguished = distinguishedjson.getAsString();
|
||||||
String permalink = "https://www.reddit.com" + data.get("permalink").getAsString();
|
String permalink = "https://www.reddit.com" + data.get("permalink").getAsString();
|
||||||
long date = data.get("created_utc").getAsLong();
|
long date = data.get("created_utc").getAsLong();
|
||||||
if (date > lastseentime().get())
|
if (date > lastSeenTime().get())
|
||||||
lastseentime().set(date);
|
lastSeenTime().set(date);
|
||||||
else if (date > lastannouncementtime().get()) {
|
else if (date > lastAnnouncementTime().get()) {
|
||||||
do {
|
do {
|
||||||
val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit");
|
val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit");
|
||||||
if (reddituserclass == null)
|
if (reddituserclass == null)
|
||||||
|
@ -122,13 +121,13 @@ public class AnnouncerModule extends Component<DiscordPlugin> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (msgsb.length() > 0)
|
if (msgsb.length() > 0)
|
||||||
channel().get().pin(DiscordPlugin.sendMessageToChannelWait(channel().get(), msgsb.toString()));
|
channel().get().flatMap(ch -> ch.createMessage(msgsb.toString()))
|
||||||
|
.flatMap(Message::pin).subscribe();
|
||||||
if (modmsgsb.length() > 0)
|
if (modmsgsb.length() > 0)
|
||||||
DiscordPlugin.sendMessageToChannel(modChannel().get(), modmsgsb.toString());
|
modChannel().get().flatMap(ch -> ch.createMessage(modmsgsb.toString()))
|
||||||
if (lastannouncementtime().get() != lastanntime) {
|
.flatMap(Message::pin).subscribe();
|
||||||
lastannouncementtime().set(lastanntime); // If sending succeeded
|
if (lastAnnouncementTime().get() != lastanntime)
|
||||||
getPlugin().saveConfig(); //TODO: Won't be needed if I implement auto-saving
|
lastAnnouncementTime().set(lastanntime); // If sending succeeded
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package buttondevteam.discordplugin.commands;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class Command2DC extends Command2<ICommand2DC, Command2DCSender> {
|
public class Command2DC extends Command2<ICommand2DC, Command2DCSender> {
|
||||||
@Override
|
@Override
|
||||||
public void registerCommand(ICommand2DC command) {
|
public void registerCommand(ICommand2DC command) {
|
||||||
|
@ -10,8 +12,8 @@ public class Command2DC extends Command2<ICommand2DC, Command2DCSender> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(Command2DCSender sender, ICommand2DC command) {
|
public boolean hasPermission(Command2DCSender sender, ICommand2DC command, Method method) {
|
||||||
//return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.ModRole().get()); //TODO: ModRole may be null; more customisable way?
|
//return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.modRole().get()); //TODO: modRole may be null; more customisable way?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,28 @@ package buttondevteam.discordplugin.commands;
|
||||||
|
|
||||||
import buttondevteam.discordplugin.DPUtils;
|
import buttondevteam.discordplugin.DPUtils;
|
||||||
import buttondevteam.lib.chat.Command2Sender;
|
import buttondevteam.lib.chat.Command2Sender;
|
||||||
|
import discord4j.core.object.entity.Message;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import sx.blah.discord.handle.obj.IMessage;
|
import lombok.val;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class Command2DCSender implements Command2Sender {
|
public class Command2DCSender implements Command2Sender {
|
||||||
private final @Getter IMessage message;
|
private final @Getter
|
||||||
|
Message message;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(String message) {
|
public void sendMessage(String message) {
|
||||||
if (message.length() == 0) return;
|
if (message.length() == 0) return;
|
||||||
message = DPUtils.sanitizeString(message);
|
message = DPUtils.sanitizeString(message);
|
||||||
message = Character.toLowerCase(message.charAt(0)) + message.substring(1);
|
message = Character.toLowerCase(message.charAt(0)) + message.substring(1);
|
||||||
this.message.reply(message);
|
val msg = message;
|
||||||
|
/*this.message.getAuthorAsMember().flatMap(author ->
|
||||||
|
this.message.getChannel().flatMap(ch ->
|
||||||
|
ch.createMessage(author.getNicknameMention() + ", " + msg))).subscribe();*/
|
||||||
|
this.message.getChannel().flatMap(ch ->
|
||||||
|
ch.createMessage(this.message.getAuthor().map(u -> DPUtils.nickMention(u.getId()) + ", ").orElse("")
|
||||||
|
+ msg)).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package buttondevteam.discordplugin.commands;
|
package buttondevteam.discordplugin.commands;
|
||||||
|
|
||||||
import buttondevteam.discordplugin.DiscordPlayer;
|
import buttondevteam.discordplugin.DiscordPlayer;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
|
@ -28,34 +27,37 @@ public class ConnectCommand extends ICommand2DC {
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public boolean def(Command2DCSender sender, String Minecraftname) {
|
public boolean def(Command2DCSender sender, String Minecraftname) {
|
||||||
val message = sender.getMessage();
|
val message = sender.getMessage();
|
||||||
if (WaitingToConnect.inverse().containsKey(message.getAuthor().getStringID())) {
|
val channel = message.getChannel().block();
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(),
|
val author = message.getAuthor().orElse(null);
|
||||||
"Replacing " + WaitingToConnect.inverse().get(message.getAuthor().getStringID()) + " with " + Minecraftname);
|
if (author == null || channel == null) return true;
|
||||||
WaitingToConnect.inverse().remove(message.getAuthor().getStringID());
|
if (WaitingToConnect.inverse().containsKey(author.getId().asString())) {
|
||||||
|
channel.createMessage(
|
||||||
|
"Replacing " + WaitingToConnect.inverse().get(author.getId().asString()) + " with " + Minecraftname).subscribe();
|
||||||
|
WaitingToConnect.inverse().remove(author.getId().asString());
|
||||||
}
|
}
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
OfflinePlayer p = Bukkit.getOfflinePlayer(Minecraftname);
|
OfflinePlayer p = Bukkit.getOfflinePlayer(Minecraftname);
|
||||||
if (p == null) {
|
if (p == null) {
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(), "The specified Minecraft player cannot be found");
|
channel.createMessage("The specified Minecraft player cannot be found").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
try (TBMCPlayer pl = TBMCPlayerBase.getPlayer(p.getUniqueId(), TBMCPlayer.class)) {
|
try (TBMCPlayer pl = TBMCPlayerBase.getPlayer(p.getUniqueId(), TBMCPlayer.class)) {
|
||||||
DiscordPlayer dp = pl.getAs(DiscordPlayer.class);
|
DiscordPlayer dp = pl.getAs(DiscordPlayer.class);
|
||||||
if (dp != null && message.getAuthor().getStringID().equals(dp.getDiscordID())) {
|
if (dp != null && author.getId().asString().equals(dp.getDiscordID())) {
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(), "You already have this account connected.");
|
channel.createMessage("You already have this account connected.").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("An error occured while connecting a Discord account!", e);
|
TBMCCoreAPI.SendException("An error occured while connecting a Discord account!", e);
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(), "An internal error occured!\n" + e);
|
channel.createMessage("An internal error occured!\n" + e).subscribe();
|
||||||
}
|
}
|
||||||
WaitingToConnect.put(p.getName(), message.getAuthor().getStringID());
|
WaitingToConnect.put(p.getName(), author.getId().asString());
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(),
|
channel.createMessage(
|
||||||
"Alright! Now accept the connection in Minecraft from the account " + Minecraftname
|
"Alright! Now accept the connection in Minecraft from the account " + Minecraftname
|
||||||
+ " before the next server restart. You can also adjust the Minecraft name you want to connect to with the same command.");
|
+ " before the next server restart. You can also adjust the Minecraft name you want to connect to with the same command.").subscribe();
|
||||||
if (p.isOnline())
|
if (p.isOnline())
|
||||||
((Player) p).sendMessage("§bTo connect with the Discord account " + message.getAuthor().getName() + "#"
|
((Player) p).sendMessage("§bTo connect with the Discord account " + author.getUsername() + "#"
|
||||||
+ message.getAuthor().getDiscriminator() + " do /discord accept");
|
+ author.getDiscriminator() + " do /discord accept");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,17 +4,27 @@ import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.discordplugin.listeners.CommonListeners;
|
import buttondevteam.discordplugin.listeners.CommonListeners;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
@CommandClass(helpText = {
|
@CommandClass(helpText = {
|
||||||
"Switches debug mode."
|
"Switches debug mode."
|
||||||
})
|
})
|
||||||
public class DebugCommand extends ICommand2DC {
|
public class DebugCommand extends ICommand2DC {
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public boolean def(Command2DCSender sender, String args) {
|
public boolean def(Command2DCSender sender) {
|
||||||
if (sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.ModRole().get()))
|
sender.getMessage().getAuthorAsMember()
|
||||||
sender.sendMessage("debug " + (CommonListeners.debug() ? "enabled" : "disabled"));
|
.switchIfEmpty(sender.getMessage().getAuthor() //Support DMs
|
||||||
else
|
.map(u -> u.asMember(DiscordPlugin.mainServer.getId()))
|
||||||
sender.sendMessage("you need to be a moderator to use this command.");
|
.orElse(Mono.empty()))
|
||||||
return true;
|
.flatMap(m -> DiscordPlugin.plugin.modRole().get()
|
||||||
}
|
.map(mr -> m.getRoleIds().stream().anyMatch(r -> r.equals(mr.getId())))
|
||||||
|
.switchIfEmpty(Mono.fromSupplier(() -> DiscordPlugin.mainServer.getOwnerId().asLong() == m.getId().asLong()))) //Role not found
|
||||||
|
.subscribe(success -> {
|
||||||
|
if (success)
|
||||||
|
sender.sendMessage("debug " + (CommonListeners.debug() ? "enabled" : "disabled"));
|
||||||
|
else
|
||||||
|
sender.sendMessage("you need to be a moderator to use this command.");
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package buttondevteam.discordplugin.commands;
|
package buttondevteam.discordplugin.commands;
|
||||||
|
|
||||||
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
|
|
||||||
@CommandClass(helpText = {
|
@CommandClass(helpText = {
|
||||||
|
@ -7,12 +8,17 @@ import buttondevteam.lib.chat.CommandClass;
|
||||||
"Shows some info about a command or lists the available commands.", //
|
"Shows some info about a command or lists the available commands.", //
|
||||||
})
|
})
|
||||||
public class HelpCommand extends ICommand2DC {
|
public class HelpCommand extends ICommand2DC {
|
||||||
@Override
|
@Command2.Subcommand
|
||||||
public boolean def(Command2DCSender sender, String args) {
|
public boolean def(Command2DCSender sender, @Command2.TextArg @Command2.OptionalArg String args) {
|
||||||
if (args.length() == 0)
|
if (args == null || args.length() == 0)
|
||||||
sender.sendMessage(getManager().getCommandsText());
|
sender.sendMessage(getManager().getCommandsText());
|
||||||
else
|
else {
|
||||||
sender.sendMessage("Soon:tm:"); //TODO
|
String[] ht = getManager().getHelpText(args);
|
||||||
return true;
|
if (ht == null)
|
||||||
|
sender.sendMessage("Command not found: " + args);
|
||||||
|
else
|
||||||
|
sender.sendMessage(ht);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,11 @@ import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
import buttondevteam.lib.player.ChromaGamerBase;
|
import buttondevteam.lib.player.ChromaGamerBase;
|
||||||
import buttondevteam.lib.player.ChromaGamerBase.InfoTarget;
|
import buttondevteam.lib.player.ChromaGamerBase.InfoTarget;
|
||||||
|
import discord4j.core.object.entity.Message;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import sx.blah.discord.handle.obj.IMessage;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@CommandClass(helpText = {
|
@CommandClass(helpText = {
|
||||||
"User information", //
|
"User information", //
|
||||||
|
@ -24,67 +22,71 @@ public class UserinfoCommand extends ICommand2DC {
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public boolean def(Command2DCSender sender, @Command2.OptionalArg @Command2.TextArg String user) {
|
public boolean def(Command2DCSender sender, @Command2.OptionalArg @Command2.TextArg String user) {
|
||||||
val message = sender.getMessage();
|
val message = sender.getMessage();
|
||||||
IUser target = null;
|
User target = null;
|
||||||
|
val channel = message.getChannel().block();
|
||||||
|
assert channel != null;
|
||||||
if (user == null || user.length() == 0)
|
if (user == null || user.length() == 0)
|
||||||
target = message.getAuthor();
|
target = message.getAuthor().orElse(null);
|
||||||
else {
|
else {
|
||||||
final Optional<IUser> firstmention = message.getMentions().stream()
|
@SuppressWarnings("OptionalGetWithoutIsPresent") final User firstmention = message.getUserMentions()
|
||||||
.filter(m -> !m.getStringID().equals(DiscordPlugin.dc.getOurUser().getStringID())).findFirst();
|
.filter(m -> !m.getId().asString().equals(DiscordPlugin.dc.getSelfId().get().asString())).blockFirst();
|
||||||
if (firstmention.isPresent())
|
if (firstmention != null)
|
||||||
target = firstmention.get();
|
target = firstmention;
|
||||||
else if (user.contains("#")) {
|
else if (user.contains("#")) {
|
||||||
String[] targettag = user.split("#");
|
String[] targettag = user.split("#");
|
||||||
final List<IUser> targets = getUsers(message, targettag[0]);
|
final List<User> targets = getUsers(message, targettag[0]);
|
||||||
if (targets.size() == 0) {
|
if (targets.size() == 0) {
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(),
|
channel.createMessage("The user cannot be found (by name): " + user).subscribe();
|
||||||
"The user cannot be found (by name): " + user);
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
for (IUser ptarget : targets) {
|
for (User ptarget : targets) {
|
||||||
if (ptarget.getDiscriminator().equalsIgnoreCase(targettag[1])) {
|
if (ptarget.getDiscriminator().equalsIgnoreCase(targettag[1])) {
|
||||||
target = ptarget;
|
target = ptarget;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(),
|
channel.createMessage("The user cannot be found (by discriminator): " + user + "(Found " + targets.size()
|
||||||
"The user cannot be found (by discriminator): " + user + "(Found " + targets.size()
|
+ " users with the name.)").subscribe();
|
||||||
+ " users with the name.)");
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final List<IUser> targets = getUsers(message, user);
|
final List<User> targets = getUsers(message, user);
|
||||||
if (targets.size() == 0) {
|
if (targets.size() == 0) {
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(),
|
channel.createMessage("The user cannot be found on Discord: " + user).subscribe();
|
||||||
"The user cannot be found on Discord: " + user);
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (targets.size() > 1) {
|
if (targets.size() > 1) {
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(),
|
channel.createMessage("Multiple users found with that (nick)name. Please specify the whole tag, like ChromaBot#6338 or use a ping.").subscribe();
|
||||||
"Multiple users found with that (nick)name. Please specify the whole tag, like ChromaBot#6338 or use a ping.");
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
target = targets.get(0);
|
target = targets.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try (DiscordPlayer dp = ChromaGamerBase.getUser(target.getStringID(), DiscordPlayer.class)) {
|
if (target == null) {
|
||||||
StringBuilder uinfo = new StringBuilder("User info for ").append(target.getName()).append(":\n");
|
sender.sendMessage("An error occurred.");
|
||||||
uinfo.append(dp.getInfo(InfoTarget.Discord));
|
return true;
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(), uinfo.toString());
|
|
||||||
} catch (Exception e) {
|
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(), "An error occured while getting the user!");
|
|
||||||
TBMCCoreAPI.SendException("Error while getting info about " + target.getName() + "!", e);
|
|
||||||
}
|
}
|
||||||
return true;
|
try (DiscordPlayer dp = ChromaGamerBase.getUser(target.getId().asString(), DiscordPlayer.class)) {
|
||||||
|
StringBuilder uinfo = new StringBuilder("User info for ").append(target.getUsername()).append(":\n");
|
||||||
|
uinfo.append(dp.getInfo(InfoTarget.Discord));
|
||||||
|
channel.createMessage(uinfo.toString()).subscribe();
|
||||||
|
} catch (Exception e) {
|
||||||
|
channel.createMessage("An error occured while getting the user!").subscribe();
|
||||||
|
TBMCCoreAPI.SendException("Error while getting info about " + target.getUsername() + "!", e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<IUser> getUsers(IMessage message, String args) {
|
private List<User> getUsers(Message message, String args) {
|
||||||
final List<IUser> targets;
|
final List<User> targets;
|
||||||
if (message.getChannel().isPrivate())
|
val guild = message.getGuild().block();
|
||||||
targets = DiscordPlugin.dc.getUsers().stream().filter(u -> u.getName().equalsIgnoreCase(args))
|
if (guild == null) //Private channel
|
||||||
.collect(Collectors.toList());
|
targets = DiscordPlugin.dc.getUsers().filter(u -> u.getUsername().equalsIgnoreCase(args))
|
||||||
|
.collectList().block();
|
||||||
else
|
else
|
||||||
targets = message.getGuild().getUsersByName(args, true);
|
targets = guild.getMembers().filter(m -> m.getUsername().equalsIgnoreCase(args))
|
||||||
|
.map(m -> (User) m).collectList().block();
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,12 @@ package buttondevteam.discordplugin.exceptions;
|
||||||
import buttondevteam.core.ComponentManager;
|
import buttondevteam.core.ComponentManager;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.lib.TBMCDebugMessageEvent;
|
import buttondevteam.lib.TBMCDebugMessageEvent;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
public class DebugMessageListener implements Listener{
|
public class DebugMessageListener implements Listener {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onDebugMessage(TBMCDebugMessageEvent e) {
|
public void onDebugMessage(TBMCDebugMessageEvent e) {
|
||||||
SendMessage(e.getDebugMessage());
|
SendMessage(e.getDebugMessage());
|
||||||
|
@ -17,13 +19,15 @@ public class DebugMessageListener implements Listener{
|
||||||
if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(ExceptionListenerModule.class))
|
if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(ExceptionListenerModule.class))
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
|
Mono<MessageChannel> mc = ExceptionListenerModule.getChannel();
|
||||||
|
if (mc == null) return;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("```").append("\n");
|
sb.append("```").append("\n");
|
||||||
if (message.length() > 2000)
|
if (message.length() > 2000)
|
||||||
message = message.substring(0, 2000);
|
message = message.substring(0, 2000);
|
||||||
sb.append(message).append("\n");
|
sb.append(message).append("\n");
|
||||||
sb.append("```");
|
sb.append("```");
|
||||||
DiscordPlugin.sendMessageToChannel(ExceptionListenerModule.getChannel(), sb.toString());
|
mc.flatMap(ch -> ch.createMessage(sb.toString())).subscribe();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,16 @@ import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.TBMCExceptionEvent;
|
import buttondevteam.lib.TBMCExceptionEvent;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
|
import buttondevteam.lib.architecture.ReadOnlyConfigData;
|
||||||
|
import discord4j.core.object.entity.Guild;
|
||||||
|
import discord4j.core.object.entity.GuildChannel;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.Role;
|
||||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.obj.IGuild;
|
|
||||||
import sx.blah.discord.handle.obj.IRole;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -21,64 +24,71 @@ import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ExceptionListenerModule extends Component<DiscordPlugin> implements Listener {
|
public class ExceptionListenerModule extends Component<DiscordPlugin> implements Listener {
|
||||||
private List<Throwable> lastthrown = new ArrayList<>();
|
private List<Throwable> lastthrown = new ArrayList<>();
|
||||||
private List<String> lastsourcemsg = new ArrayList<>();
|
private List<String> lastsourcemsg = new ArrayList<>();
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onException(TBMCExceptionEvent e) {
|
public void onException(TBMCExceptionEvent e) {
|
||||||
if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass()))
|
if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass()))
|
||||||
return;
|
return;
|
||||||
if (lastthrown.stream()
|
if (lastthrown.stream()
|
||||||
.anyMatch(ex -> Arrays.equals(e.getException().getStackTrace(), ex.getStackTrace())
|
.anyMatch(ex -> Arrays.equals(e.getException().getStackTrace(), ex.getStackTrace())
|
||||||
&& (e.getException().getMessage() == null ? ex.getMessage() == null
|
&& (e.getException().getMessage() == null ? ex.getMessage() == null
|
||||||
: e.getException().getMessage().equals(ex.getMessage()))) // e.Exception.Message==ex.Message
|
: e.getException().getMessage().equals(ex.getMessage()))) // e.Exception.Message==ex.Message
|
||||||
&& lastsourcemsg.contains(e.getSourceMessage()))
|
&& lastsourcemsg.contains(e.getSourceMessage()))
|
||||||
return;
|
return;
|
||||||
SendException(e.getException(), e.getSourceMessage());
|
SendException(e.getException(), e.getSourceMessage());
|
||||||
if (lastthrown.size() >= 10)
|
if (lastthrown.size() >= 10)
|
||||||
lastthrown.remove(0);
|
lastthrown.remove(0);
|
||||||
if (lastsourcemsg.size() >= 10)
|
if (lastsourcemsg.size() >= 10)
|
||||||
lastsourcemsg.remove(0);
|
lastsourcemsg.remove(0);
|
||||||
lastthrown.add(e.getException());
|
lastthrown.add(e.getException());
|
||||||
lastsourcemsg.add(e.getSourceMessage());
|
lastsourcemsg.add(e.getSourceMessage());
|
||||||
e.setHandled();
|
e.setHandled();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SendException(Throwable e, String sourcemessage) {
|
private static void SendException(Throwable e, String sourcemessage) {
|
||||||
if (instance == null) return;
|
if (instance == null) return;
|
||||||
try {
|
try {
|
||||||
IChannel channel = getChannel();
|
Mono<MessageChannel> channel = getChannel();
|
||||||
assert channel != null;
|
assert channel != null;
|
||||||
IRole coderRole = instance.pingRole(channel.getGuild()).get();
|
Mono<Role> coderRole;
|
||||||
StringBuilder sb = TBMCCoreAPI.IsTestServer() ? new StringBuilder()
|
if (channel instanceof GuildChannel)
|
||||||
: new StringBuilder(coderRole == null ? "" : coderRole.mention()).append("\n");
|
coderRole = instance.pingRole(((GuildChannel) channel).getGuild()).get();
|
||||||
sb.append(sourcemessage).append("\n");
|
else
|
||||||
sb.append("```").append("\n");
|
coderRole = Mono.empty();
|
||||||
String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n"))
|
coderRole.map(role -> TBMCCoreAPI.IsTestServer() ? new StringBuilder()
|
||||||
.filter(s -> !s.contains("\tat ") || s.contains("\tat buttondevteam."))
|
: new StringBuilder(role.getMention()).append("\n"))
|
||||||
.collect(Collectors.joining("\n"));
|
.defaultIfEmpty(new StringBuilder())
|
||||||
if (stackTrace.length() > 1800)
|
.flatMap(sb -> {
|
||||||
stackTrace = stackTrace.substring(0, 1800);
|
sb.append(sourcemessage).append("\n");
|
||||||
sb.append(stackTrace).append("\n");
|
sb.append("```").append("\n");
|
||||||
sb.append("```");
|
String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n"))
|
||||||
DiscordPlugin.sendMessageToChannel(channel, sb.toString()); //Instance isn't null here
|
.filter(s -> !s.contains("\tat ") || s.contains("\tat buttondevteam."))
|
||||||
} catch (Exception ex) {
|
.collect(Collectors.joining("\n"));
|
||||||
ex.printStackTrace();
|
if (sb.length() + stackTrace.length() >= 1980)
|
||||||
}
|
stackTrace = stackTrace.substring(0, 1980 - sb.length());
|
||||||
}
|
sb.append(stackTrace).append("\n");
|
||||||
|
sb.append("```");
|
||||||
|
return channel.flatMap(ch -> ch.createMessage(sb.toString()));
|
||||||
|
}).subscribe();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static ExceptionListenerModule instance;
|
private static ExceptionListenerModule instance;
|
||||||
|
|
||||||
public static IChannel getChannel() {
|
public static Mono<MessageChannel> getChannel() {
|
||||||
if (instance != null) return instance.channel().get();
|
if (instance != null) return instance.channel().get();
|
||||||
return null;
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigData<IChannel> channel() {
|
private ReadOnlyConfigData<Mono<MessageChannel>> channel() {
|
||||||
return DPUtils.channelData(getConfig(), "channel", 239519012529111040L);
|
return DPUtils.channelData(getConfig(), "channel", 239519012529111040L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigData<IRole> pingRole(IGuild guild) {
|
private ConfigData<Mono<Role>> pingRole(Mono<Guild> guild) {
|
||||||
return DPUtils.roleData(getConfig(), "pingRole", "Coder", guild);
|
return DPUtils.roleData(getConfig(), "pingRole", "Coder", guild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,17 @@ import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
|
import buttondevteam.lib.architecture.ReadOnlyConfigData;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import discord4j.core.event.domain.PresenceUpdateEvent;
|
||||||
|
import discord4j.core.object.entity.*;
|
||||||
|
import discord4j.core.object.presence.Status;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.obj.*;
|
|
||||||
import sx.blah.discord.util.EmbedBuilder;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -23,36 +25,43 @@ import java.util.Random;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The YEEHAW event uses an emoji named :YEEHAW: if available
|
||||||
|
*/
|
||||||
public class FunModule extends Component<DiscordPlugin> implements Listener {
|
public class FunModule extends Component<DiscordPlugin> implements Listener {
|
||||||
private static final String[] serverReadyStrings = new String[]{"In one week from now", // Ali
|
private static final String[] serverReadyStrings = new String[]{"In one week from now", // Ali
|
||||||
"Between now and the heat-death of the universe.", // Ghostise
|
"Between now and the heat-death of the universe.", // Ghostise
|
||||||
"Soon™", "Ask again this time next month", // Ghostise
|
"Soon™", "Ask again this time next month", // Ghostise
|
||||||
"In about 3 seconds", // Nicolai
|
"In about 3 seconds", // Nicolai
|
||||||
"After we finish 8 plugins", // Ali
|
"After we finish 8 plugins", // Ali
|
||||||
"Tomorrow.", // Ali
|
"Tomorrow.", // Ali
|
||||||
"After one tiiiny feature", // Ali
|
"After one tiiiny feature", // Ali
|
||||||
"Next commit", // Ali
|
"Next commit", // Ali
|
||||||
"After we finish strangling Towny", // Ali
|
"After we finish strangling Towny", // Ali
|
||||||
"When we kill every *fucking* bug", // Ali
|
"When we kill every *fucking* bug", // Ali
|
||||||
"Once the server stops screaming.", // Ali
|
"Once the server stops screaming.", // Ali
|
||||||
"After HL3 comes out", // Ali
|
"After HL3 comes out", // Ali
|
||||||
"Next time you ask", // Ali
|
"Next time you ask", // Ali
|
||||||
"When will *you* be open?" // Ali
|
"When will *you* be open?" // Ali
|
||||||
};
|
};
|
||||||
|
|
||||||
private ConfigData<Boolean> serverReady() {
|
/**
|
||||||
return getConfig().getData("serverReady", true);
|
* Questions that the bot will choose a random answer to give to.
|
||||||
|
*/
|
||||||
|
private ConfigData<String[]> serverReady() {
|
||||||
|
return getConfig().getData("serverReady", () -> new String[]{"when will the server be open",
|
||||||
|
"when will the server be ready", "when will the server be done", "when will the server be complete",
|
||||||
|
"when will the server be finished", "when's the server ready", "when's the server open",
|
||||||
|
"Vhen vill ze server be open?"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Answers for a recognized question. Selected randomly.
|
||||||
|
*/
|
||||||
private ConfigData<ArrayList<String>> serverReadyAnswers() {
|
private ConfigData<ArrayList<String>> serverReadyAnswers() {
|
||||||
return getConfig().getData("serverReadyAnswers", () -> Lists.newArrayList(serverReadyStrings)); //TODO: Test
|
return getConfig().getData("serverReadyAnswers", () -> Lists.newArrayList(serverReadyStrings)); //TODO: Test
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] serverReadyQuestions = new String[]{"when will the server be open",
|
|
||||||
"when will the server be ready", "when will the server be done", "when will the server be complete",
|
|
||||||
"when will the server be finished", "when's the server ready", "when's the server open",
|
|
||||||
"Vhen vill ze server be open?"};
|
|
||||||
|
|
||||||
private static final Random serverReadyRandom = new Random();
|
private static final Random serverReadyRandom = new Random();
|
||||||
private static final ArrayList<Short> usableServerReadyStrings = new ArrayList<>(0);
|
private static final ArrayList<Short> usableServerReadyStrings = new ArrayList<>(0);
|
||||||
|
|
||||||
|
@ -76,10 +85,10 @@ public class FunModule extends Component<DiscordPlugin> implements Listener {
|
||||||
|
|
||||||
private static short ListC = 0;
|
private static short ListC = 0;
|
||||||
|
|
||||||
public static boolean executeMemes(IMessage message) {
|
public static boolean executeMemes(Message message) {
|
||||||
val fm = ComponentManager.getIfEnabled(FunModule.class);
|
val fm = ComponentManager.getIfEnabled(FunModule.class);
|
||||||
if (fm == null) return false;
|
if (fm == null) return false;
|
||||||
String msglowercased = message.getContent().toLowerCase();
|
String msglowercased = message.getContent().orElse("").toLowerCase();
|
||||||
lastlist++;
|
lastlist++;
|
||||||
if (lastlist > 5) {
|
if (lastlist > 5) {
|
||||||
ListC = 0;
|
ListC = 0;
|
||||||
|
@ -87,22 +96,20 @@ public class FunModule extends Component<DiscordPlugin> implements Listener {
|
||||||
}
|
}
|
||||||
if (msglowercased.equals("list") && Bukkit.getOnlinePlayers().size() == lastlistp && ListC++ > 2) // Lowered already
|
if (msglowercased.equals("list") && Bukkit.getOnlinePlayers().size() == lastlistp && ListC++ > 2) // Lowered already
|
||||||
{
|
{
|
||||||
message.reply("Stop it. You know the answer.");
|
DPUtils.reply(message, null, "Stop it. You know the answer.").subscribe();
|
||||||
lastlist = 0;
|
lastlist = 0;
|
||||||
lastlistp = (short) Bukkit.getOnlinePlayers().size();
|
lastlistp = (short) Bukkit.getOnlinePlayers().size();
|
||||||
return true; //Handled
|
return true; //Handled
|
||||||
}
|
}
|
||||||
lastlistp = (short) Bukkit.getOnlinePlayers().size(); //Didn't handle
|
lastlistp = (short) Bukkit.getOnlinePlayers().size(); //Didn't handle
|
||||||
if (fm.serverReady().get()) {
|
if (!TBMCCoreAPI.IsTestServer()
|
||||||
if (!TBMCCoreAPI.IsTestServer()
|
&& Arrays.stream(fm.serverReady().get()).anyMatch(msglowercased::contains)) {
|
||||||
&& Arrays.stream(serverReadyQuestions).anyMatch(msglowercased::contains)) {
|
int next;
|
||||||
int next;
|
if (usableServerReadyStrings.size() == 0)
|
||||||
if (usableServerReadyStrings.size() == 0)
|
fm.createUsableServerReadyStrings();
|
||||||
fm.createUsableServerReadyStrings();
|
next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size()));
|
||||||
next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size()));
|
DPUtils.reply(message, null, fm.serverReadyAnswers().get().get(next)).subscribe();
|
||||||
DiscordPlugin.sendMessageToChannel(message.getChannel(), serverReadyStrings[next]);
|
return false; //Still process it as a command/mcchat if needed
|
||||||
return false; //Still process it as a command/mcchat if needed
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -112,12 +119,12 @@ public class FunModule extends Component<DiscordPlugin> implements Listener {
|
||||||
ListC = 0;
|
ListC = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigData<IRole> fullHouseDevRole(IGuild guild) {
|
private ConfigData<Mono<Role>> fullHouseDevRole(Mono<Guild> guild) {
|
||||||
return DPUtils.roleData(getConfig(), "fullHouseDevRole", "Developer", guild);
|
return DPUtils.roleData(getConfig(), "fullHouseDevRole", "Developer", guild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ConfigData<IChannel> fullHouseChannel() {
|
private ReadOnlyConfigData<Mono<MessageChannel>> fullHouseChannel() {
|
||||||
return DPUtils.channelData(getConfig(), "fullHouseChannel", 219626707458457603L);
|
return DPUtils.channelData(getConfig(), "fullHouseChannel", 219626707458457603L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,24 +133,24 @@ public class FunModule extends Component<DiscordPlugin> implements Listener {
|
||||||
public static void handleFullHouse(PresenceUpdateEvent event) {
|
public static void handleFullHouse(PresenceUpdateEvent event) {
|
||||||
val fm = ComponentManager.getIfEnabled(FunModule.class);
|
val fm = ComponentManager.getIfEnabled(FunModule.class);
|
||||||
if (fm == null) return;
|
if (fm == null) return;
|
||||||
val channel = fm.fullHouseChannel().get();
|
if (Calendar.getInstance().get(Calendar.DAY_OF_MONTH) % 5 != 0) return;
|
||||||
if (channel == null) return;
|
fm.fullHouseChannel().get()
|
||||||
val devrole = fm.fullHouseDevRole(channel.getGuild()).get();
|
.filter(ch -> ch instanceof GuildChannel)
|
||||||
if (devrole == null) return;
|
.flatMap(channel -> fm.fullHouseDevRole(((GuildChannel) channel).getGuild()).get()
|
||||||
if (event.getOldPresence().getStatus().equals(StatusType.OFFLINE)
|
.filter(role -> event.getOld().map(p -> p.getStatus().equals(Status.OFFLINE)).orElse(false))
|
||||||
&& !event.getNewPresence().getStatus().equals(StatusType.OFFLINE)
|
.filter(role -> !event.getCurrent().getStatus().equals(Status.OFFLINE))
|
||||||
&& event.getUser().getRolesForGuild(channel.getGuild()).stream()
|
.filterWhen(devrole -> event.getMember().flatMap(m -> m.getRoles()
|
||||||
.anyMatch(r -> r.getLongID() == devrole.getLongID())
|
.any(r -> r.getId().asLong() == devrole.getId().asLong())))
|
||||||
&& channel.getGuild().getUsersByRole(devrole).stream()
|
.filterWhen(devrole ->
|
||||||
.noneMatch(u -> u.getPresence().getStatus().equals(StatusType.OFFLINE))
|
event.getGuild().flatMapMany(g -> g.getMembers().filter(m -> m.getRoleIds().stream().anyMatch(s -> s.equals(devrole.getId()))))
|
||||||
&& lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime())
|
.flatMap(Member::getPresence).all(pr -> !pr.getStatus().equals(Status.OFFLINE)))
|
||||||
&& Calendar.getInstance().get(Calendar.DAY_OF_MONTH) % 5 == 0) {
|
.filter(devrole -> lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime())) //This should stay so it checks this last
|
||||||
DiscordPlugin.sendMessageToChannel(channel, "Full house!",
|
.flatMap(devrole -> {
|
||||||
new EmbedBuilder()
|
lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime());
|
||||||
.withImage(
|
return channel.createMessage(mcs -> mcs.setContent("Full house!").setEmbed(ecs ->
|
||||||
"https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")
|
ecs.setImage(
|
||||||
.build());
|
"https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")
|
||||||
lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime());
|
));
|
||||||
}
|
})).subscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
package buttondevteam.discordplugin.listeners;
|
package buttondevteam.discordplugin.listeners;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.DPUtils;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.discordplugin.commands.Command2DCSender;
|
import buttondevteam.discordplugin.commands.Command2DCSender;
|
||||||
|
import buttondevteam.discordplugin.util.Timings;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
import discord4j.core.object.entity.Message;
|
||||||
import sx.blah.discord.handle.obj.IMessage;
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
import sx.blah.discord.handle.obj.IRole;
|
import discord4j.core.object.entity.PrivateChannel;
|
||||||
|
import discord4j.core.object.entity.Role;
|
||||||
|
import lombok.val;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public class CommandListener {
|
public class CommandListener {
|
||||||
/**
|
/**
|
||||||
|
@ -13,44 +20,61 @@ public class CommandListener {
|
||||||
*
|
*
|
||||||
* @param message The Discord message
|
* @param message The Discord message
|
||||||
* @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message
|
* @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message
|
||||||
* @return Whether it ran the command
|
* @return Whether it <b>did not run</b> the command
|
||||||
*/
|
*/
|
||||||
public static boolean runCommand(IMessage message, boolean mentionedonly) {
|
public static Mono<Boolean> runCommand(Message message, MessageChannel commandChannel, boolean mentionedonly) {
|
||||||
if (message.getContent().length() == 0)
|
Timings timings = CommonListeners.timings;
|
||||||
return false; //Pin messages and such, let the mcchat listener deal with it
|
Mono<Boolean> ret = Mono.just(true);
|
||||||
final IChannel channel = message.getChannel();
|
if (!message.getContent().isPresent())
|
||||||
if (!mentionedonly) { //mentionedonly conditions are in CommonListeners
|
return ret; //Pin messages and such, let the mcchat listener deal with it
|
||||||
if (!message.getChannel().isPrivate()
|
val content = message.getContent().get();
|
||||||
&& !(message.getContent().charAt(0) == DiscordPlugin.getPrefix()
|
timings.printElapsed("A");
|
||||||
&& channel.getStringID().equals(DiscordPlugin.plugin.CommandChannel().get().getStringID()))) //
|
return message.getChannel().flatMap(channel -> {
|
||||||
return false;
|
Mono<?> tmp = ret;
|
||||||
message.getChannel().setTypingStatus(true); // Fun
|
if (!mentionedonly) { //mentionedonly conditions are in CommonListeners
|
||||||
}
|
timings.printElapsed("B");
|
||||||
final StringBuilder cmdwithargs = new StringBuilder(message.getContent());
|
if (!(channel instanceof PrivateChannel)
|
||||||
final String mention = DiscordPlugin.dc.getOurUser().mention(false);
|
&& !(content.charAt(0) == DiscordPlugin.getPrefix()
|
||||||
final String mentionNick = DiscordPlugin.dc.getOurUser().mention(true);
|
&& channel.getId().asLong() == commandChannel.getId().asLong())) //
|
||||||
boolean gotmention = checkanddeletemention(cmdwithargs, mention, message);
|
return ret;
|
||||||
gotmention = checkanddeletemention(cmdwithargs, mentionNick, message) || gotmention;
|
timings.printElapsed("C");
|
||||||
for (String mentionRole : (Iterable<String>) message.getRoleMentions().stream().filter(r -> DiscordPlugin.dc.getOurUser().hasRole(r)).map(IRole::mention)::iterator)
|
tmp = ret.then(channel.type()).thenReturn(true); // Fun (this true is ignored - x)
|
||||||
gotmention = checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention; // Delete all mentions
|
}
|
||||||
if (mentionedonly && !gotmention) {
|
final StringBuilder cmdwithargs = new StringBuilder(content);
|
||||||
message.getChannel().setTypingStatus(false);
|
val gotmention = new AtomicBoolean();
|
||||||
return false;
|
timings.printElapsed("Before self");
|
||||||
}
|
return tmp.flatMapMany(x ->
|
||||||
message.getChannel().setTypingStatus(true);
|
DiscordPlugin.dc.getSelf().flatMap(self -> self.asMember(DiscordPlugin.mainServer.getId()))
|
||||||
String cmdwithargsString = cmdwithargs.toString();
|
.flatMapMany(self -> {
|
||||||
try {
|
timings.printElapsed("D");
|
||||||
if (!DiscordPlugin.plugin.getManager().handleCommand(new Command2DCSender(message), cmdwithargsString))
|
gotmention.set(checkanddeletemention(cmdwithargs, self.getMention(), message));
|
||||||
message.reply("Unknown command. Do " + DiscordPlugin.getPrefix() + "help for help.\n" + cmdwithargsString);
|
gotmention.set(checkanddeletemention(cmdwithargs, self.getNicknameMention(), message) || gotmention.get());
|
||||||
} catch (Exception e) {
|
val mentions = message.getRoleMentions();
|
||||||
TBMCCoreAPI.SendException("Failed to process Discord command: " + cmdwithargsString, e);
|
return self.getRoles().filterWhen(r -> mentions.any(rr -> rr.getName().equals(r.getName())))
|
||||||
}
|
.map(Role::getMention);
|
||||||
message.getChannel().setTypingStatus(false);
|
}).map(mentionRole -> {
|
||||||
return true;
|
timings.printElapsed("E");
|
||||||
|
gotmention.set(checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention.get()); // Delete all mentions
|
||||||
|
return !mentionedonly || gotmention.get(); //Stops here if false
|
||||||
|
}).switchIfEmpty(Mono.fromSupplier(() -> !mentionedonly || gotmention.get())))
|
||||||
|
.filter(b -> b).last(false).filter(b -> b).doOnNext(b -> channel.type().subscribe()).flatMap(b -> {
|
||||||
|
String cmdwithargsString = cmdwithargs.toString();
|
||||||
|
try {
|
||||||
|
timings.printElapsed("F");
|
||||||
|
if (!DiscordPlugin.plugin.getManager().handleCommand(new Command2DCSender(message), cmdwithargsString))
|
||||||
|
return DPUtils.reply(message, channel, "Unknown command. Do " + DiscordPlugin.getPrefix() + "help for help.\n" + cmdwithargsString)
|
||||||
|
.map(m -> false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
TBMCCoreAPI.SendException("Failed to process Discord command: " + cmdwithargsString, e);
|
||||||
|
}
|
||||||
|
return Mono.just(false); //If the command succeeded or there was an error, return false
|
||||||
|
}).defaultIfEmpty(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkanddeletemention(StringBuilder cmdwithargs, String mention, IMessage message) {
|
private static boolean checkanddeletemention(StringBuilder cmdwithargs, String mention, Message message) {
|
||||||
if (message.getContent().startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text
|
final char prefix = DiscordPlugin.getPrefix();
|
||||||
|
if (message.getContent().orElse("").startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text
|
||||||
if (cmdwithargs.length() > mention.length() + 1) {
|
if (cmdwithargs.length() > mention.length() + 1) {
|
||||||
int i = cmdwithargs.indexOf(" ", mention.length());
|
int i = cmdwithargs.indexOf(" ", mention.length());
|
||||||
if (i == -1)
|
if (i == -1)
|
||||||
|
@ -60,14 +84,16 @@ public class CommandListener {
|
||||||
for (; i < cmdwithargs.length() && cmdwithargs.charAt(i) == ' '; i++)
|
for (; i < cmdwithargs.length() && cmdwithargs.charAt(i) == ' '; i++)
|
||||||
; //Removes any space before the command
|
; //Removes any space before the command
|
||||||
cmdwithargs.delete(0, i);
|
cmdwithargs.delete(0, i);
|
||||||
cmdwithargs.insert(0, DiscordPlugin.getPrefix()); //Always use the prefix for processing
|
cmdwithargs.insert(0, prefix); //Always use the prefix for processing
|
||||||
} else
|
} else
|
||||||
cmdwithargs.replace(0, cmdwithargs.length(), DiscordPlugin.getPrefix() + "help");
|
cmdwithargs.replace(0, cmdwithargs.length(), prefix + "help");
|
||||||
else {
|
else {
|
||||||
|
if (cmdwithargs.length() == 0)
|
||||||
|
cmdwithargs.replace(0, cmdwithargs.length(), prefix + "help");
|
||||||
|
else if (cmdwithargs.charAt(0) != prefix)
|
||||||
|
cmdwithargs.insert(0, prefix);
|
||||||
return false; //Don't treat / as mention, mentions can be used in public mcchat
|
return false; //Don't treat / as mention, mentions can be used in public mcchat
|
||||||
}
|
}
|
||||||
if (cmdwithargs.length() == 0)
|
|
||||||
cmdwithargs.replace(0, cmdwithargs.length(), DiscordPlugin.getPrefix() + "help");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,23 @@ import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.discordplugin.fun.FunModule;
|
import buttondevteam.discordplugin.fun.FunModule;
|
||||||
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
||||||
import buttondevteam.discordplugin.role.GameRoleModule;
|
import buttondevteam.discordplugin.role.GameRoleModule;
|
||||||
|
import buttondevteam.discordplugin.util.Timings;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
|
import discord4j.core.event.EventDispatcher;
|
||||||
|
import discord4j.core.event.domain.PresenceUpdateEvent;
|
||||||
|
import discord4j.core.event.domain.message.MessageCreateEvent;
|
||||||
|
import discord4j.core.event.domain.role.RoleCreateEvent;
|
||||||
|
import discord4j.core.event.domain.role.RoleDeleteEvent;
|
||||||
|
import discord4j.core.event.domain.role.RoleUpdateEvent;
|
||||||
|
import discord4j.core.object.entity.PrivateChannel;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import sx.blah.discord.api.events.IListener;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
|
|
||||||
import sx.blah.discord.handle.impl.events.guild.role.RoleCreateEvent;
|
|
||||||
import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent;
|
|
||||||
import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent;
|
|
||||||
import sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent;
|
|
||||||
|
|
||||||
public class CommonListeners {
|
public class CommonListeners {
|
||||||
|
|
||||||
|
public static final Timings timings = new Timings();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MentionEvent:
|
MentionEvent:
|
||||||
- CommandListener (starts with mention, only 'channelcon' and not in #bot)
|
- CommandListener (starts with mention, only 'channelcon' and not in #bot)
|
||||||
|
@ -26,52 +31,59 @@ public class CommonListeners {
|
||||||
- Minecraft chat (is enabled in the channel and message isn't [/]mcchat)
|
- Minecraft chat (is enabled in the channel and message isn't [/]mcchat)
|
||||||
- CommandListener (with the correct prefix in #bot, or in private)
|
- CommandListener (with the correct prefix in #bot, or in private)
|
||||||
*/
|
*/
|
||||||
public static IListener<?>[] getListeners() {
|
public static void register(EventDispatcher dispatcher) {
|
||||||
return new IListener[]{new IListener<MessageReceivedEvent>() {
|
dispatcher.on(MessageCreateEvent.class).flatMap(event -> {
|
||||||
@Override
|
timings.printElapsed("Message received");
|
||||||
public void handle(MessageReceivedEvent event) {
|
val def = Mono.empty();
|
||||||
if (DiscordPlugin.SafeMode)
|
if (DiscordPlugin.SafeMode)
|
||||||
return;
|
return def;
|
||||||
if (event.getMessage().getAuthor().isBot())
|
val author = event.getMessage().getAuthor();
|
||||||
return;
|
if (!author.isPresent() || author.get().isBot())
|
||||||
if (FunModule.executeMemes(event.getMessage()))
|
return def;
|
||||||
return;
|
if (FunModule.executeMemes(event.getMessage()))
|
||||||
try {
|
return def;
|
||||||
boolean handled = false;
|
val commandChannel = DiscordPlugin.plugin.commandChannel().get();
|
||||||
val commandChannel = DiscordPlugin.plugin.CommandChannel().get();
|
val commandCh = DPUtils.getMessageChannel(DiscordPlugin.plugin.commandChannel());
|
||||||
if ((commandChannel != null && event.getChannel().getLongID() == commandChannel.getLongID()) //If mentioned, that's higher than chat
|
return commandCh.filterWhen(ch -> event.getMessage().getChannel().map(mch ->
|
||||||
|| event.getMessage().getContent().contains("channelcon")) //Only 'channelcon' is allowed in other channels
|
(commandChannel != null && mch.getId().asLong() == commandChannel.asLong()) //If mentioned, that's higher than chat
|
||||||
handled = CommandListener.runCommand(event.getMessage(), true); //#bot is handled here
|
|| mch instanceof PrivateChannel
|
||||||
if (handled) return;
|
|| event.getMessage().getContent().orElse("").contains("channelcon")) //Only 'channelcon' is allowed in other channels
|
||||||
val mcchat = Component.getComponents().get(MinecraftChatModule.class);
|
.flatMap(shouldRun -> { //Only continue if this doesn't handle the event
|
||||||
if (mcchat != null && mcchat.isEnabled()) //ComponentManager.isEnabled() searches the component again
|
if (!shouldRun)
|
||||||
handled = ((MinecraftChatModule) mcchat).getListener().handleDiscord(event); //Also runs Discord commands in chat channels
|
return Mono.just(true); //The condition is only for the first command execution, not mcchat
|
||||||
if (!handled)
|
timings.printElapsed("Run command 1");
|
||||||
handled = CommandListener.runCommand(event.getMessage(), false);
|
return CommandListener.runCommand(event.getMessage(), ch, true); //#bot is handled here
|
||||||
} catch (Exception e) {
|
})).filterWhen(ch -> {
|
||||||
TBMCCoreAPI.SendException("An error occured while handling a message!", e);
|
timings.printElapsed("mcchat");
|
||||||
}
|
val mcchat = Component.getComponents().get(MinecraftChatModule.class);
|
||||||
}
|
if (mcchat != null && mcchat.isEnabled()) //ComponentManager.isEnabled() searches the component again
|
||||||
}, new IListener<sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent>() {
|
return ((MinecraftChatModule) mcchat).getListener().handleDiscord(event); //Also runs Discord commands in chat channels
|
||||||
@Override
|
return Mono.empty(); //Wasn't handled, continue
|
||||||
public void handle(PresenceUpdateEvent event) {
|
}).filterWhen(ch -> {
|
||||||
if (DiscordPlugin.SafeMode)
|
timings.printElapsed("Run command 2");
|
||||||
return;
|
return CommandListener.runCommand(event.getMessage(), ch, false);
|
||||||
FunModule.handleFullHouse(event);
|
});
|
||||||
}
|
}).onErrorContinue((err, obj) -> TBMCCoreAPI.SendException("An error occured while handling a message!", err))
|
||||||
}, (IListener<RoleCreateEvent>) GameRoleModule::handleRoleEvent, //
|
.subscribe();
|
||||||
(IListener<RoleDeleteEvent>) GameRoleModule::handleRoleEvent, //
|
dispatcher.on(PresenceUpdateEvent.class).subscribe(event -> {
|
||||||
(IListener<RoleUpdateEvent>) GameRoleModule::handleRoleEvent};
|
if (DiscordPlugin.SafeMode)
|
||||||
|
return;
|
||||||
|
FunModule.handleFullHouse(event);
|
||||||
|
});
|
||||||
|
dispatcher.on(RoleCreateEvent.class).subscribe(GameRoleModule::handleRoleEvent);
|
||||||
|
dispatcher.on(RoleDeleteEvent.class).subscribe(GameRoleModule::handleRoleEvent);
|
||||||
|
dispatcher.on(RoleUpdateEvent.class).subscribe(GameRoleModule::handleRoleEvent);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean debug = false;
|
private static boolean debug = false;
|
||||||
|
|
||||||
public static void debug(String debug) {
|
public static void debug(String debug) {
|
||||||
if (CommonListeners.debug) //Debug
|
if (CommonListeners.debug) //Debug
|
||||||
DPUtils.getLogger().info(debug);
|
DPUtils.getLogger().info(debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean debug() {
|
public static boolean debug() {
|
||||||
return debug = !debug;
|
return debug = !debug;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,39 +5,50 @@ import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.discordplugin.commands.ConnectCommand;
|
import buttondevteam.discordplugin.commands.ConnectCommand;
|
||||||
import buttondevteam.lib.player.TBMCPlayerGetInfoEvent;
|
import buttondevteam.lib.player.TBMCPlayerGetInfoEvent;
|
||||||
import buttondevteam.lib.player.TBMCPlayerJoinEvent;
|
import buttondevteam.lib.player.TBMCPlayerJoinEvent;
|
||||||
|
import discord4j.core.object.entity.Member;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
|
import discord4j.core.object.util.Snowflake;
|
||||||
|
import lombok.val;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.server.ServerCommandEvent;
|
import org.bukkit.event.server.ServerCommandEvent;
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
public class MCListener implements Listener {
|
public class MCListener implements Listener {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(TBMCPlayerJoinEvent e) {
|
public void onPlayerJoin(TBMCPlayerJoinEvent e) {
|
||||||
if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) {
|
if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) {
|
||||||
@SuppressWarnings("ConstantConditions") IUser user = DiscordPlugin.dc
|
@SuppressWarnings("ConstantConditions") User user = DiscordPlugin.dc
|
||||||
.getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get())));
|
.getUserById(Snowflake.of(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get()))).block();
|
||||||
e.getPlayer().sendMessage("§bTo connect with the Discord account @" + user.getName() + "#" + user.getDiscriminator()
|
if (user == null) return;
|
||||||
|
e.getPlayer().sendMessage("§bTo connect with the Discord account @" + user.getUsername() + "#" + user.getDiscriminator()
|
||||||
+ " do /discord accept");
|
+ " do /discord accept");
|
||||||
e.getPlayer().sendMessage("§bIf it wasn't you, do /discord decline");
|
e.getPlayer().sendMessage("§bIf it wasn't you, do /discord decline");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onGetInfo(TBMCPlayerGetInfoEvent e) {
|
public void onGetInfo(TBMCPlayerGetInfoEvent e) {
|
||||||
if (DiscordPlugin.SafeMode)
|
if (DiscordPlugin.SafeMode)
|
||||||
return;
|
return;
|
||||||
DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class);
|
DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class);
|
||||||
if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals(""))
|
if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals(""))
|
||||||
return;
|
return;
|
||||||
IUser user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()));
|
User user = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).block();
|
||||||
e.addInfo("Discord tag: " + user.getName() + "#" + user.getDiscriminator());
|
if (user == null) return;
|
||||||
e.addInfo(user.getPresence().getStatus().toString());
|
e.addInfo("Discord tag: " + user.getUsername() + "#" + user.getDiscriminator());
|
||||||
if (user.getPresence().getActivity().isPresent() && user.getPresence().getText().isPresent())
|
Member member = user.asMember(DiscordPlugin.mainServer.getId()).block();
|
||||||
e.addInfo(user.getPresence().getActivity().get() + ": " + user.getPresence().getText().get());
|
if (member == null) return;
|
||||||
}
|
val pr = member.getPresence().block();
|
||||||
|
if (pr == null) return;
|
||||||
|
e.addInfo(pr.getStatus().toString());
|
||||||
|
if (pr.getActivity().isPresent()) {
|
||||||
|
val activity = pr.getActivity().get();
|
||||||
|
e.addInfo(activity.getType() + ": " + activity.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onServerCommand(ServerCommandEvent e) {
|
public void onServerCommand(ServerCommandEvent e) {
|
||||||
DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped
|
DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,16 @@ import buttondevteam.lib.TBMCSystemChatEvent;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
import buttondevteam.lib.player.TBMCPlayer;
|
import buttondevteam.lib.player.TBMCPlayer;
|
||||||
|
import discord4j.core.object.entity.Message;
|
||||||
|
import discord4j.core.object.util.Permission;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import sx.blah.discord.handle.obj.IMessage;
|
|
||||||
import sx.blah.discord.handle.obj.Permissions;
|
|
||||||
import sx.blah.discord.util.PermissionUtils;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -32,15 +33,17 @@ import java.util.stream.Collectors;
|
||||||
"Mentioning the bot is needed in this case because the / prefix only works in #bot.", //
|
"Mentioning the bot is needed in this case because the / prefix only works in #bot.", //
|
||||||
"Invite link: <https://discordapp.com/oauth2/authorize?client_id=226443037893591041&scope=bot&permissions=268509264>" //
|
"Invite link: <https://discordapp.com/oauth2/authorize?client_id=226443037893591041&scope=bot&permissions=268509264>" //
|
||||||
})
|
})
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class ChannelconCommand extends ICommand2DC {
|
public class ChannelconCommand extends ICommand2DC {
|
||||||
|
private final MinecraftChatModule module;
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public boolean remove(Command2DCSender sender) {
|
public boolean remove(Command2DCSender sender) {
|
||||||
val message = sender.getMessage();
|
val message = sender.getMessage();
|
||||||
if (checkPerms(message)) return true;
|
if (checkPerms(message)) return true;
|
||||||
if (MCChatCustom.removeCustomChat(message.getChannel()))
|
if (MCChatCustom.removeCustomChat(message.getChannelId()))
|
||||||
message.reply("channel connection removed.");
|
DPUtils.reply(message, null, "channel connection removed.").subscribe();
|
||||||
else
|
else
|
||||||
message.reply("this channel isn't connected.");
|
DPUtils.reply(message, null, "this channel isn't connected.").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,13 +51,13 @@ public class ChannelconCommand extends ICommand2DC {
|
||||||
public boolean toggle(Command2DCSender sender, @Command2.OptionalArg String toggle) {
|
public boolean toggle(Command2DCSender sender, @Command2.OptionalArg String toggle) {
|
||||||
val message = sender.getMessage();
|
val message = sender.getMessage();
|
||||||
if (checkPerms(message)) return true;
|
if (checkPerms(message)) return true;
|
||||||
val cc = MCChatCustom.getCustomChat(message.getChannel());
|
val cc = MCChatCustom.getCustomChat(message.getChannelId());
|
||||||
if (cc == null)
|
if (cc == null)
|
||||||
return respond(sender, "this channel isn't connected.");
|
return respond(sender, "this channel isn't connected.");
|
||||||
Supplier<String> togglesString = () -> Arrays.stream(ChannelconBroadcast.values()).map(t -> t.toString().toLowerCase() + ": " + ((cc.toggles & t.flag) == 0 ? "disabled" : "enabled")).collect(Collectors.joining("\n"))
|
Supplier<String> togglesString = () -> Arrays.stream(ChannelconBroadcast.values()).map(t -> t.toString().toLowerCase() + ": " + ((cc.toggles & t.flag) == 0 ? "disabled" : "enabled")).collect(Collectors.joining("\n"))
|
||||||
+ "\n\n" + TBMCSystemChatEvent.BroadcastTarget.stream().map(target -> target.getName() + ": " + (cc.brtoggles.contains(target) ? "enabled" : "disabled")).collect(Collectors.joining("\n"));
|
+ "\n\n" + TBMCSystemChatEvent.BroadcastTarget.stream().map(target -> target.getName() + ": " + (cc.brtoggles.contains(target) ? "enabled" : "disabled")).collect(Collectors.joining("\n"));
|
||||||
if (toggle == null) {
|
if (toggle == null) {
|
||||||
message.reply("toggles:\n" + togglesString.get());
|
DPUtils.reply(message, null, "toggles:\n" + togglesString.get()).subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
String arg = toggle.toUpperCase();
|
String arg = toggle.toUpperCase();
|
||||||
|
@ -62,7 +65,7 @@ public class ChannelconCommand extends ICommand2DC {
|
||||||
if (!b.isPresent()) {
|
if (!b.isPresent()) {
|
||||||
val bt = TBMCSystemChatEvent.BroadcastTarget.get(arg);
|
val bt = TBMCSystemChatEvent.BroadcastTarget.get(arg);
|
||||||
if (bt == null) {
|
if (bt == null) {
|
||||||
message.reply("cannot find toggle. Toggles:\n" + togglesString.get());
|
DPUtils.reply(message, null, "cannot find toggle. Toggles:\n" + togglesString.get()).subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
final boolean add;
|
final boolean add;
|
||||||
|
@ -80,7 +83,7 @@ public class ChannelconCommand extends ICommand2DC {
|
||||||
//1 1 | 0
|
//1 1 | 0
|
||||||
// XOR
|
// XOR
|
||||||
cc.toggles ^= b.get().flag;
|
cc.toggles ^= b.get().flag;
|
||||||
message.reply("'" + b.get().toString().toLowerCase() + "' " + ((cc.toggles & b.get().flag) == 0 ? "disabled" : "enabled"));
|
DPUtils.reply(message, null, "'" + b.get().toString().toLowerCase() + "' " + ((cc.toggles & b.get().flag) == 0 ? "disabled" : "enabled")).subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,45 +91,49 @@ public class ChannelconCommand extends ICommand2DC {
|
||||||
public boolean def(Command2DCSender sender, String channelID) {
|
public boolean def(Command2DCSender sender, String channelID) {
|
||||||
val message = sender.getMessage();
|
val message = sender.getMessage();
|
||||||
if (checkPerms(message)) return true;
|
if (checkPerms(message)) return true;
|
||||||
if (MCChatCustom.hasCustomChat(message.getChannel()))
|
if (MCChatCustom.hasCustomChat(message.getChannelId()))
|
||||||
return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it.");
|
return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it.");
|
||||||
val chan = Channel.getChannels().filter(ch -> ch.ID.equalsIgnoreCase(channelID) || (Arrays.stream(ch.IDs().get()).anyMatch(cid -> cid.equalsIgnoreCase(channelID)))).findAny();
|
val chan = Channel.getChannels().filter(ch -> ch.ID.equalsIgnoreCase(channelID) || (Arrays.stream(ch.IDs().get()).anyMatch(cid -> cid.equalsIgnoreCase(channelID)))).findAny();
|
||||||
if (!chan.isPresent()) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW)
|
if (!chan.isPresent()) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW)
|
||||||
message.reply("MC channel with ID '" + channelID + "' not found! The ID is the command for it without the /.");
|
DPUtils.reply(message, null, "MC channel with ID '" + channelID + "' not found! The ID is the command for it without the /.").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
val dp = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class);
|
if (!message.getAuthor().isPresent()) return true;
|
||||||
|
val author = message.getAuthor().get();
|
||||||
|
val dp = DiscordPlayer.getUser(author.getId().asString(), DiscordPlayer.class);
|
||||||
val chp = dp.getAs(TBMCPlayer.class);
|
val chp = dp.getAs(TBMCPlayer.class);
|
||||||
if (chp == null) {
|
if (chp == null) {
|
||||||
message.reply("you need to connect your Minecraft account. On our server in " + DPUtils.botmention() + " do " + DiscordPlugin.getPrefix() + "connect <MCname>");
|
DPUtils.reply(message, null, "you need to connect your Minecraft account. On our server in " + DPUtils.botmention() + " do " + DiscordPlugin.getPrefix() + "connect <MCname>").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
DiscordConnectedPlayer dcp = new DiscordConnectedPlayer(message.getAuthor(), message.getChannel(), chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName());
|
val channel = message.getChannel().block();
|
||||||
|
DiscordConnectedPlayer dcp = new DiscordConnectedPlayer(message.getAuthor().get(), channel, chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName(), module);
|
||||||
//Using a fake player with no login/logout, should be fine for this event
|
//Using a fake player with no login/logout, should be fine for this event
|
||||||
String groupid = chan.get().getGroupID(dcp);
|
String groupid = chan.get().getGroupID(dcp);
|
||||||
if (groupid == null && !(chan.get() instanceof ChatRoom)) { //ChatRooms don't allow it unless the user joins, which happens later
|
if (groupid == null && !(chan.get() instanceof ChatRoom)) { //ChatRooms don't allow it unless the user joins, which happens later
|
||||||
message.reply("sorry, you cannot use that Minecraft channel.");
|
DPUtils.reply(message, null, "sorry, you cannot use that Minecraft channel.").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (chan.get() instanceof ChatRoom) { //ChatRooms don't work well
|
if (chan.get() instanceof ChatRoom) { //ChatRooms don't work well
|
||||||
message.reply("chat rooms are not supported yet.");
|
DPUtils.reply(message, null, "chat rooms are not supported yet.").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/*if (MCChatListener.getCustomChats().stream().anyMatch(cc -> cc.groupID.equals(groupid) && cc.mcchannel.ID.equals(chan.get().ID))) {
|
/*if (MCChatListener.getCustomChats().stream().anyMatch(cc -> cc.groupID.equals(groupid) && cc.mcchannel.ID.equals(chan.get().ID))) {
|
||||||
message.reply("sorry, this MC chat is already connected to a different channel, multiple channels are not supported atm.");
|
DPUtils.reply(message, null, "sorry, this MC chat is already connected to a different channel, multiple channels are not supported atm.");
|
||||||
return true;
|
return true;
|
||||||
}*/ //TODO: "Channel admins" that can connect channels?
|
}*/ //TODO: "Channel admins" that can connect channels?
|
||||||
MCChatCustom.addCustomChat(message.getChannel(), groupid, chan.get(), message.getAuthor(), dcp, 0, new HashSet<>());
|
MCChatCustom.addCustomChat(channel, groupid, chan.get(), author, dcp, 0, new HashSet<>());
|
||||||
if (chan.get() instanceof ChatRoom)
|
if (chan.get() instanceof ChatRoom)
|
||||||
message.reply("alright, connection made to the room!");
|
DPUtils.reply(message, null, "alright, connection made to the room!").subscribe();
|
||||||
else
|
else
|
||||||
message.reply("alright, connection made to group `" + groupid + "`!");
|
DPUtils.reply(message, null, "alright, connection made to group `" + groupid + "`!").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkPerms(IMessage message) {
|
@SuppressWarnings("ConstantConditions")
|
||||||
if (!PermissionUtils.hasPermissions(message.getChannel(), message.getAuthor(), Permissions.MANAGE_CHANNEL)) {
|
private boolean checkPerms(Message message) {
|
||||||
message.reply("you need to have manage permissions for this channel!");
|
if (!message.getAuthorAsMember().block().getBasePermissions().block().contains(Permission.MANAGE_CHANNELS)) {
|
||||||
|
DPUtils.reply(message, null, "you need to have manage permissions for this channel!").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -140,7 +147,7 @@ public class ChannelconCommand extends ICommand2DC {
|
||||||
"You need to have access to the MC channel and have manage permissions on the Discord channel.", //
|
"You need to have access to the MC channel and have manage permissions on the Discord channel.", //
|
||||||
"You also need to have your Minecraft account connected. In " + DPUtils.botmention() + " use " + DiscordPlugin.getPrefix() + "connect <mcname>.", //
|
"You also need to have your Minecraft account connected. In " + DPUtils.botmention() + " use " + DiscordPlugin.getPrefix() + "connect <mcname>.", //
|
||||||
"Call this command from the channel you want to use.", //
|
"Call this command from the channel you want to use.", //
|
||||||
"Usage: @" + DiscordPlugin.dc.getOurUser().getName() + " channelcon <mcchannel>", //
|
"Usage: " + Objects.requireNonNull(DiscordPlugin.dc.getSelf().block()).getMention() + " channelcon <mcchannel>", //
|
||||||
"Use the ID (command) of the channel, for example `g` for the global chat.", //
|
"Use the ID (command) of the channel, for example `g` for the global chat.", //
|
||||||
"To remove a connection use @ChromaBot channelcon remove in the channel.", //
|
"To remove a connection use @ChromaBot channelcon remove in the channel.", //
|
||||||
"Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix() + " prefix only works in " + DPUtils.botmention() + ".", //
|
"Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix() + " prefix only works in " + DPUtils.botmention() + ".", //
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package buttondevteam.discordplugin.mcchat;
|
package buttondevteam.discordplugin.mcchat;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.DPUtils;
|
||||||
import buttondevteam.discordplugin.DiscordPlayer;
|
import buttondevteam.discordplugin.DiscordPlayer;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.discordplugin.commands.Command2DCSender;
|
import buttondevteam.discordplugin.commands.Command2DCSender;
|
||||||
|
@ -7,6 +8,7 @@ import buttondevteam.discordplugin.commands.ICommand2DC;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
|
import discord4j.core.object.entity.PrivateChannel;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
|
||||||
@CommandClass(helpText = {
|
@CommandClass(helpText = {
|
||||||
|
@ -20,18 +22,20 @@ public class MCChatCommand extends ICommand2DC {
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public boolean def(Command2DCSender sender) {
|
public boolean def(Command2DCSender sender) {
|
||||||
val message = sender.getMessage();
|
val message = sender.getMessage();
|
||||||
if (!message.getChannel().isPrivate()) {
|
val channel = message.getChannel().block();
|
||||||
message.reply("this command can only be issued in a direct message with the bot.");
|
@SuppressWarnings("OptionalGetWithoutIsPresent") val author = message.getAuthor().get();
|
||||||
|
if (!(channel instanceof PrivateChannel)) {
|
||||||
|
DPUtils.reply(message, null, "this command can only be issued in a direct message with the bot.").subscribe();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
try (final DiscordPlayer user = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class)) {
|
try (final DiscordPlayer user = DiscordPlayer.getUser(author.getId().asString(), DiscordPlayer.class)) {
|
||||||
boolean mcchat = !user.isMinecraftChatEnabled();
|
boolean mcchat = !user.isMinecraftChatEnabled();
|
||||||
MCChatPrivate.privateMCChat(message.getChannel(), mcchat, message.getAuthor(), user);
|
MCChatPrivate.privateMCChat(channel, mcchat, author, user);
|
||||||
message.reply("Minecraft chat " + (mcchat //
|
DPUtils.reply(message, null, "Minecraft chat " + (mcchat //
|
||||||
? "enabled. Use '" + DiscordPlugin.getPrefix() + "mcchat' again to turn it off." //
|
? "enabled. Use '" + DiscordPlugin.getPrefix() + "mcchat' again to turn it off." //
|
||||||
: "disabled."));
|
: "disabled.")).subscribe();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e);
|
TBMCCoreAPI.SendException("Error while setting mcchat for user " + author.getUsername() + "#" + author.getDiscriminator(), e);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} // TODO: Pin channel switching to indicate the current channel
|
} // TODO: Pin channel switching to indicate the current channel
|
||||||
|
|
|
@ -4,10 +4,11 @@ import buttondevteam.core.component.channel.Channel;
|
||||||
import buttondevteam.core.component.channel.ChatRoom;
|
import buttondevteam.core.component.channel.ChatRoom;
|
||||||
import buttondevteam.discordplugin.DiscordConnectedPlayer;
|
import buttondevteam.discordplugin.DiscordConnectedPlayer;
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
|
import discord4j.core.object.util.Snowflake;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -21,7 +22,7 @@ public class MCChatCustom {
|
||||||
*/
|
*/
|
||||||
static ArrayList<CustomLMD> lastmsgCustom = new ArrayList<>();
|
static ArrayList<CustomLMD> lastmsgCustom = new ArrayList<>();
|
||||||
|
|
||||||
public static void addCustomChat(IChannel channel, String groupid, Channel mcchannel, IUser user, DiscordConnectedPlayer dcp, int toggles, Set<TBMCSystemChatEvent.BroadcastTarget> brtoggles) {
|
public static void addCustomChat(MessageChannel channel, String groupid, Channel mcchannel, User user, DiscordConnectedPlayer dcp, int toggles, Set<TBMCSystemChatEvent.BroadcastTarget> brtoggles) {
|
||||||
if (mcchannel instanceof ChatRoom) {
|
if (mcchannel instanceof ChatRoom) {
|
||||||
((ChatRoom) mcchannel).joinRoom(dcp);
|
((ChatRoom) mcchannel).joinRoom(dcp);
|
||||||
if (groupid == null) groupid = mcchannel.getGroupID(dcp);
|
if (groupid == null) groupid = mcchannel.getGroupID(dcp);
|
||||||
|
@ -30,19 +31,19 @@ public class MCChatCustom {
|
||||||
lastmsgCustom.add(lmd);
|
lastmsgCustom.add(lmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasCustomChat(IChannel channel) {
|
public static boolean hasCustomChat(Snowflake channel) {
|
||||||
return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getLongID() == channel.getLongID());
|
return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getId().asLong() == channel.asLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static CustomLMD getCustomChat(IChannel channel) {
|
public static CustomLMD getCustomChat(Snowflake channel) {
|
||||||
return lastmsgCustom.stream().filter(lmd -> lmd.channel.getLongID() == channel.getLongID()).findAny().orElse(null);
|
return lastmsgCustom.stream().filter(lmd -> lmd.channel.getId().asLong() == channel.asLong()).findAny().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean removeCustomChat(IChannel channel) {
|
public static boolean removeCustomChat(Snowflake channel) {
|
||||||
MCChatUtils.lastmsgfromd.remove(channel.getLongID());
|
MCChatUtils.lastmsgfromd.remove(channel.asLong());
|
||||||
return lastmsgCustom.removeIf(lmd -> {
|
return lastmsgCustom.removeIf(lmd -> {
|
||||||
if (lmd.channel.getLongID() != channel.getLongID())
|
if (lmd.channel.getId().asLong() != channel.asLong())
|
||||||
return false;
|
return false;
|
||||||
if (lmd.mcchannel instanceof ChatRoom)
|
if (lmd.mcchannel instanceof ChatRoom)
|
||||||
((ChatRoom) lmd.mcchannel).leaveRoom(lmd.dcp);
|
((ChatRoom) lmd.mcchannel).leaveRoom(lmd.dcp);
|
||||||
|
@ -61,8 +62,8 @@ public class MCChatCustom {
|
||||||
public int toggles;
|
public int toggles;
|
||||||
public Set<TBMCSystemChatEvent.BroadcastTarget> brtoggles;
|
public Set<TBMCSystemChatEvent.BroadcastTarget> brtoggles;
|
||||||
|
|
||||||
private CustomLMD(@NonNull IChannel channel, @NonNull IUser user,
|
private CustomLMD(@NonNull MessageChannel channel, @NonNull User user,
|
||||||
@NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp, int toggles, Set<TBMCSystemChatEvent.BroadcastTarget> brtoggles) {
|
@NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp, int toggles, Set<TBMCSystemChatEvent.BroadcastTarget> brtoggles) {
|
||||||
super(channel, user);
|
super(channel, user);
|
||||||
groupID = groupid;
|
groupID = groupid;
|
||||||
this.mcchannel = mcchannel;
|
this.mcchannel = mcchannel;
|
||||||
|
|
|
@ -8,26 +8,26 @@ import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.discordplugin.DiscordSender;
|
import buttondevteam.discordplugin.DiscordSender;
|
||||||
import buttondevteam.discordplugin.DiscordSenderBase;
|
import buttondevteam.discordplugin.DiscordSenderBase;
|
||||||
import buttondevteam.discordplugin.listeners.CommandListener;
|
import buttondevteam.discordplugin.listeners.CommandListener;
|
||||||
|
import buttondevteam.discordplugin.listeners.CommonListeners;
|
||||||
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
||||||
|
import buttondevteam.discordplugin.util.Timings;
|
||||||
import buttondevteam.lib.*;
|
import buttondevteam.lib.*;
|
||||||
import buttondevteam.lib.chat.ChatMessage;
|
import buttondevteam.lib.chat.ChatMessage;
|
||||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||||
import buttondevteam.lib.player.TBMCPlayer;
|
import buttondevteam.lib.player.TBMCPlayer;
|
||||||
import com.vdurmont.emoji.EmojiParser;
|
import com.vdurmont.emoji.EmojiParser;
|
||||||
|
import discord4j.core.event.domain.message.MessageCreateEvent;
|
||||||
|
import discord4j.core.object.Embed;
|
||||||
|
import discord4j.core.object.entity.*;
|
||||||
|
import discord4j.core.object.util.Snowflake;
|
||||||
|
import discord4j.core.spec.EmbedCreateSpec;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
import sx.blah.discord.api.internal.json.objects.EmbedObject;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
|
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IMessage;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
import sx.blah.discord.util.DiscordException;
|
|
||||||
import sx.blah.discord.util.EmbedBuilder;
|
|
||||||
import sx.blah.discord.util.MissingPermissionsException;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
@ -36,15 +36,16 @@ import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MCChatListener implements Listener {
|
public class MCChatListener implements Listener {
|
||||||
private BukkitTask sendtask;
|
private BukkitTask sendtask;
|
||||||
private LinkedBlockingQueue<AbstractMap.SimpleEntry<TBMCChatEvent, Instant>> sendevents = new LinkedBlockingQueue<>();
|
private LinkedBlockingQueue<AbstractMap.SimpleEntry<TBMCChatEvent, Instant>> sendevents = new LinkedBlockingQueue<>();
|
||||||
private Runnable sendrunnable;
|
private Runnable sendrunnable;
|
||||||
private static Thread sendthread;
|
private static Thread sendthread;
|
||||||
private final MinecraftChatModule module;
|
private final MinecraftChatModule module;
|
||||||
|
|
||||||
public MCChatListener(MinecraftChatModule minecraftChatModule) {
|
public MCChatListener(MinecraftChatModule minecraftChatModule) {
|
||||||
|
@ -52,359 +53,363 @@ public class MCChatListener implements Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler // Minecraft
|
@EventHandler // Minecraft
|
||||||
public void onMCChat(TBMCChatEvent ev) {
|
public void onMCChat(TBMCChatEvent ev) {
|
||||||
if (!ComponentManager.isEnabled(MinecraftChatModule.class) || ev.isCancelled()) //SafeMode: Needed so it doesn't restart after server shutdown
|
if (!ComponentManager.isEnabled(MinecraftChatModule.class) || ev.isCancelled()) //SafeMode: Needed so it doesn't restart after server shutdown
|
||||||
return;
|
return;
|
||||||
sendevents.add(new AbstractMap.SimpleEntry<>(ev, Instant.now()));
|
sendevents.add(new AbstractMap.SimpleEntry<>(ev, Instant.now()));
|
||||||
if (sendtask != null)
|
if (sendtask != null)
|
||||||
return;
|
return;
|
||||||
sendrunnable = () -> {
|
sendrunnable = () -> {
|
||||||
sendthread = Thread.currentThread();
|
sendthread = Thread.currentThread();
|
||||||
processMCToDiscord();
|
processMCToDiscord();
|
||||||
if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
|
if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
|
||||||
sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
|
sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
|
||||||
};
|
};
|
||||||
sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
|
sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMCToDiscord() {
|
private void processMCToDiscord() {
|
||||||
try {
|
try {
|
||||||
TBMCChatEvent e;
|
TBMCChatEvent e;
|
||||||
Instant time;
|
Instant time;
|
||||||
val se = sendevents.take(); // Wait until an element is available
|
val se = sendevents.take(); // Wait until an element is available
|
||||||
e = se.getKey();
|
e = se.getKey();
|
||||||
time = se.getValue();
|
time = se.getValue();
|
||||||
|
|
||||||
final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName().get()) + "] " //
|
final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName().get()) + "] " //
|
||||||
+ ("Minecraft".equals(e.getOrigin()) ? "" : "[" + e.getOrigin().substring(0, 1) + "]") //
|
+ ("Minecraft".equals(e.getOrigin()) ? "" : "[" + e.getOrigin().substring(0, 1) + "]") //
|
||||||
+ (DPUtils.sanitizeStringNoEscape(e.getSender() instanceof Player //
|
+ (DPUtils.sanitizeStringNoEscape(ThorpeUtils.getDisplayName(e.getSender())));
|
||||||
? ((Player) e.getSender()).getDisplayName() //
|
val color = e.getChannel().Color().get();
|
||||||
: e.getSender().getName()));
|
final Consumer<EmbedCreateSpec> embed = ecs -> {
|
||||||
val color = e.getChannel().Color().get();
|
ecs.setDescription(e.getMessage()).setColor(new Color(color.getRed(),
|
||||||
final EmbedBuilder embed = new EmbedBuilder().withAuthorName(authorPlayer)
|
color.getGreen(), color.getBlue()));
|
||||||
.withDescription(e.getMessage()).withColor(new Color(color.getRed(),
|
if (e.getSender() instanceof Player)
|
||||||
color.getGreen(), color.getBlue()));
|
DPUtils.embedWithHead(ecs, authorPlayer, e.getSender().getName(),
|
||||||
// embed.appendField("Channel", ((e.getSender() instanceof DiscordSenderBase ? "d|" : "")
|
"https://tbmcplugins.github.io/profile.html?type=minecraft&id="
|
||||||
// + DiscordPlugin.sanitizeString(e.getChannel().DisplayName)), false);
|
+ ((Player) e.getSender()).getUniqueId());
|
||||||
if (e.getSender() instanceof Player)
|
else if (e.getSender() instanceof DiscordSenderBase)
|
||||||
DPUtils.embedWithHead(
|
ecs.setAuthor(authorPlayer, "https://tbmcplugins.github.io/profile.html?type=discord&id=" // TODO: Constant/method to get URLs like this
|
||||||
embed.withAuthorUrl("https://tbmcplugins.github.io/profile.html?type=minecraft&id="
|
+ ((DiscordSenderBase) e.getSender()).getUser().getId().asString(),
|
||||||
+ ((Player) e.getSender()).getUniqueId()),
|
((DiscordSenderBase) e.getSender()).getUser().getAvatarUrl());
|
||||||
e.getSender().getName());
|
else
|
||||||
else if (e.getSender() instanceof DiscordSenderBase)
|
DPUtils.embedWithHead(ecs, authorPlayer, e.getSender().getName(), null);
|
||||||
embed.withAuthorIcon(((DiscordSenderBase) e.getSender()).getUser().getAvatarURL())
|
ecs.setTimestamp(time);
|
||||||
.withAuthorUrl("https://tbmcplugins.github.io/profile.html?type=discord&id="
|
};
|
||||||
+ ((DiscordSenderBase) e.getSender()).getUser().getStringID()); // TODO: Constant/method to get URLs like this
|
final long nanoTime = System.nanoTime();
|
||||||
// embed.withFooterText(e.getChannel().DisplayName);
|
InterruptibleConsumer<MCChatUtils.LastMsgData> doit = lastmsgdata -> {
|
||||||
embed.withTimestamp(time);
|
if (lastmsgdata.message == null
|
||||||
final long nanoTime = System.nanoTime();
|
|| !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().map(Embed.Author::getName).orElse(null))
|
||||||
InterruptibleConsumer<MCChatUtils.LastMsgData> doit = lastmsgdata -> {
|
|| lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120
|
||||||
final EmbedObject embedObject = embed.build();
|
|| !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID)) {
|
||||||
if (lastmsgdata.message == null || lastmsgdata.message.isDeleted()
|
lastmsgdata.message = lastmsgdata.channel.createEmbed(embed).block();
|
||||||
|| !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().getName())
|
lastmsgdata.time = nanoTime;
|
||||||
|| lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120
|
lastmsgdata.mcchannel = e.getChannel();
|
||||||
|| !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID)) {
|
lastmsgdata.content = e.getMessage();
|
||||||
lastmsgdata.message = DiscordPlugin.sendMessageToChannelWait(lastmsgdata.channel, "",
|
} else {
|
||||||
embedObject); // TODO Use ChromaBot API
|
lastmsgdata.content = lastmsgdata.content + "\n"
|
||||||
lastmsgdata.time = nanoTime;
|
+ e.getMessage(); // The message object doesn't get updated
|
||||||
lastmsgdata.mcchannel = e.getChannel();
|
lastmsgdata.message.edit(mes -> mes.setEmbed(embed.andThen(ecs ->
|
||||||
lastmsgdata.content = embedObject.description;
|
ecs.setDescription(lastmsgdata.content)))).block();
|
||||||
} else
|
}
|
||||||
try {
|
};
|
||||||
lastmsgdata.content = embedObject.description = lastmsgdata.content + "\n"
|
// Checks if the given channel is different than where the message was sent from
|
||||||
+ embedObject.description;// The message object doesn't get updated
|
// Or if it was from MC
|
||||||
final MCChatUtils.LastMsgData _lastmsgdata = lastmsgdata;
|
Predicate<Snowflake> isdifferentchannel = id -> !(e.getSender() instanceof DiscordSenderBase)
|
||||||
DPUtils.perform(() -> _lastmsgdata.message.edit("", embedObject));
|
|| ((DiscordSenderBase) e.getSender()).getChannel().getId().asLong() != id.asLong();
|
||||||
} catch (MissingPermissionsException | DiscordException e1) {
|
|
||||||
TBMCCoreAPI.SendException("An error occurred while editing chat message!", e1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Checks if the given channel is different than where the message was sent from
|
|
||||||
// Or if it was from MC
|
|
||||||
Predicate<IChannel> isdifferentchannel = ch -> !(e.getSender() instanceof DiscordSenderBase)
|
|
||||||
|| ((DiscordSenderBase) e.getSender()).getChannel().getLongID() != ch.getLongID();
|
|
||||||
|
|
||||||
if (e.getChannel().isGlobal()
|
if (e.getChannel().isGlobal()
|
||||||
&& (e.isFromCommand() || isdifferentchannel.test(module.chatChannel().get())))
|
&& (e.isFromCommand() || isdifferentchannel.test(module.chatChannel().get())))
|
||||||
doit.accept(MCChatUtils.lastmsgdata == null
|
doit.accept(MCChatUtils.lastmsgdata == null
|
||||||
? MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannel().get(), null)
|
? MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannelMono().block(), null)
|
||||||
: MCChatUtils.lastmsgdata);
|
: MCChatUtils.lastmsgdata);
|
||||||
|
|
||||||
for (MCChatUtils.LastMsgData data : MCChatPrivate.lastmsgPerUser) {
|
for (MCChatUtils.LastMsgData data : MCChatPrivate.lastmsgPerUser) {
|
||||||
if ((e.isFromCommand() || isdifferentchannel.test(data.channel))
|
if ((e.isFromCommand() || isdifferentchannel.test(data.channel.getId()))
|
||||||
&& e.shouldSendTo(MCChatUtils.getSender(data.channel, data.user)))
|
&& e.shouldSendTo(MCChatUtils.getSender(data.channel.getId(), data.user)))
|
||||||
doit.accept(data);
|
doit.accept(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
val iterator = MCChatCustom.lastmsgCustom.iterator();
|
val iterator = MCChatCustom.lastmsgCustom.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
val lmd = iterator.next();
|
val lmd = iterator.next();
|
||||||
if ((e.isFromCommand() || isdifferentchannel.test(lmd.channel)) //Test if msg is from Discord
|
if ((e.isFromCommand() || isdifferentchannel.test(lmd.channel.getId())) //Test if msg is from Discord
|
||||||
&& e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it
|
&& e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it
|
||||||
&& e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58
|
&& e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58
|
||||||
if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions
|
if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions
|
||||||
doit.accept(lmd);
|
doit.accept(lmd);
|
||||||
else {
|
else {
|
||||||
iterator.remove(); //If the user no longer has permission, remove the connection
|
iterator.remove(); //If the user no longer has permission, remove the connection
|
||||||
DiscordPlugin.sendMessageToChannel(lmd.channel, "The user no longer has permission to view the channel, connection removed.");
|
lmd.channel.createMessage("The user no longer has permission to view the channel, connection removed.").subscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (InterruptedException ex) { //Stop if interrupted anywhere
|
} catch (InterruptedException ex) { //Stop if interrupted anywhere
|
||||||
sendtask.cancel();
|
sendtask.cancel();
|
||||||
sendtask = null;
|
sendtask = null;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
TBMCCoreAPI.SendException("Error while sending message to Discord!", ex);
|
TBMCCoreAPI.SendException("Error while sending message to Discord!", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onChatPreprocess(TBMCChatPreprocessEvent event) {
|
public void onChatPreprocess(TBMCChatPreprocessEvent event) {
|
||||||
int start = -1;
|
int start = -1;
|
||||||
while ((start = event.getMessage().indexOf('@', start + 1)) != -1) {
|
while ((start = event.getMessage().indexOf('@', start + 1)) != -1) {
|
||||||
int mid = event.getMessage().indexOf('#', start + 1);
|
int mid = event.getMessage().indexOf('#', start + 1);
|
||||||
if (mid == -1)
|
if (mid == -1)
|
||||||
return;
|
return;
|
||||||
int end_ = event.getMessage().indexOf(' ', mid + 1);
|
int end_ = event.getMessage().indexOf(' ', mid + 1);
|
||||||
if (end_ == -1)
|
if (end_ == -1)
|
||||||
end_ = event.getMessage().length();
|
end_ = event.getMessage().length();
|
||||||
final int end = end_;
|
final int end = end_;
|
||||||
final int startF = start;
|
final int startF = start;
|
||||||
DiscordPlugin.dc.getUsersByName(event.getMessage().substring(start + 1, mid)).stream()
|
val user = DiscordPlugin.dc.getUsers().filter(u -> u.getUsername().equals(event.getMessage().substring(startF + 1, mid)))
|
||||||
.filter(u -> u.getDiscriminator().equals(event.getMessage().substring(mid + 1, end))).findAny()
|
.filter(u -> u.getDiscriminator().equals(event.getMessage().substring(mid + 1, end))).blockFirst();
|
||||||
.ifPresent(user -> event.setMessage(event.getMessage().substring(0, startF) + "@" + user.getName()
|
if (user != null) //TODO: Nicknames
|
||||||
+ (event.getMessage().length() > end ? event.getMessage().substring(end) : ""))); // TODO: Add formatting
|
event.setMessage(event.getMessage().substring(0, startF) + "@" + user.getUsername()
|
||||||
start = end; // Skip any @s inside the mention
|
+ (event.getMessage().length() > end ? event.getMessage().substring(end) : "")); // TODO: Add formatting
|
||||||
}
|
start = end; // Skip any @s inside the mention
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender
|
// ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender
|
||||||
// Offline public chat......x............................................
|
// Offline public chat......x............................................
|
||||||
// Online public chat.......x...........................................x
|
// Online public chat.......x...........................................x
|
||||||
// Offline private chat.....x.......................x....................
|
// Offline private chat.....x.......................x....................
|
||||||
// Online private chat......x.......................x...................x
|
// Online private chat......x.......................x...................x
|
||||||
// If online and enabling private chat, don't login
|
// If online and enabling private chat, don't login
|
||||||
// If leaving the server and private chat is enabled (has ConnectedPlayer), call login in a task on lowest priority
|
// If leaving the server and private chat is enabled (has ConnectedPlayer), call login in a task on lowest priority
|
||||||
// If private chat is enabled and joining the server, logout the fake player on highest priority
|
// If private chat is enabled and joining the server, logout the fake player on highest priority
|
||||||
// If online and disabling private chat, don't logout
|
// If online and disabling private chat, don't logout
|
||||||
// The maps may not contain the senders for UnconnectedSenders
|
// The maps may not contain the senders for UnconnectedSenders
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the listener. Any calls to onMCChat will restart it as long as we're not in safe mode.
|
* Stop the listener. Any calls to onMCChat will restart it as long as we're not in safe mode.
|
||||||
*
|
*
|
||||||
* @param wait Wait 5 seconds for the threads to stop
|
* @param wait Wait 5 seconds for the threads to stop
|
||||||
*/
|
*/
|
||||||
public static void stop(boolean wait) {
|
public static void stop(boolean wait) {
|
||||||
if (sendthread != null) sendthread.interrupt();
|
if (sendthread != null) sendthread.interrupt();
|
||||||
if (recthread != null) recthread.interrupt();
|
if (recthread != null) recthread.interrupt();
|
||||||
try {
|
try {
|
||||||
if (sendthread != null) {
|
if (sendthread != null) {
|
||||||
sendthread.interrupt();
|
sendthread.interrupt();
|
||||||
if (wait)
|
if (wait)
|
||||||
sendthread.join(5000);
|
sendthread.join(5000);
|
||||||
}
|
}
|
||||||
if (recthread != null) {
|
if (recthread != null) {
|
||||||
recthread.interrupt();
|
recthread.interrupt();
|
||||||
if (wait)
|
if (wait)
|
||||||
recthread.join(5000);
|
recthread.join(5000);
|
||||||
}
|
}
|
||||||
MCChatUtils.lastmsgdata = null;
|
MCChatUtils.lastmsgdata = null;
|
||||||
MCChatPrivate.lastmsgPerUser.clear();
|
MCChatPrivate.lastmsgPerUser.clear();
|
||||||
MCChatCustom.lastmsgCustom.clear();
|
MCChatCustom.lastmsgCustom.clear();
|
||||||
MCChatUtils.lastmsgfromd.clear();
|
MCChatUtils.lastmsgfromd.clear();
|
||||||
MCChatUtils.ConnectedSenders.clear();
|
MCChatUtils.ConnectedSenders.clear();
|
||||||
MCChatUtils.UnconnectedSenders.clear();
|
MCChatUtils.UnconnectedSenders.clear();
|
||||||
recthread = sendthread = null;
|
recthread = sendthread = null;
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace(); //This thread shouldn't be interrupted
|
e.printStackTrace(); //This thread shouldn't be interrupted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BukkitTask rectask;
|
private BukkitTask rectask;
|
||||||
private LinkedBlockingQueue<MessageReceivedEvent> recevents = new LinkedBlockingQueue<>();
|
private LinkedBlockingQueue<MessageCreateEvent> recevents = new LinkedBlockingQueue<>();
|
||||||
private Runnable recrun;
|
private Runnable recrun;
|
||||||
private static Thread recthread;
|
private static Thread recthread;
|
||||||
|
|
||||||
// Discord
|
// Discord
|
||||||
public boolean handleDiscord(MessageReceivedEvent ev) {
|
public Mono<Boolean> handleDiscord(MessageCreateEvent ev) {
|
||||||
if (!ComponentManager.isEnabled(MinecraftChatModule.class))
|
val ret = Mono.just(true);
|
||||||
return false;
|
if (!ComponentManager.isEnabled(MinecraftChatModule.class))
|
||||||
val author = ev.getMessage().getAuthor();
|
return ret;
|
||||||
final boolean hasCustomChat = MCChatCustom.hasCustomChat(ev.getChannel());
|
Timings timings = CommonListeners.timings;
|
||||||
if (ev.getMessage().getChannel().getLongID() != module.chatChannel().get().getLongID()
|
timings.printElapsed("Chat event");
|
||||||
&& !(ev.getMessage().getChannel().isPrivate() && MCChatPrivate.isMinecraftChatEnabled(author.getStringID()))
|
val author = ev.getMessage().getAuthor();
|
||||||
&& !hasCustomChat)
|
final boolean hasCustomChat = MCChatCustom.hasCustomChat(ev.getMessage().getChannelId());
|
||||||
return false; //Chat isn't enabled on this channel
|
return ev.getMessage().getChannel().filter(channel -> {
|
||||||
if (ev.getMessage().getChannel().isPrivate() //Only in private chat
|
timings.printElapsed("Filter 1");
|
||||||
&& ev.getMessage().getContent().length() < "/mcchat<>".length()
|
return !(ev.getMessage().getChannelId().asLong() != module.chatChannel().get().asLong()
|
||||||
&& ev.getMessage().getContent().replace("/", "")
|
&& !(channel instanceof PrivateChannel
|
||||||
.equalsIgnoreCase("mcchat")) //Either mcchat or /mcchat
|
&& author.map(u -> MCChatPrivate.isMinecraftChatEnabled(u.getId().asString())).orElse(false)
|
||||||
return false; //Allow disabling the chat if needed
|
&& !hasCustomChat)); //Chat isn't enabled on this channel
|
||||||
if (CommandListener.runCommand(ev.getMessage(), true))
|
}).filter(channel -> {
|
||||||
return true; //Allow running commands in chat channels
|
timings.printElapsed("Filter 2");
|
||||||
MCChatUtils.resetLastMessage(ev.getChannel());
|
return !(channel instanceof PrivateChannel //Only in private chat
|
||||||
recevents.add(ev);
|
&& ev.getMessage().getContent().isPresent()
|
||||||
if (rectask != null)
|
&& ev.getMessage().getContent().get().length() < "/mcchat<>".length()
|
||||||
return true;
|
&& ev.getMessage().getContent().get().replace("/", "")
|
||||||
recrun = () -> { //Don't return in a while loop next time
|
.equalsIgnoreCase("mcchat")); //Either mcchat or /mcchat
|
||||||
recthread = Thread.currentThread();
|
//Allow disabling the chat if needed
|
||||||
processDiscordToMC();
|
}).filterWhen(channel -> CommandListener.runCommand(ev.getMessage(), channel, true))
|
||||||
if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
|
//Allow running commands in chat channels
|
||||||
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing
|
.filter(channel -> {
|
||||||
};
|
MCChatUtils.resetLastMessage(channel);
|
||||||
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Start message processing
|
recevents.add(ev);
|
||||||
return true;
|
timings.printElapsed("Message event added");
|
||||||
}
|
if (rectask != null)
|
||||||
|
return true;
|
||||||
|
recrun = () -> { //Don't return in a while loop next time
|
||||||
|
recthread = Thread.currentThread();
|
||||||
|
processDiscordToMC();
|
||||||
|
if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
|
||||||
|
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing
|
||||||
|
};
|
||||||
|
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Start message processing
|
||||||
|
return true;
|
||||||
|
}).map(b -> false).defaultIfEmpty(true);
|
||||||
|
}
|
||||||
|
|
||||||
private void processDiscordToMC() {
|
private void processDiscordToMC() {
|
||||||
@val
|
MessageCreateEvent event;
|
||||||
sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent event;
|
try {
|
||||||
try {
|
event = recevents.take();
|
||||||
event = recevents.take();
|
} catch (InterruptedException e1) {
|
||||||
} catch (InterruptedException e1) {
|
rectask.cancel();
|
||||||
rectask.cancel();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
val sender = event.getMessage().getAuthor().orElse(null);
|
||||||
val sender = event.getMessage().getAuthor();
|
String dmessage = event.getMessage().getContent().orElse("");
|
||||||
String dmessage = event.getMessage().getContent();
|
try {
|
||||||
try {
|
final DiscordSenderBase dsender = MCChatUtils.getSender(event.getMessage().getChannelId(), sender);
|
||||||
final DiscordSenderBase dsender = MCChatUtils.getSender(event.getMessage().getChannel(), sender);
|
val user = dsender.getChromaUser();
|
||||||
val user = dsender.getChromaUser();
|
|
||||||
|
|
||||||
for (IUser u : event.getMessage().getMentions()) {
|
for (User u : event.getMessage().getUserMentions().toIterable()) { //TODO: Role mentions
|
||||||
dmessage = dmessage.replace(u.mention(false), "@" + u.getName()); // TODO: IG Formatting
|
dmessage = dmessage.replace(u.getMention(), "@" + u.getUsername()); // TODO: IG Formatting
|
||||||
final String nick = u.getNicknameForGuild(DiscordPlugin.mainServer);
|
val m = u.asMember(DiscordPlugin.mainServer.getId()).block();
|
||||||
dmessage = dmessage.replace(u.mention(true), "@" + (nick != null ? nick : u.getName()));
|
if (m != null) {
|
||||||
}
|
final String nick = m.getDisplayName();
|
||||||
for (IChannel ch : event.getMessage().getChannelMentions()) {
|
dmessage = dmessage.replace(m.getNicknameMention(), "@" + nick);
|
||||||
dmessage = dmessage.replace(ch.mention(), "#" + ch.getName()); // TODO: IG Formatting
|
}
|
||||||
}
|
}
|
||||||
|
for (GuildChannel ch : event.getGuild().flux().flatMap(Guild::getChannels).toIterable()) {
|
||||||
|
dmessage = dmessage.replace(ch.getMention(), "#" + ch.getName()); // TODO: IG Formatting
|
||||||
|
}
|
||||||
|
|
||||||
dmessage = EmojiParser.parseToAliases(dmessage, EmojiParser.FitzpatrickAction.PARSE); //Converts emoji to text- TODO: Add option to disable (resource pack?)
|
dmessage = EmojiParser.parseToAliases(dmessage, EmojiParser.FitzpatrickAction.PARSE); //Converts emoji to text- TODO: Add option to disable (resource pack?)
|
||||||
dmessage = dmessage.replaceAll(":(\\S+)\\|type_(?:(\\d)|(1)_2):", ":$1::skin-tone-$2:"); //Convert to Discord's format so it still shows up
|
dmessage = dmessage.replaceAll(":(\\S+)\\|type_(?:(\\d)|(1)_2):", ":$1::skin-tone-$2:"); //Convert to Discord's format so it still shows up
|
||||||
|
|
||||||
Function<String, String> getChatMessage = msg -> //
|
Function<String, String> getChatMessage = msg -> //
|
||||||
msg + (event.getMessage().getAttachments().size() > 0 ? "\n" + event.getMessage()
|
msg + (event.getMessage().getAttachments().size() > 0 ? "\n" + event.getMessage()
|
||||||
.getAttachments().stream().map(IMessage.Attachment::getUrl).collect(Collectors.joining("\n"))
|
.getAttachments().stream().map(Attachment::getUrl).collect(Collectors.joining("\n"))
|
||||||
: "");
|
: "");
|
||||||
|
|
||||||
MCChatCustom.CustomLMD clmd = MCChatCustom.getCustomChat(event.getChannel());
|
MCChatCustom.CustomLMD clmd = MCChatCustom.getCustomChat(event.getMessage().getChannelId());
|
||||||
|
|
||||||
boolean react = false;
|
boolean react = false;
|
||||||
|
|
||||||
if (dmessage.startsWith("/")) { // Ingame command
|
val sendChannel = event.getMessage().getChannel().block();
|
||||||
DPUtils.perform(() -> {
|
boolean isPrivate = sendChannel instanceof PrivateChannel;
|
||||||
if (!event.getMessage().isDeleted() && !event.getChannel().isPrivate())
|
if (dmessage.startsWith("/")) { // Ingame command
|
||||||
event.getMessage().delete();
|
if (!isPrivate)
|
||||||
});
|
event.getMessage().delete().subscribe();
|
||||||
final String cmd = dmessage.substring(1);
|
final String cmd = dmessage.substring(1);
|
||||||
final String cmdlowercased = cmd.toLowerCase();
|
final String cmdlowercased = cmd.toLowerCase();
|
||||||
if (dsender instanceof DiscordSender && module.whitelistedCommands().get().stream()
|
if (dsender instanceof DiscordSender && module.whitelistedCommands().get().stream()
|
||||||
.noneMatch(s -> cmdlowercased.equals(s) || cmdlowercased.startsWith(s + " "))) {
|
.noneMatch(s -> cmdlowercased.equals(s) || cmdlowercased.startsWith(s + " "))) {
|
||||||
// Command not whitelisted
|
// Command not whitelisted
|
||||||
dsender.sendMessage("Sorry, you can only access these commands:\n"
|
dsender.sendMessage("Sorry, you can only access these commands:\n"
|
||||||
+ module.whitelistedCommands().get().stream().map(uc -> "/" + uc)
|
+ module.whitelistedCommands().get().stream().map(uc -> "/" + uc)
|
||||||
.collect(Collectors.joining(", "))
|
.collect(Collectors.joining(", "))
|
||||||
+ (user.getConnectedID(TBMCPlayer.class) == null
|
+ (user.getConnectedID(TBMCPlayer.class) == null
|
||||||
? "\nTo access your commands, first please connect your accounts, using /connect in "
|
? "\nTo access your commands, first please connect your accounts, using /connect in "
|
||||||
+ DPUtils.botmention()
|
+ DPUtils.botmention()
|
||||||
+ "\nThen y"
|
+ "\nThen y"
|
||||||
: "\nY")
|
: "\nY")
|
||||||
+ "ou can access all of your regular commands (even offline) in private chat: DM me `mcchat`!");
|
+ "ou can access all of your regular commands (even offline) in private chat: DM me `mcchat`!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
val ev = new TBMCCommandPreprocessEvent(dsender, dmessage);
|
val ev = new TBMCCommandPreprocessEvent(dsender, dmessage);
|
||||||
Bukkit.getPluginManager().callEvent(ev);
|
Bukkit.getPluginManager().callEvent(ev);
|
||||||
if (ev.isCancelled())
|
if (ev.isCancelled())
|
||||||
return;
|
return;
|
||||||
int spi = cmdlowercased.indexOf(' ');
|
int spi = cmdlowercased.indexOf(' ');
|
||||||
final String topcmd = spi == -1 ? cmdlowercased : cmdlowercased.substring(0, spi);
|
final String topcmd = spi == -1 ? cmdlowercased : cmdlowercased.substring(0, spi);
|
||||||
Optional<Channel> ch = Channel.getChannels()
|
Optional<Channel> ch = Channel.getChannels()
|
||||||
.filter(c -> c.ID.equalsIgnoreCase(topcmd)
|
.filter(c -> c.ID.equalsIgnoreCase(topcmd)
|
||||||
|| (c.IDs().get().length > 0
|
|| (c.IDs().get().length > 0
|
||||||
&& Arrays.stream(c.IDs().get()).anyMatch(id -> id.equalsIgnoreCase(topcmd)))).findAny();
|
&& Arrays.stream(c.IDs().get()).anyMatch(id -> id.equalsIgnoreCase(topcmd)))).findAny();
|
||||||
if (!ch.isPresent()) //TODO: What if talking in the public chat while we have it on a different one
|
if (!ch.isPresent()) //TODO: What if talking in the public chat while we have it on a different one
|
||||||
Bukkit.getScheduler().runTask(DiscordPlugin.plugin, //Commands need to be run sync
|
Bukkit.getScheduler().runTask(DiscordPlugin.plugin, //Commands need to be run sync
|
||||||
() -> { //TODO: Better handling...
|
() -> { //TODO: Better handling...
|
||||||
val channel = user.channel();
|
val channel = user.channel();
|
||||||
val chtmp = channel.get();
|
val chtmp = channel.get();
|
||||||
if (clmd != null) {
|
if (clmd != null) {
|
||||||
channel.set(clmd.mcchannel); //Hack to send command in the channel
|
channel.set(clmd.mcchannel); //Hack to send command in the channel
|
||||||
} //TODO: Permcheck isn't implemented for commands
|
} //TODO: Permcheck isn't implemented for commands
|
||||||
VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd);
|
VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd);
|
||||||
Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased);
|
Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased);
|
||||||
if (clmd != null)
|
if (clmd != null)
|
||||||
channel.set(chtmp);
|
channel.set(chtmp);
|
||||||
});
|
});
|
||||||
else {
|
else {
|
||||||
Channel chc = ch.get();
|
Channel chc = ch.get();
|
||||||
if (!chc.isGlobal() && !event.getMessage().getChannel().isPrivate())
|
if (!chc.isGlobal() && !isPrivate)
|
||||||
dsender.sendMessage(
|
dsender.sendMessage(
|
||||||
"You can only talk in a public chat here. DM `mcchat` to enable private chat to talk in the other channels.");
|
"You can only talk in a public chat here. DM `mcchat` to enable private chat to talk in the other channels.");
|
||||||
else {
|
else {
|
||||||
if (spi == -1) // Switch channels
|
if (spi == -1) // Switch channels
|
||||||
{
|
{
|
||||||
val channel = dsender.getChromaUser().channel();
|
val channel = dsender.getChromaUser().channel();
|
||||||
val oldch = channel.get();
|
val oldch = channel.get();
|
||||||
if (oldch instanceof ChatRoom)
|
if (oldch instanceof ChatRoom)
|
||||||
((ChatRoom) oldch).leaveRoom(dsender);
|
((ChatRoom) oldch).leaveRoom(dsender);
|
||||||
if (!oldch.ID.equals(chc.ID)) {
|
if (!oldch.ID.equals(chc.ID)) {
|
||||||
channel.set(chc);
|
channel.set(chc);
|
||||||
if (chc instanceof ChatRoom)
|
if (chc instanceof ChatRoom)
|
||||||
((ChatRoom) chc).joinRoom(dsender);
|
((ChatRoom) chc).joinRoom(dsender);
|
||||||
} else
|
} else
|
||||||
channel.set(Channel.GlobalChat);
|
channel.set(Channel.GlobalChat);
|
||||||
dsender.sendMessage("You're now talking in: "
|
dsender.sendMessage("You're now talking in: "
|
||||||
+ DPUtils.sanitizeString(channel.get().DisplayName().get()));
|
+ DPUtils.sanitizeString(channel.get().DisplayName().get()));
|
||||||
} else { // Send single message
|
} else { // Send single message
|
||||||
final String msg = cmd.substring(spi + 1);
|
final String msg = cmd.substring(spi + 1);
|
||||||
val cmb = ChatMessage.builder(dsender, user, getChatMessage.apply(msg)).fromCommand(true);
|
val cmb = ChatMessage.builder(dsender, user, getChatMessage.apply(msg)).fromCommand(true);
|
||||||
if (clmd == null)
|
if (clmd == null)
|
||||||
TBMCChatAPI.SendChatMessage(cmb.build(), chc);
|
TBMCChatAPI.SendChatMessage(cmb.build(), chc);
|
||||||
else
|
else
|
||||||
TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build(), chc);
|
TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build(), chc);
|
||||||
react = true;
|
react = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {// Not a command
|
} else {// Not a command
|
||||||
if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0
|
if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0
|
||||||
&& !event.getChannel().isPrivate() && event.getMessage().isSystemMessage()) {
|
&& !isPrivate && event.getMessage().getType() == Message.Type.CHANNEL_PINNED_MESSAGE) {
|
||||||
val rtr = clmd != null ? clmd.mcchannel.getRTR(clmd.dcp)
|
val rtr = clmd != null ? clmd.mcchannel.getRTR(clmd.dcp)
|
||||||
: dsender.getChromaUser().channel().get().getRTR(dsender);
|
: dsender.getChromaUser().channel().get().getRTR(dsender);
|
||||||
TBMCChatAPI.SendSystemMessage(clmd != null ? clmd.mcchannel : dsender.getChromaUser().channel().get(), rtr,
|
TBMCChatAPI.SendSystemMessage(clmd != null ? clmd.mcchannel : dsender.getChromaUser().channel().get(), rtr,
|
||||||
(dsender instanceof Player ? ((Player) dsender).getDisplayName()
|
(dsender instanceof Player ? ((Player) dsender).getDisplayName()
|
||||||
: dsender.getName()) + " pinned a message on Discord.", TBMCSystemChatEvent.BroadcastTarget.ALL);
|
: dsender.getName()) + " pinned a message on Discord.", TBMCSystemChatEvent.BroadcastTarget.ALL);
|
||||||
}
|
} else {
|
||||||
else {
|
val cmb = ChatMessage.builder(dsender, user, getChatMessage.apply(dmessage)).fromCommand(false);
|
||||||
val cmb = ChatMessage.builder(dsender, user, getChatMessage.apply(dmessage)).fromCommand(false);
|
if (clmd != null)
|
||||||
if (clmd != null)
|
TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build(), clmd.mcchannel);
|
||||||
TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build(), clmd.mcchannel);
|
else
|
||||||
else
|
TBMCChatAPI.SendChatMessage(cmb.build());
|
||||||
TBMCChatAPI.SendChatMessage(cmb.build());
|
react = true;
|
||||||
react = true;
|
}
|
||||||
}
|
}
|
||||||
}
|
if (react) {
|
||||||
if (react) {
|
try {
|
||||||
try {
|
val lmfd = MCChatUtils.lastmsgfromd.get(event.getMessage().getChannelId().asLong());
|
||||||
val lmfd = MCChatUtils.lastmsgfromd.get(event.getChannel().getLongID());
|
if (lmfd != null) {
|
||||||
if (lmfd != null) {
|
lmfd.removeSelfReaction(DiscordPlugin.DELIVERED_REACTION).subscribe(); // Remove it no matter what, we know it's there 99.99% of the time
|
||||||
DPUtils.perform(() -> lmfd.removeReaction(DiscordPlugin.dc.getOurUser(),
|
}
|
||||||
DiscordPlugin.DELIVERED_REACTION)); // Remove it no matter what, we know it's there 99.99% of the time
|
} catch (Exception e) {
|
||||||
}
|
TBMCCoreAPI.SendException("An error occured while removing reactions from chat!", e);
|
||||||
} catch (Exception e) {
|
}
|
||||||
TBMCCoreAPI.SendException("An error occured while removing reactions from chat!", e);
|
MCChatUtils.lastmsgfromd.put(event.getMessage().getChannelId().asLong(), event.getMessage());
|
||||||
}
|
event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION).subscribe();
|
||||||
MCChatUtils.lastmsgfromd.put(event.getChannel().getLongID(), event.getMessage());
|
}
|
||||||
DPUtils.perform(() -> event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION));
|
} catch (Exception e) {
|
||||||
}
|
TBMCCoreAPI.SendException("An error occured while handling message \"" + dmessage + "\"!", e);
|
||||||
} catch (Exception e) {
|
}
|
||||||
TBMCCoreAPI.SendException("An error occured while handling message \"" + dmessage + "\"!", e);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
private interface InterruptibleConsumer<T> {
|
private interface InterruptibleConsumer<T> {
|
||||||
void accept(T value) throws TimeoutException, InterruptedException;
|
void accept(T value) throws TimeoutException, InterruptedException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
package buttondevteam.discordplugin.mcchat;
|
package buttondevteam.discordplugin.mcchat;
|
||||||
|
|
||||||
|
import buttondevteam.core.ComponentManager;
|
||||||
import buttondevteam.discordplugin.DiscordConnectedPlayer;
|
import buttondevteam.discordplugin.DiscordConnectedPlayer;
|
||||||
import buttondevteam.discordplugin.DiscordPlayer;
|
import buttondevteam.discordplugin.DiscordPlayer;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
|
||||||
import buttondevteam.lib.player.TBMCPlayer;
|
import buttondevteam.lib.player.TBMCPlayer;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.PrivateChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IPrivateChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@ -22,27 +19,31 @@ public class MCChatPrivate {
|
||||||
*/
|
*/
|
||||||
static ArrayList<MCChatUtils.LastMsgData> lastmsgPerUser = new ArrayList<>();
|
static ArrayList<MCChatUtils.LastMsgData> lastmsgPerUser = new ArrayList<>();
|
||||||
|
|
||||||
public static boolean privateMCChat(IChannel channel, boolean start, IUser user, DiscordPlayer dp) {
|
public static boolean privateMCChat(MessageChannel channel, boolean start, User user, DiscordPlayer dp) {
|
||||||
TBMCPlayer mcp = dp.getAs(TBMCPlayer.class);
|
TBMCPlayer mcp = dp.getAs(TBMCPlayer.class);
|
||||||
if (mcp != null) { // If the accounts aren't connected, can't make a connected sender
|
if (mcp != null) { // If the accounts aren't connected, can't make a connected sender
|
||||||
val p = Bukkit.getPlayer(mcp.getUUID());
|
val p = Bukkit.getPlayer(mcp.getUUID());
|
||||||
val op = Bukkit.getOfflinePlayer(mcp.getUUID());
|
val op = Bukkit.getOfflinePlayer(mcp.getUUID());
|
||||||
|
val mcm = ComponentManager.getIfEnabled(MinecraftChatModule.class);
|
||||||
if (start) {
|
if (start) {
|
||||||
val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName());
|
val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName(), mcm);
|
||||||
MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender);
|
MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender);
|
||||||
if (p == null)// Player is offline - If the player is online, that takes precedence
|
if (p == null)// Player is offline - If the player is online, that takes precedence
|
||||||
callEventSync(new PlayerJoinEvent(sender, ""));
|
MCChatUtils.callLoginEvents(sender);
|
||||||
} else {
|
} else {
|
||||||
val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel, user);
|
val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId(), user);
|
||||||
if (p == null)// Player is offline - If the player is online, that takes precedence
|
assert sender != null;
|
||||||
callEventSync(new PlayerQuitEvent(sender, ""));
|
if (p == null // Player is offline - If the player is online, that takes precedence
|
||||||
|
&& sender.isLoggedIn()) //Don't call the quit event if login failed
|
||||||
|
MCChatUtils.callLogoutEvent(sender, true);
|
||||||
|
sender.setLoggedIn(false);
|
||||||
}
|
}
|
||||||
} // ---- PermissionsEx warning is normal on logout ----
|
} // ---- PermissionsEx warning is normal on logout ----
|
||||||
if (!start)
|
if (!start)
|
||||||
MCChatUtils.lastmsgfromd.remove(channel.getLongID());
|
MCChatUtils.lastmsgfromd.remove(channel.getId().asLong());
|
||||||
return start //
|
return start //
|
||||||
? lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs
|
? lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs
|
||||||
: lastmsgPerUser.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID());
|
: lastmsgPerUser.removeIf(lmd -> lmd.channel.getId().asLong() == channel.getId().asLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isMinecraftChatEnabled(DiscordPlayer dp) {
|
public static boolean isMinecraftChatEnabled(DiscordPlayer dp) {
|
||||||
|
@ -51,18 +52,16 @@ public class MCChatPrivate {
|
||||||
|
|
||||||
public static boolean isMinecraftChatEnabled(String did) { // Don't load the player data just for this
|
public static boolean isMinecraftChatEnabled(String did) { // Don't load the player data just for this
|
||||||
return lastmsgPerUser.stream()
|
return lastmsgPerUser.stream()
|
||||||
.anyMatch(lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(did));
|
.anyMatch(lmd -> ((PrivateChannel) lmd.channel)
|
||||||
|
.getRecipientIds().stream().anyMatch(u -> u.asString().equals(did)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void logoutAll() {
|
public static void logoutAll() {
|
||||||
for (val entry : MCChatUtils.ConnectedSenders.entrySet())
|
for (val entry : MCChatUtils.ConnectedSenders.entrySet())
|
||||||
for (val valueEntry : entry.getValue().entrySet())
|
for (val valueEntry : entry.getValue().entrySet())
|
||||||
if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey(), valueEntry.getValue().getUser()) == null) //If the player is online then the fake player was already logged out
|
if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey(), valueEntry.getValue().getUser()) == null) //If the player is online then the fake player was already logged out
|
||||||
MCChatUtils.callEventExcludingSome(new PlayerQuitEvent(valueEntry.getValue(), "")); //This is sync
|
MCChatUtils.callLogoutEvent(valueEntry.getValue(), false); //This is sync
|
||||||
MCChatUtils.ConnectedSenders.clear();
|
MCChatUtils.ConnectedSenders.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void callEventSync(Event event) {
|
|
||||||
Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> MCChatUtils.callEventExcludingSome(event));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package buttondevteam.discordplugin.mcchat;
|
package buttondevteam.discordplugin.mcchat;
|
||||||
|
|
||||||
import buttondevteam.core.ComponentManager;
|
import buttondevteam.core.ComponentManager;
|
||||||
import buttondevteam.core.component.channel.Channel;
|
|
||||||
import buttondevteam.discordplugin.*;
|
import buttondevteam.discordplugin.*;
|
||||||
import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule;
|
import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule;
|
||||||
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import discord4j.core.object.entity.*;
|
||||||
|
import discord4j.core.object.util.Snowflake;
|
||||||
import io.netty.util.collection.LongObjectHashMap;
|
import io.netty.util.collection.LongObjectHashMap;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.experimental.var;
|
import lombok.experimental.var;
|
||||||
|
@ -13,16 +16,20 @@ import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.plugin.AuthorNagException;
|
import org.bukkit.plugin.AuthorNagException;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.plugin.RegisteredListener;
|
import org.bukkit.plugin.RegisteredListener;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.obj.IMessage;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -34,23 +41,22 @@ public class MCChatUtils {
|
||||||
/**
|
/**
|
||||||
* May contain P<DiscordID> as key for public chat
|
* May contain P<DiscordID> as key for public chat
|
||||||
*/
|
*/
|
||||||
public static final HashMap<String, HashMap<IChannel, DiscordSender>> UnconnectedSenders = new HashMap<>();
|
public static final HashMap<String, HashMap<Snowflake, DiscordSender>> UnconnectedSenders = new HashMap<>();
|
||||||
public static final HashMap<String, HashMap<IChannel, DiscordConnectedPlayer>> ConnectedSenders = new HashMap<>();
|
public static final HashMap<String, HashMap<Snowflake, DiscordConnectedPlayer>> ConnectedSenders = new HashMap<>();
|
||||||
/**
|
/**
|
||||||
* May contain P<DiscordID> as key for public chat
|
* May contain P<DiscordID> as key for public chat
|
||||||
*/
|
*/
|
||||||
public static final HashMap<String, HashMap<IChannel, DiscordPlayerSender>> OnlineSenders = new HashMap<>();
|
public static final HashMap<String, HashMap<Snowflake, DiscordPlayerSender>> OnlineSenders = new HashMap<>();
|
||||||
static @Nullable LastMsgData lastmsgdata;
|
static @Nullable LastMsgData lastmsgdata;
|
||||||
static LongObjectHashMap<IMessage> lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks
|
static LongObjectHashMap<Message> lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks
|
||||||
private static MinecraftChatModule module;
|
private static MinecraftChatModule module;
|
||||||
|
private static HashMap<Class<? extends Event>, HashSet<String>> staticExcludedPlugins = new HashMap<>();
|
||||||
|
|
||||||
public static void updatePlayerList() {
|
public static void updatePlayerList() {
|
||||||
if (notEnabled()) return;
|
if (notEnabled()) return;
|
||||||
DPUtils.performNoWait(() -> {
|
if (lastmsgdata != null)
|
||||||
if (lastmsgdata != null)
|
updatePL(lastmsgdata);
|
||||||
updatePL(lastmsgdata);
|
MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL);
|
||||||
MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean notEnabled() {
|
private static boolean notEnabled() {
|
||||||
|
@ -64,55 +70,60 @@ public class MCChatUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void updatePL(LastMsgData lmd) {
|
private static void updatePL(LastMsgData lmd) {
|
||||||
String topic = lmd.channel.getTopic();
|
if (!(lmd.channel instanceof TextChannel)) {
|
||||||
if (topic == null || topic.length() == 0)
|
TBMCCoreAPI.SendException("Failed to update player list for channel " + lmd.channel.getId(),
|
||||||
|
new Exception("The channel isn't a (guild) text channel."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String topic = ((TextChannel) lmd.channel).getTopic().orElse("");
|
||||||
|
if (topic.length() == 0)
|
||||||
topic = ".\n----\nMinecraft chat\n----\n.";
|
topic = ".\n----\nMinecraft chat\n----\n.";
|
||||||
String[] s = topic.split("\\n----\\n");
|
String[] s = topic.split("\\n----\\n");
|
||||||
if (s.length < 3)
|
if (s.length < 3)
|
||||||
return;
|
return;
|
||||||
s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "")
|
s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "")
|
||||||
+ " online";
|
+ " online";
|
||||||
s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream()
|
s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream()
|
||||||
.map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", "));
|
.map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", "));
|
||||||
lmd.channel.changeTopic(String.join("\n----\n", s));
|
((TextChannel) lmd.channel).edit(tce -> tce.setTopic(String.join("\n----\n", s)).setReason("Player list update")).subscribe(); //Don't wait
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends DiscordSenderBase> T addSender(HashMap<String, HashMap<IChannel, T>> senders,
|
public static <T extends DiscordSenderBase> T addSender(HashMap<String, HashMap<Snowflake, T>> senders,
|
||||||
IUser user, T sender) {
|
User user, T sender) {
|
||||||
return addSender(senders, user.getStringID(), sender);
|
return addSender(senders, user.getId().asString(), sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends DiscordSenderBase> T addSender(HashMap<String, HashMap<IChannel, T>> senders,
|
public static <T extends DiscordSenderBase> T addSender(HashMap<String, HashMap<Snowflake, T>> senders,
|
||||||
String did, T sender) {
|
String did, T sender) {
|
||||||
var map = senders.get(did);
|
var map = senders.get(did);
|
||||||
if (map == null)
|
if (map == null)
|
||||||
map = new HashMap<>();
|
map = new HashMap<>();
|
||||||
map.put(sender.getChannel(), sender);
|
map.put(sender.getChannel().getId(), sender);
|
||||||
senders.put(did, map);
|
senders.put(did, map);
|
||||||
return sender;
|
return sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends DiscordSenderBase> T getSender(HashMap<String, HashMap<IChannel, T>> senders,
|
public static <T extends DiscordSenderBase> T getSender(HashMap<String, HashMap<Snowflake, T>> senders,
|
||||||
IChannel channel, IUser user) {
|
Snowflake channel, User user) {
|
||||||
var map = senders.get(user.getStringID());
|
var map = senders.get(user.getId().asString());
|
||||||
if (map != null)
|
if (map != null)
|
||||||
return map.get(channel);
|
return map.get(channel);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends DiscordSenderBase> T removeSender(HashMap<String, HashMap<IChannel, T>> senders,
|
public static <T extends DiscordSenderBase> T removeSender(HashMap<String, HashMap<Snowflake, T>> senders,
|
||||||
IChannel channel, IUser user) {
|
Snowflake channel, User user) {
|
||||||
var map = senders.get(user.getStringID());
|
var map = senders.get(user.getId().asString());
|
||||||
if (map != null)
|
if (map != null)
|
||||||
return map.remove(channel);
|
return map.remove(channel);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forAllMCChat(Consumer<IChannel> action) {
|
public static void forAllMCChat(Consumer<Mono<MessageChannel>> action) {
|
||||||
if (notEnabled()) return;
|
if (notEnabled()) return;
|
||||||
action.accept(module.chatChannel().get());
|
action.accept(module.chatChannelMono());
|
||||||
for (LastMsgData data : MCChatPrivate.lastmsgPerUser)
|
for (LastMsgData data : MCChatPrivate.lastmsgPerUser)
|
||||||
action.accept(data.channel);
|
action.accept(Mono.just(data.channel));
|
||||||
// lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat
|
// lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,11 +134,11 @@ public class MCChatUtils {
|
||||||
* @param toggle The toggle to check
|
* @param toggle The toggle to check
|
||||||
* @param hookmsg Whether the message is also sent from the hook
|
* @param hookmsg Whether the message is also sent from the hook
|
||||||
*/
|
*/
|
||||||
public static void forCustomAndAllMCChat(Consumer<IChannel> action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
|
public static void forCustomAndAllMCChat(Consumer<Mono<MessageChannel>> action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
|
||||||
if (notEnabled()) return;
|
if (notEnabled()) return;
|
||||||
if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg)
|
if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg)
|
||||||
forAllMCChat(action);
|
forAllMCChat(action);
|
||||||
final Consumer<MCChatCustom.CustomLMD> customLMDConsumer = cc -> action.accept(cc.channel);
|
final Consumer<MCChatCustom.CustomLMD> customLMDConsumer = cc -> action.accept(Mono.just(cc.channel));
|
||||||
if (toggle == null)
|
if (toggle == null)
|
||||||
MCChatCustom.lastmsgCustom.forEach(customLMDConsumer);
|
MCChatCustom.lastmsgCustom.forEach(customLMDConsumer);
|
||||||
else
|
else
|
||||||
|
@ -141,7 +152,7 @@ public class MCChatUtils {
|
||||||
* @param sender The sender to check perms of or null to send to all that has it toggled
|
* @param sender The sender to check perms of or null to send to all that has it toggled
|
||||||
* @param toggle The toggle to check or null to send to all allowed
|
* @param toggle The toggle to check or null to send to all allowed
|
||||||
*/
|
*/
|
||||||
public static void forAllowedCustomMCChat(Consumer<IChannel> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle) {
|
public static void forAllowedCustomMCChat(Consumer<Mono<MessageChannel>> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle) {
|
||||||
if (notEnabled()) return;
|
if (notEnabled()) return;
|
||||||
MCChatCustom.lastmsgCustom.stream().filter(clmd -> {
|
MCChatCustom.lastmsgCustom.stream().filter(clmd -> {
|
||||||
//new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple
|
//new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple
|
||||||
|
@ -150,7 +161,7 @@ public class MCChatUtils {
|
||||||
if (sender == null)
|
if (sender == null)
|
||||||
return true;
|
return true;
|
||||||
return clmd.groupID.equals(clmd.mcchannel.getGroupID(sender));
|
return clmd.groupID.equals(clmd.mcchannel.getGroupID(sender));
|
||||||
}).forEach(cc -> action.accept(cc.channel)); //TODO: Send error messages on channel connect
|
}).forEach(cc -> action.accept(Mono.just(cc.channel))); //TODO: Send error messages on channel connect
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -161,42 +172,42 @@ public class MCChatUtils {
|
||||||
* @param toggle The toggle to check or null to send to all allowed
|
* @param toggle The toggle to check or null to send to all allowed
|
||||||
* @param hookmsg Whether the message is also sent from the hook
|
* @param hookmsg Whether the message is also sent from the hook
|
||||||
*/
|
*/
|
||||||
public static void forAllowedCustomAndAllMCChat(Consumer<IChannel> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
|
public static void forAllowedCustomAndAllMCChat(Consumer<Mono<MessageChannel>> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
|
||||||
if (notEnabled()) return;
|
if (notEnabled()) return;
|
||||||
if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg)
|
if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg)
|
||||||
forAllMCChat(action);
|
forAllMCChat(action);
|
||||||
forAllowedCustomMCChat(action, sender, toggle);
|
forAllowedCustomMCChat(action, sender, toggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Consumer<IChannel> send(String message) {
|
public static Consumer<Mono<MessageChannel>> send(String message) {
|
||||||
return ch -> DiscordPlugin.sendMessageToChannel(ch, DPUtils.sanitizeString(message));
|
return ch -> ch.flatMap(mc -> mc.createMessage(DPUtils.sanitizeString(message))).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forAllowedMCChat(Consumer<IChannel> action, TBMCSystemChatEvent event) {
|
public static void forAllowedMCChat(Consumer<Mono<MessageChannel>> action, TBMCSystemChatEvent event) {
|
||||||
if (notEnabled()) return;
|
if (notEnabled()) return;
|
||||||
if (event.getChannel().isGlobal())
|
if (event.getChannel().isGlobal())
|
||||||
action.accept(module.chatChannel().get());
|
action.accept(module.chatChannelMono());
|
||||||
for (LastMsgData data : MCChatPrivate.lastmsgPerUser)
|
for (LastMsgData data : MCChatPrivate.lastmsgPerUser)
|
||||||
if (event.shouldSendTo(getSender(data.channel, data.user)))
|
if (event.shouldSendTo(getSender(data.channel.getId(), data.user)))
|
||||||
action.accept(data.channel);
|
action.accept(Mono.just(data.channel)); //TODO: Only store ID?
|
||||||
MCChatCustom.lastmsgCustom.stream().filter(clmd -> {
|
MCChatCustom.lastmsgCustom.stream().filter(clmd -> {
|
||||||
if (!clmd.brtoggles.contains(event.getTarget()))
|
if (!clmd.brtoggles.contains(event.getTarget()))
|
||||||
return false;
|
return false;
|
||||||
return event.shouldSendTo(clmd.dcp);
|
return event.shouldSendTo(clmd.dcp);
|
||||||
}).map(clmd -> clmd.channel).forEach(action);
|
}).map(clmd -> Mono.just(clmd.channel)).forEach(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method will find the best sender to use: if the player is online, use that, if not but connected then use that etc.
|
* This method will find the best sender to use: if the player is online, use that, if not but connected then use that etc.
|
||||||
*/
|
*/
|
||||||
static DiscordSenderBase getSender(IChannel channel, final IUser author) {
|
static DiscordSenderBase getSender(Snowflake channel, final User author) {
|
||||||
//noinspection OptionalGetWithoutIsPresent
|
//noinspection OptionalGetWithoutIsPresent
|
||||||
return Stream.<Supplier<Optional<DiscordSenderBase>>>of( // https://stackoverflow.com/a/28833677/2703239
|
return Stream.<Supplier<Optional<DiscordSenderBase>>>of( // https://stackoverflow.com/a/28833677/2703239
|
||||||
() -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null
|
() -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null
|
||||||
() -> Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it
|
() -> Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it
|
||||||
() -> Optional.ofNullable(getSender(UnconnectedSenders, channel, author)), //
|
() -> Optional.ofNullable(getSender(UnconnectedSenders, channel, author)), //
|
||||||
() -> Optional.of(addSender(UnconnectedSenders, author,
|
() -> Optional.of(addSender(UnconnectedSenders, author,
|
||||||
new DiscordSender(author, channel)))).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get();
|
new DiscordSender(author, (MessageChannel) DiscordPlugin.dc.getChannelById(channel).block())))).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -205,15 +216,15 @@ public class MCChatUtils {
|
||||||
*
|
*
|
||||||
* @param channel The channel to reset in - the process is slightly different for the public, private and custom chats
|
* @param channel The channel to reset in - the process is slightly different for the public, private and custom chats
|
||||||
*/
|
*/
|
||||||
public static void resetLastMessage(IChannel channel) {
|
public static void resetLastMessage(Channel channel) {
|
||||||
if (notEnabled()) return;
|
if (notEnabled()) return;
|
||||||
if (channel.getLongID() == module.chatChannel().get().getLongID()) {
|
if (channel.getId().asLong() == module.chatChannel().get().asLong()) {
|
||||||
(lastmsgdata == null ? lastmsgdata = new LastMsgData(module.chatChannel().get(), null)
|
(lastmsgdata == null ? lastmsgdata = new LastMsgData(module.chatChannelMono().block(), null)
|
||||||
: lastmsgdata).message = null;
|
: lastmsgdata).message = null;
|
||||||
return;
|
return;
|
||||||
} // Don't set the whole object to null, the player and channel information should be preserved
|
} // Don't set the whole object to null, the player and channel information should be preserved
|
||||||
for (LastMsgData data : channel.isPrivate() ? MCChatPrivate.lastmsgPerUser : MCChatCustom.lastmsgCustom) {
|
for (LastMsgData data : channel instanceof PrivateChannel ? MCChatPrivate.lastmsgPerUser : MCChatCustom.lastmsgCustom) {
|
||||||
if (data.channel.getLongID() == channel.getLongID()) {
|
if (data.channel.getId().asLong() == channel.getId().asLong()) {
|
||||||
data.message = null;
|
data.message = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -221,9 +232,23 @@ public class MCChatUtils {
|
||||||
//If it gets here, it's sending a message to a non-chat channel
|
//If it gets here, it's sending a message to a non-chat channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addStaticExcludedPlugin(Class<? extends Event> event, String plugin) {
|
||||||
|
staticExcludedPlugins.compute(event, (e, hs) -> hs == null
|
||||||
|
? Sets.newHashSet(plugin)
|
||||||
|
: (hs.add(plugin) ? hs : hs));
|
||||||
|
}
|
||||||
|
|
||||||
public static void callEventExcludingSome(Event event) {
|
public static void callEventExcludingSome(Event event) {
|
||||||
if (notEnabled()) return;
|
if (notEnabled()) return;
|
||||||
callEventExcluding(event, false, module.excludedPlugins().get());
|
val second = staticExcludedPlugins.get(event.getClass());
|
||||||
|
String[] first = module.excludedPlugins().get();
|
||||||
|
String[] both = second == null ? first
|
||||||
|
: Arrays.copyOf(first, first.length + second.size());
|
||||||
|
int i = first.length;
|
||||||
|
if (second != null)
|
||||||
|
for (String plugin : second)
|
||||||
|
both[i++] = plugin;
|
||||||
|
callEventExcluding(event, false, both);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -284,13 +309,59 @@ public class MCChatUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call it from an async thread.
|
||||||
|
*/
|
||||||
|
public static void callLoginEvents(DiscordConnectedPlayer dcp) {
|
||||||
|
Consumer<Supplier<String>> loginFail = kickMsg -> {
|
||||||
|
dcp.sendMessage("Minecraft chat disabled, as the login failed: " + kickMsg.get());
|
||||||
|
MCChatPrivate.privateMCChat(dcp.getChannel(), false, dcp.getUser(), dcp.getChromaUser());
|
||||||
|
}; //Probably also happens if the user is banned or so
|
||||||
|
val event = new AsyncPlayerPreLoginEvent(dcp.getName(), InetAddress.getLoopbackAddress(), dcp.getUniqueId());
|
||||||
|
callEventExcludingSome(event);
|
||||||
|
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
|
||||||
|
loginFail.accept(event::getKickMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> {
|
||||||
|
val ev = new PlayerLoginEvent(dcp, "localhost", InetAddress.getLoopbackAddress());
|
||||||
|
callEventExcludingSome(ev);
|
||||||
|
if (ev.getResult() != PlayerLoginEvent.Result.ALLOWED) {
|
||||||
|
loginFail.accept(ev::getKickMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callEventExcludingSome(new PlayerJoinEvent(dcp, ""));
|
||||||
|
dcp.setLoggedIn(true);
|
||||||
|
DPUtils.getLogger().info(dcp.getName() + " (" + dcp.getUniqueId() + ") logged in from Discord");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only calls the events if the player is actually logged in
|
||||||
|
*
|
||||||
|
* @param dcp The player
|
||||||
|
* @param needsSync Whether we're in an async thread
|
||||||
|
*/
|
||||||
|
public static void callLogoutEvent(DiscordConnectedPlayer dcp, boolean needsSync) {
|
||||||
|
if (!dcp.isLoggedIn()) return;
|
||||||
|
val event = new PlayerQuitEvent(dcp, "");
|
||||||
|
if (needsSync) callEventSync(event);
|
||||||
|
else callEventExcludingSome(event);
|
||||||
|
dcp.setLoggedIn(false);
|
||||||
|
DPUtils.getLogger().info(dcp.getName() + " (" + dcp.getUniqueId() + ") logged out from Discord");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void callEventSync(Event event) {
|
||||||
|
Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> callEventExcludingSome(event));
|
||||||
|
}
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public static class LastMsgData {
|
public static class LastMsgData {
|
||||||
public IMessage message;
|
public Message message;
|
||||||
public long time;
|
public long time;
|
||||||
public String content;
|
public String content;
|
||||||
public final IChannel channel;
|
public final MessageChannel channel;
|
||||||
public Channel mcchannel;
|
public buttondevteam.core.component.channel.Channel mcchannel;
|
||||||
public final IUser user;
|
public final User user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package buttondevteam.discordplugin.mcchat;
|
package buttondevteam.discordplugin.mcchat;
|
||||||
|
|
||||||
import buttondevteam.discordplugin.*;
|
import buttondevteam.discordplugin.*;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
import buttondevteam.lib.player.*;
|
import buttondevteam.lib.player.*;
|
||||||
import com.earth2me.essentials.CommandSource;
|
import com.earth2me.essentials.CommandSource;
|
||||||
|
import discord4j.core.object.entity.Role;
|
||||||
|
import discord4j.core.object.util.Snowflake;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import net.ess3.api.events.AfkStatusChangeEvent;
|
import net.ess3.api.events.AfkStatusChangeEvent;
|
||||||
|
@ -17,16 +18,14 @@ import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
|
||||||
import org.bukkit.event.player.PlayerKickEvent;
|
import org.bukkit.event.player.PlayerKickEvent;
|
||||||
import org.bukkit.event.player.PlayerLoginEvent;
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
|
||||||
import org.bukkit.event.server.BroadcastMessageEvent;
|
import org.bukkit.event.server.BroadcastMessageEvent;
|
||||||
import sx.blah.discord.handle.obj.IRole;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
import sx.blah.discord.util.DiscordException;
|
import java.util.Objects;
|
||||||
import sx.blah.discord.util.MissingPermissionsException;
|
import java.util.Optional;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
class MCListener implements Listener {
|
class MCListener implements Listener {
|
||||||
|
@ -36,9 +35,11 @@ class MCListener implements Listener {
|
||||||
public void onPlayerLogin(PlayerLoginEvent e) {
|
public void onPlayerLogin(PlayerLoginEvent e) {
|
||||||
if (e.getResult() != Result.ALLOWED)
|
if (e.getResult() != Result.ALLOWED)
|
||||||
return;
|
return;
|
||||||
|
if (e.getPlayer() instanceof DiscordConnectedPlayer)
|
||||||
|
return;
|
||||||
MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) //Only private mcchat should be in ConnectedSenders
|
MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) //Only private mcchat should be in ConnectedSenders
|
||||||
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
|
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
|
||||||
.ifPresent(dcp -> MCChatUtils.callEventExcludingSome(new PlayerQuitEvent(dcp, "")));
|
.ifPresent(dcp -> MCChatUtils.callLogoutEvent(dcp, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
@ -49,11 +50,11 @@ class MCListener implements Listener {
|
||||||
final Player p = e.getPlayer();
|
final Player p = e.getPlayer();
|
||||||
DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class);
|
DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class);
|
||||||
if (dp != null) {
|
if (dp != null) {
|
||||||
val user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()));
|
val user = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).block();
|
||||||
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
|
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
|
||||||
new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p));
|
new DiscordPlayerSender(user, Objects.requireNonNull(user).getPrivateChannel().block(), p)); //TODO: Don't block
|
||||||
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
|
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
|
||||||
new DiscordPlayerSender(user, module.chatChannel().get(), p)); //Stored per-channel
|
new DiscordPlayerSender(user, module.chatChannelMono().block(), p)); //Stored per-channel
|
||||||
}
|
}
|
||||||
final String message = e.GetPlayer().PlayerName().get() + " joined the game";
|
final String message = e.GetPlayer().PlayerName().get() + " joined the game";
|
||||||
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true);
|
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true);
|
||||||
|
@ -67,10 +68,10 @@ class MCListener implements Listener {
|
||||||
return; // Only care about real users
|
return; // Only care about real users
|
||||||
MCChatUtils.OnlineSenders.entrySet()
|
MCChatUtils.OnlineSenders.entrySet()
|
||||||
.removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())));
|
.removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())));
|
||||||
Bukkit.getScheduler().runTask(DiscordPlugin.plugin,
|
Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin,
|
||||||
() -> MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream())
|
() -> MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream())
|
||||||
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
|
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
|
||||||
.ifPresent(dcp -> MCChatUtils.callEventExcludingSome(new PlayerJoinEvent(dcp, ""))));
|
.ifPresent(MCChatUtils::callLoginEvents));
|
||||||
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin,
|
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin,
|
||||||
ChromaBot.getInstance()::updatePlayerList, 5);
|
ChromaBot.getInstance()::updatePlayerList, 5);
|
||||||
final String message = e.GetPlayer().PlayerName().get() + " left the game";
|
final String message = e.GetPlayer().PlayerName().get() + " left the game";
|
||||||
|
@ -99,38 +100,34 @@ class MCListener implements Listener {
|
||||||
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(msg), base, ChannelconBroadcast.AFK, false);
|
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(msg), base, ChannelconBroadcast.AFK, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigData<IRole> muteRole() {
|
private ConfigData<Mono<Role>> muteRole() {
|
||||||
return DPUtils.roleData(module.getConfig(), "muteRole", "Muted");
|
return DPUtils.roleData(module.getConfig(), "muteRole", "Muted");
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerMute(MuteStatusChangeEvent e) {
|
public void onPlayerMute(MuteStatusChangeEvent e) {
|
||||||
try {
|
final Mono<Role> role = muteRole().get();
|
||||||
DPUtils.performNoWait(() -> {
|
if (role == null) return;
|
||||||
final IRole role = muteRole().get();
|
final CommandSource source = e.getAffected().getSource();
|
||||||
if (role == null) return;
|
if (!source.isPlayer())
|
||||||
final CommandSource source = e.getAffected().getSource();
|
return;
|
||||||
if (!source.isPlayer())
|
final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class)
|
||||||
return;
|
.getAs(DiscordPlayer.class);
|
||||||
final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class)
|
if (p == null) return;
|
||||||
.getAs(DiscordPlayer.class);
|
DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID()))
|
||||||
if (p == null) return;
|
.flatMap(user -> user.asMember(DiscordPlugin.mainServer.getId()))
|
||||||
final IUser user = DiscordPlugin.dc.getUserByID(
|
.flatMap(user -> role.flatMap(r -> {
|
||||||
Long.parseLong(p.getDiscordID()));
|
|
||||||
if (e.getValue())
|
if (e.getValue())
|
||||||
user.addRole(role);
|
user.addRole(r.getId());
|
||||||
else
|
else
|
||||||
user.removeRole(role);
|
user.removeRole(r.getId());
|
||||||
val modlog = module.modlogChannel().get();
|
val modlog = module.modlogChannel().get();
|
||||||
String msg = (e.getValue() ? "M" : "Unm") + "uted user: " + user.getName();
|
String msg = (e.getValue() ? "M" : "Unm") + "uted user: " + user.getUsername() + "#" + user.getDiscriminator();
|
||||||
if (modlog != null)
|
|
||||||
DiscordPlugin.sendMessageToChannel(modlog, msg);
|
|
||||||
DPUtils.getLogger().info(msg);
|
DPUtils.getLogger().info(msg);
|
||||||
});
|
if (modlog != null)
|
||||||
} catch (DiscordException | MissingPermissionsException ex) {
|
return modlog.flatMap(ch -> ch.createMessage(msg));
|
||||||
TBMCCoreAPI.SendException("Failed to give/take Muted role to player " + e.getAffected().getName() + "!",
|
return Mono.empty();
|
||||||
ex);
|
})).subscribe();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
@ -148,8 +145,9 @@ class MCListener implements Listener {
|
||||||
String name = event.getSender() instanceof Player ? ((Player) event.getSender()).getDisplayName()
|
String name = event.getSender() instanceof Player ? ((Player) event.getSender()).getDisplayName()
|
||||||
: event.getSender().getName();
|
: event.getSender().getName();
|
||||||
//Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO
|
//Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO
|
||||||
val yeehaw = DiscordPlugin.mainServer.getEmojiByName("YEEHAW");
|
DiscordPlugin.mainServer.getEmojis().filter(e -> "YEEHAW".equals(e.getName()))
|
||||||
MCChatUtils.forAllMCChat(MCChatUtils.send(name + (yeehaw != null ? " <:YEEHAW:" + yeehaw.getStringID() + ">s" : " YEEHAWs")));
|
.take(1).singleOrEmpty().map(Optional::of).defaultIfEmpty(Optional.empty()).subscribe(yeehaw ->
|
||||||
|
MCChatUtils.forAllMCChat(MCChatUtils.send(name + (yeehaw.map(guildEmoji -> " <:YEEHAW:" + guildEmoji.getId().asString() + ">s").orElse(" YEEHAWs")))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
package buttondevteam.discordplugin.mcchat;
|
package buttondevteam.discordplugin.mcchat;
|
||||||
|
|
||||||
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.core.component.channel.Channel;
|
import buttondevteam.core.component.channel.Channel;
|
||||||
import buttondevteam.discordplugin.DPUtils;
|
import buttondevteam.discordplugin.DPUtils;
|
||||||
import buttondevteam.discordplugin.DiscordConnectedPlayer;
|
import buttondevteam.discordplugin.DiscordConnectedPlayer;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
|
import buttondevteam.discordplugin.playerfaker.perm.LPInjector;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
|
import buttondevteam.lib.architecture.ReadOnlyConfigData;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.util.Snowflake;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -23,11 +28,12 @@ import java.util.stream.Collectors;
|
||||||
* Provides Minecraft chat connection to Discord. Commands may be used either in a public chat (limited) or in a DM.
|
* Provides Minecraft chat connection to Discord. Commands may be used either in a public chat (limited) or in a DM.
|
||||||
*/
|
*/
|
||||||
public class MinecraftChatModule extends Component<DiscordPlugin> {
|
public class MinecraftChatModule extends Component<DiscordPlugin> {
|
||||||
private @Getter MCChatListener listener;
|
private @Getter
|
||||||
|
MCChatListener listener;
|
||||||
|
|
||||||
public MCChatListener getListener() { //It doesn't want to generate
|
/*public MCChatListener getListener() { //It doesn't want to generate
|
||||||
return listener;
|
return listener; - And now ButtonProcessor didn't look beyond this - return instead of continue...
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command!
|
* A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command!
|
||||||
|
@ -40,33 +46,46 @@ public class MinecraftChatModule extends Component<DiscordPlugin> {
|
||||||
/**
|
/**
|
||||||
* The channel to use as the public Minecraft chat - everything public gets broadcasted here
|
* The channel to use as the public Minecraft chat - everything public gets broadcasted here
|
||||||
*/
|
*/
|
||||||
public ConfigData<IChannel> chatChannel() {
|
public ConfigData<Snowflake> chatChannel() {
|
||||||
return DPUtils.channelData(getConfig(), "chatChannel", 239519012529111040L);
|
return DPUtils.snowflakeData(getConfig(), "chatChannel", 239519012529111040L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<MessageChannel> chatChannelMono() {
|
||||||
|
return DPUtils.getMessageChannel(chatChannel().getPath(), chatChannel().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The channel where the plugin can log when it mutes a player on Discord because of a Minecraft mute
|
* The channel where the plugin can log when it mutes a player on Discord because of a Minecraft mute
|
||||||
*/
|
*/
|
||||||
public ConfigData<IChannel> modlogChannel() {
|
public ReadOnlyConfigData<Mono<MessageChannel>> modlogChannel() {
|
||||||
return DPUtils.channelData(getConfig(), "modlogChannel", 283840717275791360L);
|
return DPUtils.channelData(getConfig(), "modlogChannel", 283840717275791360L);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 0 * The plugins to exclude from fake player events used for the 'mcchat' command - some plugins may crash, add them here
|
* The plugins to exclude from fake player events used for the 'mcchat' command - some plugins may crash, add them here
|
||||||
*/
|
*/
|
||||||
public ConfigData<String[]> excludedPlugins() {
|
public ConfigData<String[]> excludedPlugins() {
|
||||||
return getConfig().getData("excludedPlugins", new String[]{"ProtocolLib", "LibsDisguises", "JourneyMapServer"});
|
return getConfig().getData("excludedPlugins", new String[]{"ProtocolLib", "LibsDisguises", "JourneyMapServer"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this setting is on then players logged in through the 'mcchat' command will be able to teleport using plugin commands.
|
||||||
|
* They can then use commands like /tpahere to teleport others to that place.<br />
|
||||||
|
* If this is off, then teleporting will have no effect.
|
||||||
|
*/
|
||||||
|
public ConfigData<Boolean> allowFakePlayerTeleports() {
|
||||||
|
return getConfig().getData("allowFakePlayerTeleports", false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void enable() {
|
protected void enable() {
|
||||||
if (DPUtils.disableIfConfigError(this, chatChannel())) return;
|
if (DPUtils.disableIfConfigErrorRes(this, chatChannel(), chatChannelMono()))
|
||||||
|
return;
|
||||||
listener = new MCChatListener(this);
|
listener = new MCChatListener(this);
|
||||||
DiscordPlugin.dc.getDispatcher().registerListener(listener);
|
|
||||||
TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin());
|
TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin());
|
||||||
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(this), getPlugin());//These get undone if restarting/resetting - it will ignore events if disabled
|
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(this), getPlugin());//These get undone if restarting/resetting - it will ignore events if disabled
|
||||||
getPlugin().getManager().registerCommand(new MCChatCommand());
|
getPlugin().getManager().registerCommand(new MCChatCommand());
|
||||||
getPlugin().getManager().registerCommand(new ChannelconCommand());
|
getPlugin().getManager().registerCommand(new ChannelconCommand(this));
|
||||||
|
|
||||||
val chcons = getConfig().getConfig().getConfigurationSection("chcons");
|
val chcons = getConfig().getConfig().getConfigurationSection("chcons");
|
||||||
if (chcons == null) //Fallback to old place
|
if (chcons == null) //Fallback to old place
|
||||||
|
@ -76,20 +95,28 @@ public class MinecraftChatModule extends Component<DiscordPlugin> {
|
||||||
for (val chconkey : chconkeys) {
|
for (val chconkey : chconkeys) {
|
||||||
val chcon = chcons.getConfigurationSection(chconkey);
|
val chcon = chcons.getConfigurationSection(chconkey);
|
||||||
val mcch = Channel.getChannels().filter(ch -> ch.ID.equals(chcon.getString("mcchid"))).findAny();
|
val mcch = Channel.getChannels().filter(ch -> ch.ID.equals(chcon.getString("mcchid"))).findAny();
|
||||||
val ch = DiscordPlugin.dc.getChannelByID(chcon.getLong("chid"));
|
val ch = DiscordPlugin.dc.getChannelById(Snowflake.of(chcon.getLong("chid"))).block();
|
||||||
val did = chcon.getLong("did");
|
val did = chcon.getLong("did");
|
||||||
val user = DiscordPlugin.dc.fetchUser(did);
|
val user = DiscordPlugin.dc.getUserById(Snowflake.of(did)).block();
|
||||||
val groupid = chcon.getString("groupid");
|
val groupid = chcon.getString("groupid");
|
||||||
val toggles = chcon.getInt("toggles");
|
val toggles = chcon.getInt("toggles");
|
||||||
val brtoggles = chcon.getStringList("brtoggles");
|
val brtoggles = chcon.getStringList("brtoggles");
|
||||||
if (!mcch.isPresent() || ch == null || user == null || groupid == null)
|
if (!mcch.isPresent() || ch == null || user == null || groupid == null)
|
||||||
continue;
|
continue;
|
||||||
Bukkit.getScheduler().runTask(getPlugin(), () -> { //<-- Needed because of occasional ConcurrentModificationExceptions when creating the player (PermissibleBase)
|
Bukkit.getScheduler().runTask(getPlugin(), () -> { //<-- Needed because of occasional ConcurrentModificationExceptions when creating the player (PermissibleBase)
|
||||||
val dcp = new DiscordConnectedPlayer(user, ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"));
|
val dcp = new DiscordConnectedPlayer(user, (MessageChannel) ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"), this);
|
||||||
MCChatCustom.addCustomChat(ch, groupid, mcch.get(), user, dcp, toggles, brtoggles.stream().map(TBMCSystemChatEvent.BroadcastTarget::get).filter(Objects::nonNull).collect(Collectors.toSet()));
|
MCChatCustom.addCustomChat((MessageChannel) ch, groupid, mcch.get(), user, dcp, toggles, brtoggles.stream().map(TBMCSystemChatEvent.BroadcastTarget::get).filter(Objects::nonNull).collect(Collectors.toSet()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new LPInjector(MainPlugin.Instance);
|
||||||
|
} catch (Exception e) {
|
||||||
|
TBMCCoreAPI.SendException("Failed to init LuckPerms injector", e);
|
||||||
|
} catch (NoClassDefFoundError e) {
|
||||||
|
getPlugin().getLogger().info("No LuckPerms, not injecting");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -97,10 +124,10 @@ public class MinecraftChatModule extends Component<DiscordPlugin> {
|
||||||
val chcons = MCChatCustom.getCustomChats();
|
val chcons = MCChatCustom.getCustomChats();
|
||||||
val chconsc = getConfig().getConfig().createSection("chcons");
|
val chconsc = getConfig().getConfig().createSection("chcons");
|
||||||
for (val chcon : chcons) {
|
for (val chcon : chcons) {
|
||||||
val chconc = chconsc.createSection(chcon.channel.getStringID());
|
val chconc = chconsc.createSection(chcon.channel.getId().asString());
|
||||||
chconc.set("mcchid", chcon.mcchannel.ID);
|
chconc.set("mcchid", chcon.mcchannel.ID);
|
||||||
chconc.set("chid", chcon.channel.getLongID());
|
chconc.set("chid", chcon.channel.getId().asLong());
|
||||||
chconc.set("did", chcon.user.getLongID());
|
chconc.set("did", chcon.user.getId().asLong());
|
||||||
chconc.set("mcuid", chcon.dcp.getUniqueId().toString());
|
chconc.set("mcuid", chcon.dcp.getUniqueId().toString());
|
||||||
chconc.set("mcname", chcon.dcp.getName());
|
chconc.set("mcname", chcon.dcp.getName());
|
||||||
chconc.set("groupid", chcon.groupID);
|
chconc.set("groupid", chcon.groupID);
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
package buttondevteam.discordplugin.mccommands;
|
|
||||||
|
|
||||||
import buttondevteam.discordplugin.DPUtils;
|
|
||||||
import buttondevteam.discordplugin.DiscordPlayer;
|
|
||||||
import buttondevteam.discordplugin.commands.ConnectCommand;
|
|
||||||
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
|
||||||
import buttondevteam.lib.player.ChromaGamerBase;
|
|
||||||
import buttondevteam.lib.player.TBMCPlayer;
|
|
||||||
import buttondevteam.lib.player.TBMCPlayerBase;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
@CommandClass(modOnly = false, path = "accept")
|
|
||||||
public class AcceptMCCommand extends DiscordMCCommandBase {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] GetHelpText(String alias) {
|
|
||||||
return new String[] { //
|
|
||||||
"§6---- Accept Discord connection ----", //
|
|
||||||
"Accept a pending connection between your Discord and Minecraft account.", //
|
|
||||||
"To start the connection process, do §b/connect <MCname>§r in the " + DPUtils.botmention() + " channel on Discord", //
|
|
||||||
"Usage: /" + alias + " accept" //
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean OnCommand(Player player, String alias, String[] args) {
|
|
||||||
String did = ConnectCommand.WaitingToConnect.get(player.getName());
|
|
||||||
if (did == null) {
|
|
||||||
player.sendMessage("§cYou don't have a pending connection to Discord.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
DiscordPlayer dp = ChromaGamerBase.getUser(did, DiscordPlayer.class);
|
|
||||||
TBMCPlayer mcp = TBMCPlayerBase.getPlayer(player.getUniqueId(), TBMCPlayer.class);
|
|
||||||
dp.connectWith(mcp);
|
|
||||||
dp.save();
|
|
||||||
mcp.save();
|
|
||||||
ConnectCommand.WaitingToConnect.remove(player.getName());
|
|
||||||
MCChatUtils.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed
|
|
||||||
player.sendMessage("§bAccounts connected.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package buttondevteam.discordplugin.mccommands;
|
|
||||||
|
|
||||||
import buttondevteam.discordplugin.DPUtils;
|
|
||||||
import buttondevteam.discordplugin.commands.ConnectCommand;
|
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
@CommandClass(modOnly = false, path = "decline")
|
|
||||||
public class DeclineMCCommand extends DiscordMCCommandBase {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] GetHelpText(String alias) {
|
|
||||||
return new String[] { //
|
|
||||||
"§6---- Decline Discord connection ----", //
|
|
||||||
"Decline a pending connection between your Discord and Minecraft account.", //
|
|
||||||
"To start the connection process, do §b/connect <MCname>§r in the " + DPUtils.botmention() + " channel on Discord", //
|
|
||||||
"Usage: /" + alias + " decline" //
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean OnCommand(Player player, String alias, String[] args) {
|
|
||||||
String did = ConnectCommand.WaitingToConnect.remove(player.getName());
|
|
||||||
if (did == null) {
|
|
||||||
player.sendMessage("§cYou don't have a pending connection to Discord.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
player.sendMessage("§bPending connection declined.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
package buttondevteam.discordplugin.mccommands;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.DPUtils;
|
||||||
|
import buttondevteam.discordplugin.DiscordPlayer;
|
||||||
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
|
import buttondevteam.discordplugin.DiscordSenderBase;
|
||||||
|
import buttondevteam.discordplugin.commands.ConnectCommand;
|
||||||
|
import buttondevteam.discordplugin.commands.VersionCommand;
|
||||||
|
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
||||||
|
import buttondevteam.lib.chat.Command2;
|
||||||
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
|
import buttondevteam.lib.chat.ICommand2MC;
|
||||||
|
import buttondevteam.lib.player.ChromaGamerBase;
|
||||||
|
import buttondevteam.lib.player.TBMCPlayer;
|
||||||
|
import buttondevteam.lib.player.TBMCPlayerBase;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
@CommandClass(path = "discord", helpText = {
|
||||||
|
"Discord",
|
||||||
|
"This command allows performing Discord-related actions."
|
||||||
|
})
|
||||||
|
public class DiscordMCCommand extends ICommand2MC {
|
||||||
|
@Command2.Subcommand
|
||||||
|
public boolean accept(Player player) {
|
||||||
|
String did = ConnectCommand.WaitingToConnect.get(player.getName());
|
||||||
|
if (did == null) {
|
||||||
|
player.sendMessage("§cYou don't have a pending connection to Discord.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
DiscordPlayer dp = ChromaGamerBase.getUser(did, DiscordPlayer.class);
|
||||||
|
TBMCPlayer mcp = TBMCPlayerBase.getPlayer(player.getUniqueId(), TBMCPlayer.class);
|
||||||
|
dp.connectWith(mcp);
|
||||||
|
dp.save();
|
||||||
|
mcp.save();
|
||||||
|
ConnectCommand.WaitingToConnect.remove(player.getName());
|
||||||
|
MCChatUtils.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed
|
||||||
|
player.sendMessage("§bAccounts connected.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command2.Subcommand
|
||||||
|
public boolean decline(Player player) {
|
||||||
|
String did = ConnectCommand.WaitingToConnect.remove(player.getName());
|
||||||
|
if (did == null) {
|
||||||
|
player.sendMessage("§cYou don't have a pending connection to Discord.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
player.sendMessage("§bPending connection declined.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = {
|
||||||
|
"Reload Discord plugin",
|
||||||
|
"Reloads the config. To apply some changes, you may need to also run /discord reset."
|
||||||
|
})
|
||||||
|
public void reload(CommandSender sender) {
|
||||||
|
if (DiscordPlugin.plugin.tryReloadConfig())
|
||||||
|
sender.sendMessage("§bConfig reloaded.");
|
||||||
|
else
|
||||||
|
sender.sendMessage("§cFailed to reload config.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean resetting = false;
|
||||||
|
|
||||||
|
@Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = {
|
||||||
|
"Reset ChromaBot", //
|
||||||
|
"This command disables and then enables the plugin." //
|
||||||
|
})
|
||||||
|
public void reset(CommandSender sender) {
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> {
|
||||||
|
resetting = true; //Turned off after sending enable message (ReadyEvent)
|
||||||
|
sender.sendMessage("§bDisabling DiscordPlugin...");
|
||||||
|
Bukkit.getPluginManager().disablePlugin(DiscordPlugin.plugin);
|
||||||
|
if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
|
||||||
|
sender.sendMessage("§bEnabling DiscordPlugin...");
|
||||||
|
Bukkit.getPluginManager().enablePlugin(DiscordPlugin.plugin);
|
||||||
|
if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
|
||||||
|
sender.sendMessage("§bReset finished!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command2.Subcommand(helpText = {
|
||||||
|
"Version command",
|
||||||
|
"Prints the plugin version"
|
||||||
|
})
|
||||||
|
public void version(CommandSender sender) {
|
||||||
|
sender.sendMessage(VersionCommand.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command2.Subcommand(helpText = {
|
||||||
|
"Invite",
|
||||||
|
"Shows an invite link to the server"
|
||||||
|
})
|
||||||
|
public void invite(CommandSender sender) {
|
||||||
|
String invi = DiscordPlugin.plugin.inviteLink().get();
|
||||||
|
if (invi.length() > 0) {
|
||||||
|
sender.sendMessage("§bInvite link: " + invi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DiscordPlugin.mainServer.getInvites().limitRequest(1)
|
||||||
|
.switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("§cNo invites found for the server.")))
|
||||||
|
.subscribe(inv -> {
|
||||||
|
sender.sendMessage("§bInvite link: https://discord.gg/" + inv.getCode());
|
||||||
|
}, e -> sender.sendMessage("§cThe invite link is not set and the bot has no permission to get it."));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getHelpText(Method method, Command2.Subcommand ann) {
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "accept":
|
||||||
|
return new String[]{ //
|
||||||
|
"Accept Discord connection", //
|
||||||
|
"Accept a pending connection between your Discord and Minecraft account.", //
|
||||||
|
"To start the connection process, do §b/connect <MCname>§r in the " + DPUtils.botmention() + " channel on Discord", //
|
||||||
|
};
|
||||||
|
case "decline":
|
||||||
|
return new String[]{ //
|
||||||
|
"Decline Discord connection", //
|
||||||
|
"Decline a pending connection between your Discord and Minecraft account.", //
|
||||||
|
"To start the connection process, do §b/connect <MCname>§r in the " + DPUtils.botmention() + " channel on Discord", //
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return super.getHelpText(method, ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
package buttondevteam.discordplugin.mccommands;
|
|
||||||
|
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
|
||||||
import buttondevteam.lib.chat.PlayerCommandBase;
|
|
||||||
|
|
||||||
@CommandClass(modOnly = false, path = "discord")
|
|
||||||
public abstract class DiscordMCCommandBase extends PlayerCommandBase {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package buttondevteam.discordplugin.mccommands;
|
|
||||||
|
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
|
||||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
|
|
||||||
@CommandClass(path = "discord reload")
|
|
||||||
public class ReloadMCCommand extends TBMCCommandBase {
|
|
||||||
@Override
|
|
||||||
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
|
|
||||||
if (DiscordPlugin.plugin.tryReloadConfig())
|
|
||||||
sender.sendMessage("§bConfig reloaded."); //TODO: Convert to new command system
|
|
||||||
else
|
|
||||||
sender.sendMessage("§cFailed to reload config.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] GetHelpText(String alias) {
|
|
||||||
return new String[]{
|
|
||||||
"Reload",
|
|
||||||
"Reloads the config. To apply some changes, you may need to also run /discord reset."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package buttondevteam.discordplugin.mccommands;
|
|
||||||
|
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
|
||||||
import buttondevteam.discordplugin.DiscordSenderBase;
|
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
|
||||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
|
|
||||||
@CommandClass(path = "discord reset", modOnly = true)
|
|
||||||
public class ResetMCCommand extends TBMCCommandBase { //Not player-only, so not using DiscordMCCommandBase
|
|
||||||
public static boolean resetting = false;
|
|
||||||
@Override
|
|
||||||
public boolean OnCommand(CommandSender sender, String s, String[] strings) {
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> {
|
|
||||||
resetting = true; //Turned off after sending enable message (ReadyEvent)
|
|
||||||
sender.sendMessage("§bDisabling DiscordPlugin...");
|
|
||||||
Bukkit.getPluginManager().disablePlugin(DiscordPlugin.plugin);
|
|
||||||
if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
|
|
||||||
sender.sendMessage("§bEnabling DiscordPlugin...");
|
|
||||||
Bukkit.getPluginManager().enablePlugin(DiscordPlugin.plugin);
|
|
||||||
if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
|
|
||||||
sender.sendMessage("§bReset finished!");
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] GetHelpText(String s) {
|
|
||||||
return new String[]{ //
|
|
||||||
"§6---- Reset ChromaBot ----", //
|
|
||||||
"This command disables and then enables the plugin." //
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package buttondevteam.discordplugin.mccommands;
|
|
||||||
|
|
||||||
import buttondevteam.discordplugin.commands.VersionCommand;
|
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
|
||||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
|
|
||||||
@CommandClass(path = "discord version")
|
|
||||||
public class VersionMCCommand extends TBMCCommandBase {
|
|
||||||
@Override
|
|
||||||
public boolean OnCommand(CommandSender commandSender, String s, String[] strings) {
|
|
||||||
commandSender.sendMessage(VersionCommand.getVersion());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] GetHelpText(String s) {
|
|
||||||
return VersionCommand.getVersion(); //Heh
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,10 @@
|
||||||
package buttondevteam.discordplugin.playerfaker;
|
package buttondevteam.discordplugin.playerfaker;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.discordplugin.DiscordSenderBase;
|
import buttondevteam.discordplugin.DiscordSenderBase;
|
||||||
|
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
|
@ -11,8 +15,6 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||||
import org.bukkit.metadata.MetadataValue;
|
import org.bukkit.metadata.MetadataValue;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -20,10 +22,11 @@ import java.util.*;
|
||||||
@Setter
|
@Setter
|
||||||
@SuppressWarnings("deprecated")
|
@SuppressWarnings("deprecated")
|
||||||
public abstract class DiscordEntity extends DiscordSenderBase implements Entity {
|
public abstract class DiscordEntity extends DiscordSenderBase implements Entity {
|
||||||
protected DiscordEntity(IUser user, IChannel channel, int entityId, UUID uuid) {
|
protected DiscordEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
|
||||||
super(user, channel);
|
super(user, channel);
|
||||||
this.entityId = entityId;
|
this.entityId = entityId;
|
||||||
uniqueId = uuid;
|
uniqueId = uuid;
|
||||||
|
this.module = module;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashMap<String, MetadataValue> metadata = new HashMap<String, MetadataValue>();
|
private HashMap<String, MetadataValue> metadata = new HashMap<String, MetadataValue>();
|
||||||
|
@ -34,6 +37,7 @@ public abstract class DiscordEntity extends DiscordSenderBase implements Entity
|
||||||
private EntityDamageEvent lastDamageCause;
|
private EntityDamageEvent lastDamageCause;
|
||||||
private final Set<String> scoreboardTags = new HashSet<String>();
|
private final Set<String> scoreboardTags = new HashSet<String>();
|
||||||
private final UUID uniqueId;
|
private final UUID uniqueId;
|
||||||
|
private final MinecraftChatModule module;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
|
public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
|
||||||
|
@ -42,7 +46,7 @@ public abstract class DiscordEntity extends DiscordSenderBase implements Entity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<MetadataValue> getMetadata(String metadataKey) {
|
public List<MetadataValue> getMetadata(String metadataKey) {
|
||||||
return Arrays.asList(metadata.get(metadataKey)); // Who needs multiple data anyways
|
return Collections.singletonList(metadata.get(metadataKey)); // Who needs multiple data anyways
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,31 +95,35 @@ public abstract class DiscordEntity extends DiscordSenderBase implements Entity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean teleport(Location location) {
|
public boolean teleport(Location location) {
|
||||||
this.location = location;
|
if (module.allowFakePlayerTeleports().get())
|
||||||
|
this.location = location;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean teleport(Location location, TeleportCause cause) {
|
public boolean teleport(Location location, TeleportCause cause) {
|
||||||
this.location = location;
|
if (module.allowFakePlayerTeleports().get())
|
||||||
|
this.location = location;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean teleport(Entity destination) {
|
public boolean teleport(Entity destination) {
|
||||||
this.location = destination.getLocation();
|
if (module.allowFakePlayerTeleports().get())
|
||||||
|
this.location = destination.getLocation();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean teleport(Entity destination, TeleportCause cause) {
|
public boolean teleport(Entity destination, TeleportCause cause) {
|
||||||
this.location = destination.getLocation();
|
if (module.allowFakePlayerTeleports().get())
|
||||||
|
this.location = destination.getLocation();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Entity> getNearbyEntities(double x, double y, double z) {
|
public List<Entity> getNearbyEntities(double x, double y, double z) {
|
||||||
return Arrays.asList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -163,7 +171,7 @@ public abstract class DiscordEntity extends DiscordSenderBase implements Entity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Entity> getPassengers() {
|
public List<Entity> getPassengers() {
|
||||||
return Arrays.asList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,8 @@
|
||||||
package buttondevteam.discordplugin.playerfaker;
|
package buttondevteam.discordplugin.playerfaker;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
import org.bukkit.GameMode;
|
import org.bukkit.GameMode;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
@ -8,14 +11,12 @@ import org.bukkit.entity.HumanEntity;
|
||||||
import org.bukkit.entity.Villager;
|
import org.bukkit.entity.Villager;
|
||||||
import org.bukkit.inventory.*;
|
import org.bukkit.inventory.*;
|
||||||
import org.bukkit.inventory.InventoryView.Property;
|
import org.bukkit.inventory.InventoryView.Property;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class DiscordHumanEntity extends DiscordLivingEntity implements HumanEntity {
|
public abstract class DiscordHumanEntity extends DiscordLivingEntity implements HumanEntity {
|
||||||
protected DiscordHumanEntity(IUser user, IChannel channel, int entityId, UUID uuid) {
|
protected DiscordHumanEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
|
||||||
super(user, channel, entityId, uuid);
|
super(user, channel, entityId, uuid, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlayerInventory inv = new DiscordPlayerInventory(this);
|
private PlayerInventory inv = new DiscordPlayerInventory(this);
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package buttondevteam.discordplugin.playerfaker;
|
package buttondevteam.discordplugin.playerfaker;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.User;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
@ -16,15 +19,13 @@ import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.potion.PotionEffect;
|
import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IUser;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public abstract class DiscordLivingEntity extends DiscordEntity implements LivingEntity {
|
public abstract class DiscordLivingEntity extends DiscordEntity implements LivingEntity {
|
||||||
|
|
||||||
protected DiscordLivingEntity(IUser user, IChannel channel, int entityId, UUID uuid) {
|
protected DiscordLivingEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
|
||||||
super(user, channel, entityId, uuid);
|
super(user, channel, entityId, uuid, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Getter EntityEquipment equipment = new DiscordEntityEquipment(this);
|
private @Getter EntityEquipment equipment = new DiscordEntityEquipment(this);
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
package buttondevteam.discordplugin.playerfaker.perm;
|
||||||
|
|
||||||
|
import buttondevteam.core.MainPlugin;
|
||||||
|
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
||||||
|
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
|
||||||
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
|
import me.lucko.luckperms.bukkit.LPBukkitBootstrap;
|
||||||
|
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||||
|
import me.lucko.luckperms.bukkit.inject.dummy.DummyPermissibleBase;
|
||||||
|
import me.lucko.luckperms.bukkit.inject.permissible.LPPermissible;
|
||||||
|
import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener;
|
||||||
|
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||||
|
import me.lucko.luckperms.common.locale.message.Message;
|
||||||
|
import me.lucko.luckperms.common.model.User;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
import org.bukkit.permissions.PermissibleBase;
|
||||||
|
import org.bukkit.permissions.PermissionAttachment;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public final class LPInjector implements Listener { //Disable login event for LuckPerms
|
||||||
|
private LPBukkitPlugin plugin;
|
||||||
|
private BukkitConnectionListener connectionListener;
|
||||||
|
private Set<UUID> deniedLogin;
|
||||||
|
private Field detectedCraftBukkitOfflineMode;
|
||||||
|
private Method printCraftBukkitOfflineModeError;
|
||||||
|
private Field PERMISSIBLE_BASE_ATTACHMENTS_FIELD;
|
||||||
|
private Method convertAndAddAttachments;
|
||||||
|
private Method getActive;
|
||||||
|
private Method setOldPermissible;
|
||||||
|
private Method getOldPermissible;
|
||||||
|
|
||||||
|
public LPInjector(MainPlugin mp) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException {
|
||||||
|
LPBukkitBootstrap bs = (LPBukkitBootstrap) Bukkit.getPluginManager().getPlugin("LuckPerms");
|
||||||
|
Field field = LPBukkitBootstrap.class.getDeclaredField("plugin");
|
||||||
|
field.setAccessible(true);
|
||||||
|
plugin = (LPBukkitPlugin) field.get(bs);
|
||||||
|
MCChatUtils.addStaticExcludedPlugin(PlayerLoginEvent.class, "LuckPerms");
|
||||||
|
MCChatUtils.addStaticExcludedPlugin(PlayerQuitEvent.class, "LuckPerms");
|
||||||
|
|
||||||
|
field = LPBukkitPlugin.class.getDeclaredField("connectionListener");
|
||||||
|
field.setAccessible(true);
|
||||||
|
connectionListener = (BukkitConnectionListener) field.get(plugin);
|
||||||
|
field = connectionListener.getClass().getDeclaredField("deniedLogin");
|
||||||
|
field.setAccessible(true);
|
||||||
|
//noinspection unchecked
|
||||||
|
deniedLogin = (Set<UUID>) field.get(connectionListener);
|
||||||
|
field = connectionListener.getClass().getDeclaredField("detectedCraftBukkitOfflineMode");
|
||||||
|
field.setAccessible(true);
|
||||||
|
detectedCraftBukkitOfflineMode = field;
|
||||||
|
printCraftBukkitOfflineModeError = connectionListener.getClass().getDeclaredMethod("printCraftBukkitOfflineModeError");
|
||||||
|
printCraftBukkitOfflineModeError.setAccessible(true);
|
||||||
|
|
||||||
|
//PERMISSIBLE_FIELD = DiscordFakePlayer.class.getDeclaredField("perm");
|
||||||
|
//PERMISSIBLE_FIELD.setAccessible(true); //Hacking my own plugin, while we're at it
|
||||||
|
PERMISSIBLE_BASE_ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
|
||||||
|
PERMISSIBLE_BASE_ATTACHMENTS_FIELD.setAccessible(true);
|
||||||
|
|
||||||
|
convertAndAddAttachments = LPPermissible.class.getDeclaredMethod("convertAndAddAttachments", Collection.class);
|
||||||
|
convertAndAddAttachments.setAccessible(true);
|
||||||
|
getActive = LPPermissible.class.getDeclaredMethod("getActive");
|
||||||
|
getActive.setAccessible(true);
|
||||||
|
setOldPermissible = LPPermissible.class.getDeclaredMethod("setOldPermissible", PermissibleBase.class);
|
||||||
|
setOldPermissible.setAccessible(true);
|
||||||
|
getOldPermissible = LPPermissible.class.getDeclaredMethod("getOldPermissible");
|
||||||
|
getOldPermissible.setAccessible(true);
|
||||||
|
|
||||||
|
TBMCCoreAPI.RegisterEventsForExceptions(this, mp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Code copied from LuckPerms - me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerLogin(PlayerLoginEvent e) {
|
||||||
|
/* Called when the player starts logging into the server.
|
||||||
|
At this point, the users data should be present and loaded. */
|
||||||
|
|
||||||
|
if (!(e.getPlayer() instanceof DiscordFakePlayer))
|
||||||
|
return; //Normal players must be handled by the plugin
|
||||||
|
|
||||||
|
final DiscordFakePlayer player = (DiscordFakePlayer) e.getPlayer();
|
||||||
|
|
||||||
|
if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||||
|
plugin.getLogger().info("Processing login for " + player.getUniqueId() + " - " + player.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
final User user = plugin.getUserManager().getIfLoaded(player.getUniqueId());
|
||||||
|
|
||||||
|
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
|
||||||
|
if (user == null) {
|
||||||
|
deniedLogin.add(player.getUniqueId());
|
||||||
|
|
||||||
|
if (!connectionListener.getUniqueConnections().contains(player.getUniqueId())) {
|
||||||
|
|
||||||
|
plugin.getLogger().warn("User " + player.getUniqueId() + " - " + player.getName() +
|
||||||
|
" doesn't have data pre-loaded, they have never been processed during pre-login in this session." +
|
||||||
|
" - denying login.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ((Boolean) detectedCraftBukkitOfflineMode.get(connectionListener)) {
|
||||||
|
printCraftBukkitOfflineModeError.invoke(connectionListener);
|
||||||
|
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_STATE_ERROR_CB_OFFLINE_MODE.asString(plugin.getLocaleManager()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (IllegalAccessException | InvocationTargetException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
plugin.getLogger().warn("User " + player.getUniqueId() + " - " + player.getName() +
|
||||||
|
" doesn't currently have data pre-loaded, but they have been processed before in this session." +
|
||||||
|
" - denying login.");
|
||||||
|
}
|
||||||
|
|
||||||
|
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_STATE_ERROR.asString(plugin.getLocaleManager()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User instance is there, now we can inject our custom Permissible into the player.
|
||||||
|
// Care should be taken at this stage to ensure that async tasks which manipulate bukkit data check that the player is still online.
|
||||||
|
try {
|
||||||
|
// get the existing PermissibleBase held by the player
|
||||||
|
PermissibleBase oldPermissible = player.getPerm();
|
||||||
|
|
||||||
|
// Make a new permissible for the user
|
||||||
|
LPPermissible lpPermissible = new LPPermissible(player, user, plugin);
|
||||||
|
|
||||||
|
// Inject into the player
|
||||||
|
inject(player, lpPermissible, oldPermissible);
|
||||||
|
|
||||||
|
} catch (Throwable t) {
|
||||||
|
plugin.getLogger().warn("Exception thrown when setting up permissions for " +
|
||||||
|
player.getUniqueId() + " - " + player.getName() + " - denying login.");
|
||||||
|
t.printStackTrace();
|
||||||
|
|
||||||
|
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_SETUP_ERROR.asString(plugin.getLocaleManager()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.refreshAutoOp(player, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the last priority to unload, so plugins can still perform permission checks on this event
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent e) {
|
||||||
|
if (!(e.getPlayer() instanceof DiscordFakePlayer))
|
||||||
|
return;
|
||||||
|
|
||||||
|
final DiscordFakePlayer player = (DiscordFakePlayer) e.getPlayer();
|
||||||
|
|
||||||
|
connectionListener.handleDisconnect(player.getUniqueId());
|
||||||
|
|
||||||
|
// perform unhooking from bukkit objects 1 tick later.
|
||||||
|
// this allows plugins listening after us on MONITOR to still have intact permissions data
|
||||||
|
this.plugin.getBootstrap().getServer().getScheduler().runTaskLaterAsynchronously(this.plugin.getBootstrap(), () -> {
|
||||||
|
// Remove the custom permissible
|
||||||
|
try {
|
||||||
|
uninject(player, true);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle auto op
|
||||||
|
if (this.plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||||
|
player.setOp(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove their contexts cache
|
||||||
|
this.plugin.getContextManager().onPlayerQuit(player);
|
||||||
|
}, 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
//me.lucko.luckperms.bukkit.inject.permissible.PermissibleInjector
|
||||||
|
private void inject(DiscordFakePlayer player, LPPermissible newPermissible, PermissibleBase oldPermissible) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
|
||||||
|
// seems we have already injected into this player.
|
||||||
|
if (oldPermissible instanceof LPPermissible) {
|
||||||
|
throw new IllegalStateException("LPPermissible already injected into player " + player.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move attachments over from the old permissible
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
List<PermissionAttachment> attachments = (List<PermissionAttachment>) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(oldPermissible);
|
||||||
|
|
||||||
|
convertAndAddAttachments.invoke(newPermissible, attachments);
|
||||||
|
attachments.clear();
|
||||||
|
oldPermissible.clearPermissions();
|
||||||
|
|
||||||
|
// Setup the new permissible
|
||||||
|
((AtomicBoolean) getActive.invoke(newPermissible)).set(true);
|
||||||
|
setOldPermissible.invoke(newPermissible, oldPermissible);
|
||||||
|
|
||||||
|
// inject the new instance
|
||||||
|
player.setPerm(newPermissible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uninject(DiscordFakePlayer player, boolean dummy) throws Exception {
|
||||||
|
|
||||||
|
// gets the players current permissible.
|
||||||
|
PermissibleBase permissible = player.getPerm();
|
||||||
|
|
||||||
|
// only uninject if the permissible was a luckperms one.
|
||||||
|
if (permissible instanceof LPPermissible) {
|
||||||
|
LPPermissible lpPermissible = ((LPPermissible) permissible);
|
||||||
|
|
||||||
|
// clear all permissions
|
||||||
|
lpPermissible.clearPermissions();
|
||||||
|
|
||||||
|
// set to inactive
|
||||||
|
((AtomicBoolean) getActive.invoke(lpPermissible)).set(false);
|
||||||
|
|
||||||
|
// handle the replacement permissible.
|
||||||
|
if (dummy) {
|
||||||
|
// just inject a dummy class. this is used when we know the player is about to quit the server.
|
||||||
|
player.setPerm(DummyPermissibleBase.INSTANCE);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
PermissibleBase newPb = (PermissibleBase) getOldPermissible.invoke(lpPermissible);
|
||||||
|
if (newPb == null) {
|
||||||
|
newPb = new PermissibleBase(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.setPerm(newPb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,17 +4,19 @@ import buttondevteam.core.ComponentManager;
|
||||||
import buttondevteam.discordplugin.DPUtils;
|
import buttondevteam.discordplugin.DPUtils;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ReadOnlyConfigData;
|
||||||
|
import discord4j.core.event.domain.role.RoleCreateEvent;
|
||||||
|
import discord4j.core.event.domain.role.RoleDeleteEvent;
|
||||||
|
import discord4j.core.event.domain.role.RoleEvent;
|
||||||
|
import discord4j.core.event.domain.role.RoleUpdateEvent;
|
||||||
|
import discord4j.core.object.entity.MessageChannel;
|
||||||
|
import discord4j.core.object.entity.Role;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import sx.blah.discord.handle.impl.events.guild.role.RoleCreateEvent;
|
import reactor.core.publisher.Mono;
|
||||||
import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent;
|
|
||||||
import sx.blah.discord.handle.impl.events.guild.role.RoleEvent;
|
|
||||||
import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent;
|
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
|
||||||
import sx.blah.discord.handle.obj.IRole;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -24,7 +26,7 @@ public class GameRoleModule extends Component<DiscordPlugin> {
|
||||||
@Override
|
@Override
|
||||||
protected void enable() {
|
protected void enable() {
|
||||||
getPlugin().getManager().registerCommand(new RoleCommand(this));
|
getPlugin().getManager().registerCommand(new RoleCommand(this));
|
||||||
GameRoles = DiscordPlugin.mainServer.getRoles().stream().filter(this::isGameRole).map(IRole::getName).collect(Collectors.toList());
|
GameRoles = DiscordPlugin.mainServer.getRoles().filterWhen(this::isGameRole).map(Role::getName).collect(Collectors.toList()).block();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -32,7 +34,7 @@ public class GameRoleModule extends Component<DiscordPlugin> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigData<IChannel> logChannel() {
|
private ReadOnlyConfigData<Mono<MessageChannel>> logChannel() {
|
||||||
return DPUtils.channelData(getConfig(), "logChannel", 239519012529111040L);
|
return DPUtils.channelData(getConfig(), "logChannel", 239519012529111040L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,41 +45,55 @@ public class GameRoleModule extends Component<DiscordPlugin> {
|
||||||
val logChannel = grm.logChannel().get();
|
val logChannel = grm.logChannel().get();
|
||||||
if (roleEvent instanceof RoleCreateEvent) {
|
if (roleEvent instanceof RoleCreateEvent) {
|
||||||
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
|
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
|
||||||
if (roleEvent.getRole().isDeleted() || !grm.isGameRole(roleEvent.getRole()))
|
Role role=((RoleCreateEvent) roleEvent).getRole();
|
||||||
return; //Deleted or not a game role
|
grm.isGameRole(role).flatMap(b -> {
|
||||||
GameRoles.add(roleEvent.getRole().getName());
|
if (!b)
|
||||||
if (logChannel != null)
|
return Mono.empty(); //Deleted or not a game role
|
||||||
DiscordPlugin.sendMessageToChannel(logChannel, "Added " + roleEvent.getRole().getName() + " as game role. If you don't want this, change the role's color from the default.");
|
GameRoles.add(role.getName());
|
||||||
|
if (logChannel != null)
|
||||||
|
return logChannel.flatMap(ch -> ch.createMessage("Added " + role.getName() + " as game role. If you don't want this, change the role's color from the default."));
|
||||||
|
return Mono.empty();
|
||||||
|
}).subscribe();
|
||||||
}, 100);
|
}, 100);
|
||||||
} else if (roleEvent instanceof RoleDeleteEvent) {
|
} else if (roleEvent instanceof RoleDeleteEvent) {
|
||||||
if (GameRoles.remove(roleEvent.getRole().getName()) && logChannel != null)
|
Role role=((RoleDeleteEvent) roleEvent).getRole().orElse(null);
|
||||||
DiscordPlugin.sendMessageToChannel(logChannel, "Removed " + roleEvent.getRole().getName() + " as a game role.");
|
if(role==null) return;
|
||||||
|
if (GameRoles.remove(role.getName()) && logChannel != null)
|
||||||
|
logChannel.flatMap(ch -> ch.createMessage("Removed " + role.getName() + " as a game role.")).subscribe();
|
||||||
} else if (roleEvent instanceof RoleUpdateEvent) {
|
} else if (roleEvent instanceof RoleUpdateEvent) {
|
||||||
val event = (RoleUpdateEvent) roleEvent;
|
val event = (RoleUpdateEvent) roleEvent;
|
||||||
if (!grm.isGameRole(event.getNewRole())) {
|
if(!event.getOld().isPresent()) {
|
||||||
if (GameRoles.remove(event.getOldRole().getName()) && logChannel != null)
|
DPUtils.getLogger().warning("Old role not stored, cannot update game role!");
|
||||||
DiscordPlugin.sendMessageToChannel(logChannel, "Removed " + event.getOldRole().getName() + " as a game role because it's color changed.");
|
return;
|
||||||
} else {
|
|
||||||
if (GameRoles.contains(event.getOldRole().getName()) && event.getOldRole().getName().equals(event.getNewRole().getName()))
|
|
||||||
return;
|
|
||||||
boolean removed = GameRoles.remove(event.getOldRole().getName()); //Regardless of whether it was a game role
|
|
||||||
GameRoles.add(event.getNewRole().getName()); //Add it because it has no color
|
|
||||||
if (logChannel != null) {
|
|
||||||
if (removed)
|
|
||||||
DiscordPlugin.sendMessageToChannel(logChannel, "Changed game role from " + event.getOldRole().getName() + " to " + event.getNewRole().getName() + ".");
|
|
||||||
else
|
|
||||||
DiscordPlugin.sendMessageToChannel(logChannel, "Added " + event.getNewRole().getName() + " as game role because it has the default color.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Role or=event.getOld().get();
|
||||||
|
grm.isGameRole(event.getCurrent()).flatMap(b -> {
|
||||||
|
if (!b) {
|
||||||
|
if (GameRoles.remove(or.getName()) && logChannel != null)
|
||||||
|
return logChannel.flatMap(ch -> ch.createMessage("Removed " + or.getName() + " as a game role because it's color changed."));
|
||||||
|
} else {
|
||||||
|
if (GameRoles.contains(or.getName()) && or.getName().equals(event.getCurrent().getName()))
|
||||||
|
return Mono.empty();
|
||||||
|
boolean removed = GameRoles.remove(or.getName()); //Regardless of whether it was a game role
|
||||||
|
GameRoles.add(event.getCurrent().getName()); //Add it because it has no color
|
||||||
|
if (logChannel != null) {
|
||||||
|
if (removed)
|
||||||
|
return logChannel.flatMap(ch -> ch.createMessage("Changed game role from " + or.getName() + " to " + event.getCurrent().getName() + "."));
|
||||||
|
else
|
||||||
|
return logChannel.flatMap(ch -> ch.createMessage("Added " + event.getCurrent().getName() + " as game role because it has the default color."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Mono.empty();
|
||||||
|
}).subscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isGameRole(IRole r) {
|
private Mono<Boolean> isGameRole(Role r) {
|
||||||
if (r.getGuild().getLongID() != DiscordPlugin.mainServer.getLongID())
|
if (r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong())
|
||||||
return false; //Only allow on the main server
|
return Mono.just(false); //Only allow on the main server
|
||||||
val rc = new Color(149, 165, 166, 0);
|
val rc = new Color(149, 165, 166, 0);
|
||||||
return r.getColor().equals(rc)
|
return Mono.just(r.getColor().equals(rc)).filter(b -> b).flatMap(b ->
|
||||||
&& DiscordPlugin.dc.getOurUser().getRolesForGuild(DiscordPlugin.mainServer)
|
DiscordPlugin.dc.getSelf().flatMap(u -> u.asMember(DiscordPlugin.mainServer.getId())).flatMap(m -> m.hasHigherRoles(Collections.singleton(r)))) //Below one of our roles
|
||||||
.stream().anyMatch(or -> r.getPosition() < or.getPosition()); //Below one of our roles
|
.defaultIfEmpty(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
package buttondevteam.discordplugin.role;
|
package buttondevteam.discordplugin.role;
|
||||||
|
|
||||||
import buttondevteam.discordplugin.DPUtils;
|
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
import buttondevteam.discordplugin.commands.Command2DCSender;
|
import buttondevteam.discordplugin.commands.Command2DCSender;
|
||||||
import buttondevteam.discordplugin.commands.ICommand2DC;
|
import buttondevteam.discordplugin.commands.ICommand2DC;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
|
import discord4j.core.object.entity.Role;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import sx.blah.discord.handle.obj.IRole;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -27,12 +26,12 @@ public class RoleCommand extends ICommand2DC {
|
||||||
"This command adds a role to your account."
|
"This command adds a role to your account."
|
||||||
})
|
})
|
||||||
public boolean add(Command2DCSender sender, @Command2.TextArg String rolename) {
|
public boolean add(Command2DCSender sender, @Command2.TextArg String rolename) {
|
||||||
final IRole role = checkAndGetRole(sender, rolename);
|
final Role role = checkAndGetRole(sender, rolename);
|
||||||
if (role == null)
|
if (role == null)
|
||||||
return true;
|
return true;
|
||||||
try {
|
try {
|
||||||
DPUtils.perform(() -> sender.getMessage().getAuthor().addRole(role));
|
sender.getMessage().getAuthorAsMember()
|
||||||
sender.sendMessage("added role.");
|
.subscribe(m -> m.addRole(role.getId()).subscribe(r -> sender.sendMessage("added role.")));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Error while adding role!", e);
|
TBMCCoreAPI.SendException("Error while adding role!", e);
|
||||||
sender.sendMessage("an error occured while adding the role.");
|
sender.sendMessage("an error occured while adding the role.");
|
||||||
|
@ -45,12 +44,12 @@ public class RoleCommand extends ICommand2DC {
|
||||||
"This command removes a role from your account."
|
"This command removes a role from your account."
|
||||||
})
|
})
|
||||||
public boolean remove(Command2DCSender sender, @Command2.TextArg String rolename) {
|
public boolean remove(Command2DCSender sender, @Command2.TextArg String rolename) {
|
||||||
final IRole role = checkAndGetRole(sender, rolename);
|
final Role role = checkAndGetRole(sender, rolename);
|
||||||
if (role == null)
|
if (role == null)
|
||||||
return true;
|
return true;
|
||||||
try {
|
try {
|
||||||
DPUtils.perform(() -> sender.getMessage().getAuthor().removeRole(role));
|
sender.getMessage().getAuthorAsMember()
|
||||||
sender.sendMessage("removed role.");
|
.subscribe(m -> m.removeRole(role.getId()).subscribe(r -> sender.sendMessage("removed role.")));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Error while removing role!", e);
|
TBMCCoreAPI.SendException("Error while removing role!", e);
|
||||||
sender.sendMessage("an error occured while removing the role.");
|
sender.sendMessage("an error occured while removing the role.");
|
||||||
|
@ -61,9 +60,9 @@ public class RoleCommand extends ICommand2DC {
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public void list(Command2DCSender sender) {
|
public void list(Command2DCSender sender) {
|
||||||
sender.sendMessage("list of roles:\n" + grm.GameRoles.stream().sorted().collect(Collectors.joining("\n")));
|
sender.sendMessage("list of roles:\n" + grm.GameRoles.stream().sorted().collect(Collectors.joining("\n")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IRole checkAndGetRole(Command2DCSender sender, String rolename) {
|
private Role checkAndGetRole(Command2DCSender sender, String rolename) {
|
||||||
String rname = rolename;
|
String rname = rolename;
|
||||||
if (!grm.GameRoles.contains(rolename)) { //If not found as-is, correct case
|
if (!grm.GameRoles.contains(rolename)) { //If not found as-is, correct case
|
||||||
val orn = grm.GameRoles.stream().filter(r -> r.equalsIgnoreCase(rolename)).findAny();
|
val orn = grm.GameRoles.stream().filter(r -> r.equalsIgnoreCase(rolename)).findAny();
|
||||||
|
@ -73,18 +72,23 @@ public class RoleCommand extends ICommand2DC {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
rname = orn.get();
|
rname = orn.get();
|
||||||
}
|
}
|
||||||
final List<IRole> roles = DiscordPlugin.mainServer.getRolesByName(rname);
|
val frname = rname;
|
||||||
if (roles.size() == 0) {
|
final List<Role> roles = DiscordPlugin.mainServer.getRoles().filter(r -> r.getName().equals(frname)).collectList().block();
|
||||||
sender.sendMessage("the specified role cannot be found on Discord! Removing from the list.");
|
if (roles == null) {
|
||||||
grm.GameRoles.remove(rolename);
|
sender.sendMessage("an error occured.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (roles.size() > 1) {
|
if (roles.size() == 0) {
|
||||||
sender.sendMessage("there are multiple roles with this name. Why are there multiple roles with this name?");
|
sender.sendMessage("the specified role cannot be found on Discord! Removing from the list.");
|
||||||
return null;
|
grm.GameRoles.remove(rolename);
|
||||||
}
|
return null;
|
||||||
return roles.get(0);
|
}
|
||||||
}
|
if (roles.size() > 1) {
|
||||||
|
sender.sendMessage("there are multiple roles with this name. Why are there multiple roles with this name?");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return roles.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
16
src/main/java/buttondevteam/discordplugin/util/Timings.java
Normal file
16
src/main/java/buttondevteam/discordplugin/util/Timings.java
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package buttondevteam.discordplugin.util;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.listeners.CommonListeners;
|
||||||
|
|
||||||
|
public class Timings {
|
||||||
|
private long start;
|
||||||
|
|
||||||
|
public Timings() {
|
||||||
|
start = System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printElapsed(String message) {
|
||||||
|
CommonListeners.debug(message + " (" + (System.nanoTime() - start) / 1000000L + ")");
|
||||||
|
start = System.nanoTime();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue