🎃 Halloween Update
parent
59c2058ac4
commit
b90373a139
|
@ -0,0 +1,4 @@
|
|||
/MinetestChatBridgeBot/.gradle/
|
||||
/MinetestChatBridgeBot/build/
|
||||
/MinetestChatBridgeIRCBot/.gradle/
|
||||
/MinetestChatBridgeIRCBot/build/
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="bin/main" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="test"/>
|
||||
<attribute name="gradle_used_by_scope" value="test"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>MinetestChatBridgeBot</name>
|
||||
<comment>Project MinetestChatBridgeBot created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,13 @@
|
|||
arguments=
|
||||
auto.sync=false
|
||||
build.scans.enabled=false
|
||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(5.6.1))
|
||||
connection.project.dir=
|
||||
eclipse.preferences.version=1
|
||||
gradle.user.home=
|
||||
java.home=
|
||||
jvm.arguments=
|
||||
offline.mode=false
|
||||
override.workspace.settings=true
|
||||
show.console.view=true
|
||||
show.executions.view=true
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,37 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'application'
|
||||
|
||||
|
||||
description = 'Minetest Chat Bridge Discord Bot'
|
||||
mainClassName = 'appguru.Main'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "net.dv8tion:JDA:4.0.0_39", 'ch.qos.logback:logback-classic:1.3.0-alpha4'
|
||||
compile group: 'com.google.guava', name: 'guava', version: '23.5-jre'
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Created-By': "Gradle ${gradle.gradleVersion}",
|
||||
'Main-Class': "appguru.Main"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1 @@
|
|||
rootProject.name = 'MinetestChatBridgeBot'
|
|
@ -1,6 +1,8 @@
|
|||
package appguru;
|
||||
|
||||
import bridge.FileBridge;
|
||||
import bridge.ProcessBridge;
|
||||
import bridge.SocketBridge;
|
||||
import chat.Bot;
|
||||
import commands.StatusCommand;
|
||||
import misc.GarbageCollector;
|
||||
|
@ -15,12 +17,15 @@ import java.util.Date;
|
|||
public class Main {
|
||||
public static long STARTED_AT;
|
||||
public static int GARBAGE_COLLECTION=5000; //5s
|
||||
public static long PING_WAIT=20000; //20s
|
||||
public static long PING_WAIT=5000; //5s
|
||||
public static String PREFIX="!";
|
||||
public static String DISCORD_PREFIX="?";
|
||||
public static String GUILD_ID=null;
|
||||
|
||||
public static PrintStream OUT=System.out;
|
||||
|
||||
public static ProcessBridge PROCESS_BRIDGE;
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
if (args.length > 4) {
|
||||
|
@ -60,16 +65,22 @@ public class Main {
|
|||
OUT.println("INFO: Starting Minetest chat bridge");
|
||||
String token=args[0];
|
||||
String channelname=args[1];
|
||||
File in=new File(args[2]);
|
||||
File out=new File(args[3]);
|
||||
if (!in.isFile() || !out.isFile() || !in.canWrite() || !in.canRead() || !out.canWrite() || !out.canRead()) {
|
||||
OUT.println("ERR: Input or output files do not exist or can't be read/written.");
|
||||
OUT.close();
|
||||
System.exit(0);
|
||||
|
||||
if (args[2].length() == 0) {
|
||||
int socket_port=Integer.parseInt(args[3]);
|
||||
PROCESS_BRIDGE=new SocketBridge("localhost", socket_port);
|
||||
} else {
|
||||
File in=new File(args[2]);
|
||||
File out=new File(args[3]);
|
||||
if (!in.isFile() || !out.isFile() || !in.canWrite() || !in.canRead() || !out.canWrite() || !out.canRead()) {
|
||||
OUT.println("ERR: Input or output files do not exist or can't be read/written.");
|
||||
System.exit(0);
|
||||
}
|
||||
PROCESS_BRIDGE=new FileBridge(in, out);
|
||||
}
|
||||
ProcessBridge pb=new ProcessBridge(in, out);
|
||||
|
||||
try {
|
||||
Bot i=new Bot(token, pb, channelname);
|
||||
Bot i=new Bot(token, PROCESS_BRIDGE, channelname);
|
||||
|
||||
i.registerInfo("status", "Status", "", Color.CYAN, null);
|
||||
i.registerCommand("status", new StatusCommand());
|
|
@ -5,21 +5,18 @@ import appguru.Main;
|
|||
import java.io.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ProcessBridge {
|
||||
public static long PING_WAIT=20000; //20s
|
||||
public class FileBridge extends ProcessBridge {
|
||||
|
||||
public long last_ping;
|
||||
public void ping() {
|
||||
last_ping=System.currentTimeMillis();
|
||||
}
|
||||
public File out_file;
|
||||
public File in;
|
||||
public PrintWriter out;
|
||||
private long last_ping_sent;
|
||||
|
||||
public ProcessBridge(File in, File out) throws IOException {
|
||||
public FileBridge(File in, File out) throws IOException {
|
||||
this.out = new PrintWriter(new BufferedWriter(new FileWriter(out, true)));
|
||||
this.out_file=out;
|
||||
this.in=in;
|
||||
this.last_ping_sent=System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void kill(String reason) {
|
||||
|
@ -30,9 +27,11 @@ public class ProcessBridge {
|
|||
fw.write("");
|
||||
fw.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
Main.OUT.close();
|
||||
out.write("[KIL]"+reason);
|
||||
out.close();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
|
@ -47,15 +46,18 @@ public class ProcessBridge {
|
|||
try {
|
||||
Thread.sleep(20);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
Main.OUT.flush();
|
||||
if (System.currentTimeMillis()-last_ping_sent >= 1000) {
|
||||
out.write("[PIN]");
|
||||
}
|
||||
// Main.OUT.flush();
|
||||
out.flush();
|
||||
out.close();
|
||||
try {
|
||||
out = new PrintWriter(new BufferedWriter(new FileWriter(out_file, true)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,19 +73,19 @@ public class ProcessBridge {
|
|||
try {
|
||||
Thread.sleep(20);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
BufferedReader r = null;
|
||||
try {
|
||||
r = new BufferedReader(new FileReader(in));
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
String line = null;
|
||||
try {
|
||||
line = r.readLine();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
boolean one_line = line != null;
|
||||
while (line != null) {
|
||||
|
@ -99,7 +101,7 @@ public class ProcessBridge {
|
|||
try {
|
||||
line = r.readLine();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
if (System.currentTimeMillis()-last_ping > Main.PING_WAIT) {
|
||||
|
@ -109,7 +111,7 @@ public class ProcessBridge {
|
|||
try {
|
||||
FileWriter fw = new FileWriter(in);fw.write("");fw.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package bridge;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class ProcessBridge {
|
||||
public long last_ping;
|
||||
public void ping() {
|
||||
last_ping=System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public abstract void kill(String reason);
|
||||
|
||||
public abstract void write(String out);
|
||||
|
||||
public abstract void serve();
|
||||
|
||||
public abstract void listen(Consumer<String> line_consumer);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package bridge;
|
||||
|
||||
import appguru.Main;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lars
|
||||
*/
|
||||
public class SocketBridge extends ProcessBridge {
|
||||
private Socket socket;
|
||||
private final BufferedWriter writer;
|
||||
private BufferedReader reader;
|
||||
|
||||
public SocketBridge(String host, int port) throws IOException {
|
||||
socket = new Socket();
|
||||
socket.connect(new InetSocketAddress(host, port));
|
||||
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kill(String reason) {
|
||||
Main.OUT.println("INFO: "+reason);
|
||||
if (socket.isConnected()) {
|
||||
try {
|
||||
writer.write("[KIL]"+reason);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
try {
|
||||
writer.close();
|
||||
reader.close();
|
||||
socket.close();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace(Main.OUT);
|
||||
} finally {
|
||||
Main.OUT.close();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String out) {
|
||||
synchronized (writer) {
|
||||
try {
|
||||
writer.write(out+"\n");
|
||||
writer.flush();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serve() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listen(Consumer<String> line_consumer) {
|
||||
ping();
|
||||
new Thread(() -> {
|
||||
while(true) {
|
||||
try {
|
||||
Thread.sleep(20);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
String line = null;
|
||||
try {
|
||||
line = reader.readLine();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(Main.OUT);
|
||||
if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
|
||||
kill("Socket connection lost");
|
||||
}
|
||||
}
|
||||
boolean one_line = line != null;
|
||||
while (line != null) {
|
||||
|
||||
if (line.startsWith("[PIN]")) { // A PING YAY
|
||||
ping();
|
||||
} else if (line.startsWith("[KIL]")) {
|
||||
kill("Minetest server shutting down; shutting down as well.");
|
||||
} else {
|
||||
line_consumer.accept(line);
|
||||
}
|
||||
|
||||
try {
|
||||
line = reader.readLine();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
if (System.currentTimeMillis()-last_ping > Main.PING_WAIT) {
|
||||
kill("No ping during the last "+(Main.PING_WAIT/1000)+"s; shutting down.");
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
|
@ -5,44 +5,32 @@ import bridge.ProcessBridge;
|
|||
import com.google.common.collect.HashBiMap;
|
||||
import commands.Command;
|
||||
import misc.Utils;
|
||||
import net.dv8tion.jda.core.*;
|
||||
import net.dv8tion.jda.core.entities.*;
|
||||
import net.dv8tion.jda.core.events.ReadyEvent;
|
||||
import net.dv8tion.jda.core.events.channel.text.TextChannelCreateEvent;
|
||||
import net.dv8tion.jda.core.events.channel.text.TextChannelDeleteEvent;
|
||||
import net.dv8tion.jda.core.events.channel.text.update.*;
|
||||
import net.dv8tion.jda.core.events.guild.GuildJoinEvent;
|
||||
import net.dv8tion.jda.core.events.guild.member.*;
|
||||
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.core.events.message.priv.PrivateMessageReceivedEvent;
|
||||
import net.dv8tion.jda.core.events.message.priv.react.PrivateMessageReactionAddEvent;
|
||||
import net.dv8tion.jda.core.events.role.RoleCreateEvent;
|
||||
import net.dv8tion.jda.core.events.role.RoleDeleteEvent;
|
||||
import net.dv8tion.jda.core.events.role.update.RoleUpdateColorEvent;
|
||||
import net.dv8tion.jda.core.events.role.update.RoleUpdateNameEvent;
|
||||
import net.dv8tion.jda.core.events.user.update.UserUpdateNameEvent;
|
||||
import net.dv8tion.jda.core.hooks.ListenerAdapter;
|
||||
import net.dv8tion.jda.core.utils.cache.MemberCacheView;
|
||||
import net.dv8tion.jda.api.*;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
import net.dv8tion.jda.api.events.ReadyEvent;
|
||||
import net.dv8tion.jda.api.events.channel.text.TextChannelCreateEvent;
|
||||
import net.dv8tion.jda.api.events.channel.text.TextChannelDeleteEvent;
|
||||
import net.dv8tion.jda.api.events.channel.text.update.*;
|
||||
import net.dv8tion.jda.api.events.guild.GuildJoinEvent;
|
||||
import net.dv8tion.jda.api.events.guild.member.*;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.events.role.RoleCreateEvent;
|
||||
import net.dv8tion.jda.api.events.role.RoleDeleteEvent;
|
||||
import net.dv8tion.jda.api.events.role.update.RoleUpdateColorEvent;
|
||||
import net.dv8tion.jda.api.events.role.update.RoleUpdateNameEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
|
||||
import javax.security.auth.login.LoginException;
|
||||
import java.io.*;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameEvent;
|
||||
|
||||
public class Bot extends ListenerAdapter {
|
||||
public static int DEFAULT_COLOR=Integer.parseInt("7289DA",16); // Discord color
|
||||
|
||||
public long last_ping;
|
||||
public void ping() {
|
||||
last_ping=System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public String text_channel;
|
||||
public ProcessBridge bridge;
|
||||
public JDA jda;
|
||||
|
@ -88,20 +76,27 @@ public class Bot extends ListenerAdapter {
|
|||
return this.jda.getGuildById(Main.GUILD_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChannelDelete(TextChannelDeleteEvent event) {
|
||||
if (event.getChannel().getIdLong() == global_channel) {
|
||||
System.err.println("Error ! Global channel was deleted !");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChannelUpdatePermissions(TextChannelUpdatePermissionsEvent event) {
|
||||
if (event.getChannel().getIdLong() == global_channel && !event.getChannel().canTalk()) {
|
||||
System.err.println("Error ! Cannot talk in global channel !");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChannelUpdateNSFW(TextChannelUpdateNSFWEvent event) {}
|
||||
@Override
|
||||
public void onTextChannelUpdateParent(TextChannelUpdateParentEvent event) {}
|
||||
@Override
|
||||
public void onTextChannelCreate(TextChannelCreateEvent event) {}
|
||||
|
||||
@Override
|
||||
|
@ -112,11 +107,15 @@ public class Bot extends ListenerAdapter {
|
|||
}*/
|
||||
// IDEA: leave
|
||||
}
|
||||
|
||||
public String escapeName(String name) {
|
||||
return name.replace(" ", "_").replace(",", "_");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuildMemberJoin(GuildMemberJoinEvent e) {
|
||||
Member m=e.getMember();
|
||||
String name=m.getEffectiveName().replace(" ", "_");
|
||||
String name=escapeName(m.getEffectiveName());
|
||||
members.put(Utils.getFreeKey(name, members), m.getUser().getIdLong());
|
||||
bridge.write("[JOI]"+name+" #"+ Utils.getColorString(m.getColorRaw()));
|
||||
}
|
||||
|
@ -169,10 +168,10 @@ public class Bot extends ListenerAdapter {
|
|||
if (chosen == null) {
|
||||
List<Guild> guilds=event.getJDA().getGuilds();
|
||||
String guild_id=guilds.get(0).getId();
|
||||
OffsetDateTime min_join_time=guilds.get(0).getMember(event.getJDA().getSelfUser()).getJoinDate();
|
||||
OffsetDateTime min_join_time=guilds.get(0).getMember(event.getJDA().getSelfUser()).getTimeJoined();
|
||||
for (int i=1; i < guilds.size(); i++) {
|
||||
Guild g=guilds.get(i);
|
||||
OffsetDateTime join_time=guilds.get(i).getMember(event.getJDA().getSelfUser()).getJoinDate();
|
||||
OffsetDateTime join_time=guilds.get(i).getMember(event.getJDA().getSelfUser()).getTimeJoined();
|
||||
if (join_time.isBefore(min_join_time)) {
|
||||
min_join_time=join_time;
|
||||
guild_id=g.getId();
|
||||
|
@ -182,16 +181,16 @@ public class Bot extends ListenerAdapter {
|
|||
Main.GUILD_ID=guild_id;
|
||||
}
|
||||
setGlobalChannel(getGuild().getTextChannelsByName(text_channel, true).get(0).getIdLong());
|
||||
event.getJDA().getPresence().setGame(net.dv8tion.jda.core.entities.Game.playing("Minetest"));
|
||||
event.getJDA().getPresence().setActivity(Activity.playing("Minetest"));
|
||||
for (Member m:getGuild().getMemberCache()) {
|
||||
String name=m.getEffectiveName().replace(" ", "_");
|
||||
String name=escapeName(m.getEffectiveName());
|
||||
String finalname=Utils.getFreeKey(name, members);
|
||||
members.put(finalname, m.getUser().getIdLong());
|
||||
int color=m.getColor() == null ? DEFAULT_COLOR:m.getColorRaw();
|
||||
bridge.write("[LIS]"+finalname+" #"+ Utils.getColorString(color));
|
||||
}
|
||||
for (Role r:getGuild().getRoles()) {
|
||||
String name=r.getName().replace(" ", "_");
|
||||
String name=escapeName(r.getName());
|
||||
String finalname=Utils.getFreeKey(name, roles);
|
||||
String output="[ROL]"+finalname+" #"+Utils.getColorString(r.getColorRaw());
|
||||
for (Member m:getGuild().getMembersWithRoles(r)) {
|
||||
|
@ -229,7 +228,7 @@ public class Bot extends ListenerAdapter {
|
|||
|
||||
@Override
|
||||
public void onRoleCreate(RoleCreateEvent event) {
|
||||
String name=event.getRole().getName().replace(" ", "_");
|
||||
String name=escapeName(event.getRole().getName());
|
||||
String output="[ROL]"+Utils.getFreeKey(name, roles)+" #"+Utils.getColorString(event.getRole().getColorRaw());
|
||||
bridge.write(output);
|
||||
}
|
||||
|
@ -238,7 +237,7 @@ public class Bot extends ListenerAdapter {
|
|||
@Override
|
||||
public void onRoleUpdateName(RoleUpdateNameEvent event) {
|
||||
String oldname=roles.inverse().get(event.getRole().getIdLong());
|
||||
String name=Utils.getFreeKey(event.getNewName().replace(" ", "_"), roles);
|
||||
String name=Utils.getFreeKey(escapeName(event.getNewName()), roles);
|
||||
roles.inverse().remove(event.getRole().getIdLong());
|
||||
roles.put(name, event.getRole().getIdLong());
|
||||
String output="[NAM]"+oldname+" "+name;
|
||||
|
@ -270,21 +269,18 @@ public class Bot extends ListenerAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
public void onGuildMemberNickChange(GuildMemberNickChangeEvent event) {
|
||||
String newnick=(event.getNewNick() != null ? event.getNewNick():event.getUser().getName()).replace(" ", "_");
|
||||
@Override
|
||||
public void onGuildMemberUpdateNickname(GuildMemberUpdateNicknameEvent event) {
|
||||
String newnick=escapeName((event.getNewNickname() != null ? event.getNewNickname():event.getUser().getName()));
|
||||
if (members.containsKey(newnick)) {
|
||||
getGuild().getController().setNickname(event.getMember(), event.getPrevNick()).queue();
|
||||
event.getMember().getUser().openPrivateChannel().queue(pc -> pc.sendMessage("Your nickname could not be changed to `"+event.getNewNick()+"` as there already is another guild member with a similar nickname.").queue());
|
||||
getGuild().modifyNickname(event.getMember(), event.getOldNickname()).queue();
|
||||
event.getMember().getUser().openPrivateChannel().queue(pc -> pc.sendMessage("Your nickname could not be changed to `"+event.getNewNickname()+"` as there already is another guild member with a similar nickname.").queue());
|
||||
} else {
|
||||
members.inverse().remove(event.getMember().getUser().getIdLong());
|
||||
members.put(newnick, event.getMember().getUser().getIdLong());
|
||||
}
|
||||
}
|
||||
|
||||
public String getIdentifier(Member m) {
|
||||
return null; // TODO replace spaces by underscores, check for duplicates, if yes use actual username or even discriminator
|
||||
}
|
||||
|
||||
public static String getName(Member m) {
|
||||
return m.getEffectiveName()+(m.getNickname() == null ? "":" aka "+m.getUser().getName())+" #"+m.getUser().getDiscriminator();
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package commands;
|
||||
|
||||
import chat.Bot;
|
||||
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
|
||||
public abstract class Command {
|
||||
|
|
@ -2,17 +2,20 @@ package commands;
|
|||
|
||||
import appguru.Main;
|
||||
import chat.Bot;
|
||||
import net.dv8tion.jda.core.EmbedBuilder;
|
||||
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class StatusCommand extends Command {
|
||||
|
||||
@Override
|
||||
public int getMinArgs() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxArgs() {
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="bin/main" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="test"/>
|
||||
<attribute name="gradle_used_by_scope" value="test"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>MinetestChatBridgeIRCBot</name>
|
||||
<comment>Project MinetestChatBridgeIRCBot created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,13 @@
|
|||
arguments=
|
||||
auto.sync=false
|
||||
build.scans.enabled=false
|
||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(5.6.1))
|
||||
connection.project.dir=
|
||||
eclipse.preferences.version=1
|
||||
gradle.user.home=
|
||||
java.home=
|
||||
jvm.arguments=
|
||||
offline.mode=false
|
||||
override.workspace.settings=true
|
||||
show.console.view=true
|
||||
show.executions.view=true
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,25 @@
|
|||
apply plugin: 'java'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'application'
|
||||
|
||||
|
||||
description = 'Minetest Chat Bridge IRC Bot'
|
||||
|
||||
mainClassName = 'appguru.Main'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Created-By': "Gradle ${gradle.gradleVersion}",
|
||||
'Main-Class': "appguru.Main"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1 @@
|
|||
rootProject.name = 'MinetestChatBridgeIRCBot'
|
|
@ -1,6 +1,8 @@
|
|||
package appguru;
|
||||
|
||||
import bridge.FileBridge;
|
||||
import bridge.ProcessBridge;
|
||||
import bridge.SocketBridge;
|
||||
import commands.Command;
|
||||
import commands.InfoCommand;
|
||||
import handlers.NumericTimeoutResponseHandler;
|
||||
|
@ -24,11 +26,13 @@ public class Main {
|
|||
public static String IRC_PREFIX="?";
|
||||
public static String PREFIX="!";
|
||||
|
||||
public static long PING_WAIT=20000; //20s
|
||||
public static long PING_WAIT=5000; //5s
|
||||
|
||||
public static PrintStream OUT = System.out;
|
||||
|
||||
public static IRCBot chat_bridge;
|
||||
public static IRCBot CHAT_BRIDGE;
|
||||
|
||||
public static ProcessBridge PROCESS_BRIDGE;
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
String project_url="https://github.com/appgurueu/adv_chat";
|
||||
|
@ -72,20 +76,23 @@ public class Main {
|
|||
String nickname=args[3];
|
||||
String channelname=args[4];
|
||||
|
||||
File in=new File(args[5]);
|
||||
File out=new File(args[6]);
|
||||
if (!in.isFile() || !out.isFile() || !in.canWrite() || !in.canRead() || !out.canWrite() || !out.canRead()) {
|
||||
OUT.println("ERR: Input or output files do not exist or can't be read/written.");
|
||||
System.exit(0);
|
||||
if (args[5].length() == 0) {
|
||||
int socket_port=Integer.parseInt(args[6]);
|
||||
PROCESS_BRIDGE=new SocketBridge("localhost", socket_port);
|
||||
} else {
|
||||
File in=new File(args[5]);
|
||||
File out=new File(args[6]);
|
||||
if (!in.isFile() || !out.isFile() || !in.canWrite() || !in.canRead() || !out.canWrite() || !out.canRead()) {
|
||||
OUT.println("ERR: Input or output files do not exist or can't be read/written.");
|
||||
System.exit(0);
|
||||
}
|
||||
PROCESS_BRIDGE=new FileBridge(in, out);
|
||||
}
|
||||
|
||||
/* Open Process Bridge */
|
||||
ProcessBridge pb=new ProcessBridge(in, out);
|
||||
|
||||
/* Create IRC Bot */
|
||||
chat_bridge=new IRCBot(port, network, ssl.equals("true"));
|
||||
CHAT_BRIDGE=new IRCBot(port, network, ssl.equals("true"));
|
||||
|
||||
chat_bridge.commands.put("PRIVMSG", (bot, tags, source, params) -> {
|
||||
CHAT_BRIDGE.commands.put("PRIVMSG", (bot, tags, source, params) -> {
|
||||
if (source == null || params.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
@ -130,13 +137,13 @@ public class Main {
|
|||
mentionstring+=",irc";
|
||||
}
|
||||
}
|
||||
pb.write((params.get(0).startsWith("#") ? "[CGM]":"[GMS]")+nick+" "+mentionstring+" "+msg_content);
|
||||
PROCESS_BRIDGE.write((params.get(0).startsWith("#") ? "[CGM]":"[GMS]")+nick+" "+mentionstring+" "+msg_content);
|
||||
} else {
|
||||
String command="PRIVMSG "+nick+" :No message given. Use '@mentions message'.";
|
||||
try {
|
||||
chat_bridge.send(command, new TryAgainHandler(command));
|
||||
CHAT_BRIDGE.send(command, new TryAgainHandler(command));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -146,12 +153,12 @@ public class Main {
|
|||
if (commandname_and_params[0].length() == 0) {
|
||||
String reply="PRIVMSG "+nick+" :No commandname given !";
|
||||
try {
|
||||
chat_bridge.send(reply, new TryAgainHandler(command));
|
||||
CHAT_BRIDGE.send(reply, new TryAgainHandler(command));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
} else {
|
||||
pb.write("[CMD]"+nick+" "+String.join(" ", commandname_and_params)+(commandname_and_params.length == 1 ? " ":""));
|
||||
PROCESS_BRIDGE.write("[CMD]"+nick+" "+String.join(" ", commandname_and_params)+(commandname_and_params.length == 1 ? " ":""));
|
||||
}
|
||||
return;
|
||||
} else if (params.get(1).startsWith(Main.IRC_PREFIX)) {
|
||||
|
@ -180,13 +187,13 @@ public class Main {
|
|||
return;
|
||||
}
|
||||
if (params.get(0).startsWith("#")) {
|
||||
pb.write("[MSG]"+nick+" "+params.get(1));
|
||||
PROCESS_BRIDGE.write("[MSG]"+nick+" "+params.get(1));
|
||||
} else {
|
||||
bot.sendTryAgain("PRIVMSG "+nick+" :I can only deliver your message if you use '@mentions'.");
|
||||
}
|
||||
});
|
||||
|
||||
chat_bridge.commands.put("JOIN", (bot, tags, source, params) -> {
|
||||
CHAT_BRIDGE.commands.put("JOIN", (bot, tags, source, params) -> {
|
||||
if (source == null || params.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -200,42 +207,42 @@ public class Main {
|
|||
for (byte b=0; b < colorstring.length()-6; b++) {
|
||||
colorstring="0"+colorstring;
|
||||
}
|
||||
pb.write("[JOI]"+nick+" #"+colorstring+" "+channelname);
|
||||
PROCESS_BRIDGE.write("[JOI]"+nick+" #"+colorstring+" "+channelname);
|
||||
});
|
||||
|
||||
chat_bridge.commands.put("NICK", (bot, tags, source, params) -> {
|
||||
CHAT_BRIDGE.commands.put("NICK", (bot, tags, source, params) -> {
|
||||
if (source == null || params.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int indexOf=source.indexOf('!');
|
||||
String nick=indexOf >= 0 ? source.substring(0, indexOf):source;
|
||||
pb.write("[NCK]"+nick+" "+params.get(0));
|
||||
PROCESS_BRIDGE.write("[NCK]"+nick+" "+params.get(0));
|
||||
});
|
||||
|
||||
chat_bridge.commands.put("QUIT", (bot, tags, source, params) -> {
|
||||
CHAT_BRIDGE.commands.put("QUIT", (bot, tags, source, params) -> {
|
||||
if (source == null || params.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int indexOf=source.indexOf('!');
|
||||
String nick=indexOf >= 0 ? source.substring(0, indexOf):source;
|
||||
pb.write("[EXT]"+nick+" "+(params.size() >= 2 ? params.get(1):"no reason"));
|
||||
PROCESS_BRIDGE.write("[EXT]"+nick+" "+(params.size() >= 2 ? params.get(1):"no reason"));
|
||||
});
|
||||
|
||||
chat_bridge.commands.put("PART", (bot, tags, source, params) -> {
|
||||
CHAT_BRIDGE.commands.put("PART", (bot, tags, source, params) -> {
|
||||
if (source == null || params.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int indexOf=source.indexOf('!');
|
||||
String nick=indexOf >= 0 ? source.substring(0, indexOf):source;
|
||||
pb.write("[BYE]"+nick+" "+(params.size() >= 2 ? params.get(1):"no reason"));
|
||||
PROCESS_BRIDGE.write("[BYE]"+nick+" "+(params.size() >= 2 ? params.get(1):"no reason"));
|
||||
});
|
||||
|
||||
// TODO Probably Ident & SASL negotiation ?
|
||||
//chat_bridge.send("CAP LS 302");
|
||||
chat_bridge.send("NICK "+nickname);
|
||||
chat_bridge.send("USER Minetest null null :Minetest Chat Bridge"); // 0 *
|
||||
CHAT_BRIDGE.send("NICK "+nickname);
|
||||
CHAT_BRIDGE.send("USER Minetest null null :Minetest Chat Bridge"); // 0 *
|
||||
|
||||
chat_bridge.send("JOIN "+channelname, new NumericTimeoutResponseHandler(20000) {
|
||||
CHAT_BRIDGE.send("JOIN "+channelname, new NumericTimeoutResponseHandler(20000) {
|
||||
@Override
|
||||
public HandledResponse handleNumeric(IRCBot bot, Numeric num, List<String> params) {
|
||||
switch (num) {
|
||||
|
@ -256,7 +263,7 @@ public class Main {
|
|||
for (byte b=0; b < colorstring.length()-6; b++) {
|
||||
colorstring="0"+colorstring;
|
||||
}
|
||||
pb.write("[JOI]"+nick+" #"+colorstring+" "+channelname);
|
||||
PROCESS_BRIDGE.write("[JOI]"+nick+" #"+colorstring+" "+channelname);
|
||||
}
|
||||
return HandledResponse.BREAK;
|
||||
case RPL_ENDOFNAMES:
|
||||
|
@ -282,7 +289,7 @@ public class Main {
|
|||
};
|
||||
}
|
||||
};
|
||||
chat_bridge.chatcommands.put("help", help_command);
|
||||
CHAT_BRIDGE.chatcommands.put("help", help_command);
|
||||
|
||||
InfoCommand about_command=new InfoCommand() {
|
||||
@Override
|
||||
|
@ -292,7 +299,7 @@ public class Main {
|
|||
};
|
||||
}
|
||||
};
|
||||
chat_bridge.chatcommands.put("about", about_command);
|
||||
CHAT_BRIDGE.chatcommands.put("about", about_command);
|
||||
|
||||
InfoCommand status_command=new InfoCommand() {
|
||||
@Override
|
||||
|
@ -308,31 +315,31 @@ public class Main {
|
|||
};
|
||||
}
|
||||
};
|
||||
chat_bridge.chatcommands.put("status", status_command);
|
||||
CHAT_BRIDGE.chatcommands.put("status", status_command);
|
||||
|
||||
OUT.println("INFO: Starting client");
|
||||
Main.STARTED_AT=System.currentTimeMillis();
|
||||
chat_bridge.listen();
|
||||
CHAT_BRIDGE.listen();
|
||||
|
||||
OUT.println("INFO: Starting server");
|
||||
pb.serve();
|
||||
PROCESS_BRIDGE.serve();
|
||||
OUT.println("INFO: Starting listener");
|
||||
pb.listen(line -> {
|
||||
PROCESS_BRIDGE.listen(line -> {
|
||||
if (line.startsWith("[MSG]")) {
|
||||
try {
|
||||
String command="PRIVMSG #mtchatbridgetest :"+line.substring(5);
|
||||
chat_bridge.send(command, new TryAgainHandler(command));
|
||||
CHAT_BRIDGE.send(command, new TryAgainHandler(command));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
} else if (line.startsWith("[PMS]")) { // GMS = PMS with comma separated list of targets
|
||||
String line_content=line.substring(5);
|
||||
String[] parts=line_content.split(" ", 2);
|
||||
String command="PRIVMSG "+parts[0]+" :"+parts[1];
|
||||
try {
|
||||
chat_bridge.send(command, new TryAgainHandler(command));
|
||||
CHAT_BRIDGE.send(command, new TryAgainHandler(command));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
/home/lars/.minetest/mods/adv_chat/MinetestChatBridgeBot/src/main/java/bridge/FileBridge.java
|
|
@ -0,0 +1,25 @@
|
|||
package bridge;
|
||||
|
||||
import appguru.Main;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lars
|
||||
*/
|
||||
public class IRCFileBridge extends FileBridge {
|
||||
|
||||
public IRCFileBridge(File in, File out) throws IOException {
|
||||
super(in, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kill(String reason) {
|
||||
super.kill(reason);
|
||||
|
||||
Main.CHAT_BRIDGE.shutdown(reason);
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package bridge;
|
||||
|
||||
import appguru.Main;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lars
|
||||
*/
|
||||
public class IRCSocketBridge extends SocketBridge {
|
||||
public IRCSocketBridge(String host, int port) throws IOException {
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kill(String reason) {
|
||||
super.kill(reason);
|
||||
|
||||
Main.CHAT_BRIDGE.shutdown(reason);
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
/home/lars/.minetest/mods/adv_chat/MinetestChatBridgeBot/src/main/java/bridge/ProcessBridge.java
|
|
@ -0,0 +1 @@
|
|||
/home/lars/.minetest/mods/adv_chat/MinetestChatBridgeBot/src/main/java/bridge/SocketBridge.java
|
|
@ -10,6 +10,8 @@ import java.net.Socket;
|
|||
import java.util.*;
|
||||
|
||||
import static irc.HandledResponse.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class IRCBot {
|
||||
public Map<String, commands.Command> chatcommands;
|
||||
|
@ -39,7 +41,7 @@ public class IRCBot {
|
|||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
@ -158,6 +160,16 @@ public class IRCBot {
|
|||
}
|
||||
public void listen() {
|
||||
Thread listenerThread = new Thread(() -> {
|
||||
BufferedReader reader=null;
|
||||
try {
|
||||
reader=new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(Main.OUT);
|
||||
if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
|
||||
Main.PROCESS_BRIDGE.kill("Socket connection lost");
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(20);
|
||||
|
@ -165,34 +177,29 @@ public class IRCBot {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
String message = "";
|
||||
int c = socket.getInputStream().read();
|
||||
while (c >= 0) {
|
||||
if (c == '\n' && message.charAt(message.length() - 1) == '\r') {
|
||||
// message completed !
|
||||
message = message.substring(0, message.length() - 1);
|
||||
try {
|
||||
processMessage(message);
|
||||
} catch (InvalidMessageException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
message = "";
|
||||
} else {
|
||||
message += (char) c;
|
||||
String message;
|
||||
while ((message = reader.readLine()) != null) {
|
||||
try {
|
||||
processMessage(message);
|
||||
} catch (InvalidMessageException e) {
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
c = socket.getInputStream().read();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
|
||||
Main.PROCESS_BRIDGE.kill("Socket connection lost");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
listenerThread.start();
|
||||
}
|
||||
public void send(String message) throws IOException {
|
||||
for (int i = 0; i < message.length(); i++) {
|
||||
socket.getOutputStream().write(message.charAt(i));
|
||||
if (socket.isClosed() || socket.isOutputShutdown()) {
|
||||
Main.PROCESS_BRIDGE.kill("Socket connection lost");
|
||||
}
|
||||
socket.getOutputStream().write(message.getBytes("UTF-8"));
|
||||
socket.getOutputStream().write('\r');
|
||||
socket.getOutputStream().write('\n');
|
||||
}
|
||||
|
@ -204,7 +211,21 @@ public class IRCBot {
|
|||
try {
|
||||
send(command, new TryAgainHandler(command));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
public void shutdown(String reason) {
|
||||
try {
|
||||
this.send("QUIT :" + reason);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
if (!this.socket.isClosed()) {
|
||||
try {
|
||||
this.socket.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(Main.OUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
Readme.md
52
Readme.md
|
@ -1,9 +1,9 @@
|
|||
# Advanced Chat(`adv_chat`)
|
||||
# Advanced Chat (`adv_chat`)
|
||||
|
||||
> One Mod to rule them all, One Mod to find them,
|
||||
> One Mod to bring them all, and in the darkness bind them
|
||||
|
||||
\- adapted quote from "Lord of the Rings"
|
||||
With "all" other chat mods are meant.
|
||||
|
||||
Adds roles, colors, unicode, hud notifications, and chat bridges (IRC & discord).
|
||||
|
||||
|
@ -15,26 +15,56 @@ Depends on [`modlib`](https://github.com/appgurueu/modlib). Modlib has been upda
|
|||
|
||||
Code licensed under the GPLv3 (GNU Public License Version 3). Written by Lars Mueller alias LMD or appguru(eu).
|
||||
|
||||
## Terminology
|
||||
## Links
|
||||
|
||||
Chatter : Participant in chat, be it a Minetest player, IRC user, or Discord member
|
||||
Role : "Group" of chatters
|
||||
Targets/Mentions : Roles or chatters mentioned using `@`
|
||||
* [GitHub](https://github.com/appgurueu/voxelizer) - sources, issue tracking, contributing
|
||||
* [Discord](https://discord.gg/ysP74by) - discussion, chatting
|
||||
* [Minetest Forum](https://forum.minetest.net/viewtopic.php?f=9&t=22845) - (more organized) discussion
|
||||
* [ContentDB](https://content.minetest.net/packages/LMD/voxelizer/) - releases (downloading from GitHub is recommended)
|
||||
|
||||
## Setup
|
||||
|
||||
In order to properly use `adv_chat`, you'll have to meet the following prerequisites:
|
||||
|
||||
* `modlib` Minetest mod installed and enabled as hard dependency and additionally also `cmdlib` (recommended)
|
||||
* `adv_chat` needs to be installed, enabled and added to the trusted mods in settings/`minetest.conf`
|
||||
* [LuaSocket](https://luarocks.org/modules/luasocket/luasocket) should be installed (`sudo luarocks install luasocket` on Ubuntu)
|
||||
* Complete [Java](https://www.java.com/de/) 8 or ideally newer installation under your system path (accessible from terminal via `java`)
|
||||
|
||||
Then just install it like any other mod and enjoy your greatly improved chat experience!
|
||||
|
||||
## Terminology
|
||||
|
||||
Chatter: Participant in chat, be it a Minetest player, IRC user, or Discord member
|
||||
Role: "Group" of chatters
|
||||
Targets/Mentions: Roles or chatters mentioned using `@`
|
||||
|
||||
## Features
|
||||
|
||||
* Discord & IRC chat bridges
|
||||
* Discord & IRC chat bridges, login & commands
|
||||
* Blocking
|
||||
* Colorization
|
||||
* Style preservation
|
||||
* Unicode
|
||||
* Mentions
|
||||
* HUD channels/notifications
|
||||
* Scheduled messages for offline players
|
||||
|
||||
## Changes
|
||||
|
||||
### 🎃 Halloween Update
|
||||
|
||||
* Proper formatting support
|
||||
* More configuration options
|
||||
* Remote login for chatcommand execution
|
||||
* Many under-the-hood changes cleaning up stuff & fixing bugs (improving the code & architecture)
|
||||
* See `config_help.md` and the sources for all details
|
||||
|
||||
## API
|
||||
|
||||
### HUD notifications
|
||||
|
||||
See the `hud_channels.lua` for how it works and `test.lua` for a score change demo running with random values.
|
||||
See `hud_channels.lua` for how it works and `test.lua` for a score change demo running with random values.
|
||||
|
||||
### Votes
|
||||
|
||||
|
@ -58,14 +88,14 @@ See the code and `config_help.md`. Feel free to contact me.
|
|||
|
||||
### Unicode support
|
||||
|
||||
This mod adds unicode support. Simply use the unicode codepoint in hexadecimal format prefixed by `U+`. To get a "slight smile" (🙂), you'd use `U+1F643`. Note that not all fonts fully support Unicode.
|
||||
This mod adds unicode support. Simply use the unicode codepoint in hexadecimal format prefixed by `U+`. To get a "slight smile" (🙂), you'd use `U+1F642`. Note that not all fonts fully support Unicode.
|
||||
Use the `/chat say` command to open a text entry field to paste text.
|
||||
|
||||
### Real-time chat
|
||||
|
||||
Use `@` at the beginning to message players or roles before your message.
|
||||
There are 3 special mentions : `minetest`, `irc` and `discord`.
|
||||
Can be separated by comma **&** whitespace. Examples :
|
||||
Can be separated by comma **&** whitespace. Examples:
|
||||
|
||||
* `@singleplayer hi, singleplayer !` - message `hi, singleplayer !` is sent to singleplayer
|
||||
* `lol(or whitespaces) @singleplayer hi` - message is just sent in global chat
|
||||
|
@ -103,7 +133,7 @@ Summarized, the Discord Chat Bridge works quite similar to the IRC one, with som
|
|||
Making Minetest & IRC chat compatible with Discord required the introduction of restrictions to simplify and reduce confusion.
|
||||
|
||||
* No double nicknames on Discord. If there are double nicknames, one of them gets an appendix, which is not guaranteed to be the same each time. So better make sure this doesn't happen.
|
||||
* Spaces (` `) in Discord nicknames are replaced by underscores (`_`)
|
||||
* Spaces (` `) and commata (`,`) in Discord nicknames are replaced by underscores (`_`)
|
||||
|
||||
### Internal process bridge protocol
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
minetest.original_get_player_privs = minetest.get_player_privs
|
||||
function minetest.get_player_privs(playername)
|
||||
if chatters[playername] then
|
||||
return {chatter=true}
|
||||
end
|
||||
return minetest.original_get_player_privs(playername)
|
||||
end
|
||||
|
||||
if cmd_ext then
|
||||
function call_chatcommand(chatter, call)
|
||||
local last_space, next_space = 1, call:find(" ")
|
||||
local command_trie, command_name = cmd_ext.chatcommands
|
||||
local cmd, suggestion
|
||||
local total_command_name = {}
|
||||
repeat
|
||||
next_space = next_space or call:len()+1
|
||||
command_name = call:sub(last_space, next_space-1)
|
||||
table.insert(total_command_name, command_name)
|
||||
local concat = table.concat(total_command_name, " ")
|
||||
if bridges.command_blacklist[total_command_name] then
|
||||
return false, "Command only available from Minetest."
|
||||
end
|
||||
total_command_name = {concat}
|
||||
if command_name == "" and cmd and not cmd.params then break end
|
||||
cmd, suggestion, _ = trie.search(command_trie, command_name)
|
||||
if not cmd then
|
||||
return false, "No such chatcommand."..((suggestion and " Did you mean \""..call:sub(0, last_space-1)..suggestion.."\" ?") or "")
|
||||
elseif cmd.subcommands and not cmd.implicit_call then
|
||||
command_trie = cmd.subcommands
|
||||
last_space, next_space = next_space + 1, call:find(" ", next_space+1)
|
||||
else
|
||||
last_space = next_space + 1
|
||||
break
|
||||
end
|
||||
until next_space == call:len()
|
||||
local params = call:sub(last_space)
|
||||
if cmd.privs and cmd.privs.chatter then
|
||||
return cmd.func(chatter, params)
|
||||
end
|
||||
return cmd.func((chatters[chatter] and chatters[chatter].login) or chatter, params)
|
||||
end
|
||||
else
|
||||
function call_chatcommand(chatter, call)
|
||||
local name, params = unpack(string_ext.split(call, " ", 2))
|
||||
if bridges.command_blacklist[name] then
|
||||
return false, "Command only available from Minetest."
|
||||
end
|
||||
local command = minetest.registered_chatcommands[name]
|
||||
if not command then
|
||||
return false, "No such chatcommand."
|
||||
end
|
||||
local privs = minetest.get_player_privs(chatter)
|
||||
local to_lose, to_gain = {}, {}
|
||||
for priv, val in pairs(command.privs) do
|
||||
if val ~= privs[val] then
|
||||
table.insert((val and to_gain) or to_lose, priv)
|
||||
end
|
||||
end
|
||||
if #to_lose ~= 0 or #to_gain ~= 0 then
|
||||
if #to_lose == 0 then
|
||||
return false, "Missing privileges : "..table.concat(to_gain, ", ")
|
||||
end
|
||||
if #to_gain == 0 then
|
||||
return false, "Privileges to be lost : "..table.concat(to_lose, ", ")
|
||||
end
|
||||
return false, "Missing privileges : "..table.concat(to_gain, ", ")..", privileges to be lost : "..table.concat(to_lose, ", ")
|
||||
end
|
||||
if cmd.privs.chatter then
|
||||
return cmd.func(chatter, params)
|
||||
end
|
||||
return command.func((chatters[chatter] and chatters[chatter].login) or chatter, params)
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
/home/lars/.minetest/mods/voxelizer/closest_color.lua
|
|
@ -1,35 +1,39 @@
|
|||
-- Converts "#XXXXXX" color codes to colors
|
||||
function colorize_message(message)
|
||||
local rope={}
|
||||
--IFNDEF bridge
|
||||
--IFNDEF discord
|
||||
local otherrope={}
|
||||
--ENDIF
|
||||
local last_index=1
|
||||
for i=1, string.len(message) do
|
||||
local c=string.byte(message:sub(i,i))
|
||||
if c == hashtag then
|
||||
local i=1
|
||||
while i <= message:len() do
|
||||
local c=message:sub(i,i)
|
||||
if c == string.char(0x1b) and message:sub(i+1, i+4) == "(c@#" and message:sub(i+11, i+11) == ")" then
|
||||
table.insert(rope, message:sub(i, i+11))
|
||||
i=i+11
|
||||
goto continue
|
||||
elseif c == "#" then
|
||||
for j=i+1, i+6 do
|
||||
local c2=string.byte(string.upper(message:sub(j,j)))
|
||||
if c2:len() == 0 or not string_ext.is_hexadecimal(c2) then
|
||||
i=j
|
||||
local c2=message:sub(j,j):upper()
|
||||
if c2=="" or not ((c2 >= "0" and c2 <= "9") or (c2 >= "A" and c2 <= "F")) then
|
||||
goto nocolor
|
||||
end
|
||||
end
|
||||
local colorstring=minetest.get_color_escape_sequence(string.sub(message, i, i+6))
|
||||
table.insert(rope, message:sub(last_index, i-1))
|
||||
--IFNDEF bridge
|
||||
table.insert(otherrope, message:sub(last_index, i-1))
|
||||
--ENDIF
|
||||
table.insert(rope, colorstring)
|
||||
last_index=i+7
|
||||
::nocolor::
|
||||
table.insert(rope, minetest.get_color_escape_sequence(message:sub(i, i+6)))
|
||||
i=i+6
|
||||
goto continue
|
||||
end
|
||||
::nocolor::
|
||||
table.insert(rope, c)
|
||||
--IFNDEF discord
|
||||
table.insert(otherrope, c)
|
||||
--ENDIF
|
||||
::continue::
|
||||
i=i+1
|
||||
end
|
||||
table.insert(rope, message:sub(last_index))
|
||||
--IFNDEF bridge
|
||||
table.insert(otherrope, message:sub(last_index))
|
||||
return table.concat(rope)
|
||||
--IFNDEF discord
|
||||
, table.concat(otherrope)
|
||||
--ENDIF
|
||||
return table.concat(rope, "")
|
||||
--IFNDEF bridge
|
||||
, table.concat(otherrope, "")
|
||||
--ENDIF
|
||||
end
|
||||
end
|
||||
|
||||
load_schemes()
|
92
conf.lua
92
conf.lua
|
@ -1,25 +1,36 @@
|
|||
local schemedef={mention_prefix={type="string"},
|
||||
mention_delim={type="string"},
|
||||
delim={type="string"}}
|
||||
local schemedef={
|
||||
message_prefix={type="string"},
|
||||
mention_prefix={type="string"},
|
||||
mention_delim={type="string"},
|
||||
content_prefix={type="string"},
|
||||
message_suffix={type="string"},
|
||||
}
|
||||
|
||||
local conf_spec={type="table", children={
|
||||
scheme={type="table", required_children={
|
||||
schemes={type="table", required_children={
|
||||
minetest=schemedef
|
||||
}, possible_children={
|
||||
other=schemedef
|
||||
irc=schemedef,
|
||||
discord=schemedef
|
||||
}},
|
||||
bridges={
|
||||
type="table",
|
||||
possible_children={
|
||||
irc={type="table", children={
|
||||
port={type="number", range={0, 65535}},
|
||||
network={type="string"},
|
||||
nickname={type="string"},
|
||||
channelname={type="string"},
|
||||
ssl={type="boolean"},
|
||||
prefix={type="string"},
|
||||
minetest_prefix={type="string"}
|
||||
}},
|
||||
irc={type="table", required_children={
|
||||
port={type="number", range={0, 65535}},
|
||||
network={type="string"},
|
||||
nickname={type="string"},
|
||||
channelname={type="string"},
|
||||
ssl={type="boolean"},
|
||||
prefix={type="string"},
|
||||
minetest_prefix={type="string"}
|
||||
},
|
||||
possible_children={
|
||||
bridge={type="string", possible_values={"files", "sockets"}},
|
||||
convert_minetest_colors={type="string", possible_values={"disabled", "hex", "safer", "safest"}},
|
||||
handle_discord_markdown={type="boolean"},
|
||||
handle_minetest_markdown={type="boolean"}
|
||||
}},
|
||||
discord={type="table", required_children={
|
||||
token={type="string"},
|
||||
channelname={type="string"},
|
||||
|
@ -29,9 +40,15 @@ local conf_spec={type="table", children={
|
|||
possible_children={
|
||||
blacklist={type="table", keys={type="string"}},
|
||||
whitelist={type="table", keys={type="string"}},
|
||||
guild_id={type="string"}
|
||||
guild_id={type="string"},
|
||||
bridge={type="string", possible_values={"files", "sockets"}},
|
||||
convert_internal_markdown={type="boolean"},
|
||||
convert_minetest_markdown={type="boolean"},
|
||||
handle_irc_styles={type="string", possible_values={"escape_markdown", "convert", "disabled"}}
|
||||
}
|
||||
}
|
||||
},
|
||||
command_blacklist={type="table", keys={type="number"}, values={type="string"}},
|
||||
command_whitelist={type="table", keys={type="number"}, values={type="string"}}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -39,6 +56,30 @@ local conf_spec={type="table", children={
|
|||
local config=conf.import("adv_chat", conf_spec)
|
||||
table_ext.add_all(getfenv(1), config)
|
||||
|
||||
function load_schemes()
|
||||
for k, v in pairs(schemes.minetest) do
|
||||
schemes.minetest[k] = colorize_message(v)
|
||||
end
|
||||
|
||||
for _,s in pairs({"irc", "discord"}) do
|
||||
if not schemes[s] then
|
||||
schemes[s] = {}
|
||||
for k, v in pairs(schemes.minetest) do
|
||||
schemes[s][k] = minetest.strip_colors(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
load_schemes = nil
|
||||
end
|
||||
|
||||
if not bridges.irc.style_conversion then
|
||||
bridges.irc.style_conversion={}
|
||||
if not bridges.irc.style_conversion.colors then
|
||||
bridges.irc.style_conversion.colors="disabled"
|
||||
end
|
||||
end
|
||||
|
||||
if bridges.discord then
|
||||
|
||||
local blacklist_empty=table_ext.is_empty(bridges.discord.blacklist or {})
|
||||
|
@ -58,4 +99,23 @@ if bridges.discord then
|
|||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if bridges.discord or bridges.irc then
|
||||
|
||||
bridges.command_blacklist = table_ext.set(bridges.command_blacklist or {})
|
||||
bridges.command_whitelist = table_ext.set(bridges.command_whitelist or {})
|
||||
local blacklist_empty=table_ext.is_empty(bridges.command_blacklist)
|
||||
local whitelist_empty=table_ext.is_empty(bridges.command_whitelist or {})
|
||||
if blacklist_empty then
|
||||
if not whitelist_empty then
|
||||
bridges.command_blacklist=setmetatable(bridges.command_blacklist, {__index=function(value)
|
||||
if bridges.command_whitelist[value] then
|
||||
return nil
|
||||
end
|
||||
return true
|
||||
end})
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -11,14 +11,15 @@ Explaining document(this, Markdown) : `<modpath/gamepath>/adv_chat/config_help.m
|
|||
Readme : `<modpath/gamepath>/adv_chat/Readme.md`
|
||||
|
||||
## Default Configuration
|
||||
|
||||
Located under `<modpath/gamepath>/adv_chat/default_config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"scheme" : {
|
||||
"minetest" : {"mention_prefix":"#FFFF00@", "mention_delim":"#FFFF00, ", "delim":"#FFFF00 : "},
|
||||
"schemes" : {
|
||||
"minetest" : {"message_prefix": "", "mention_prefix": "#FFFF00@", "mention_delim": "#FFFF00, ", "content_prefix": "#FFFF00: #FFFFFF"},
|
||||
"other" : null
|
||||
},
|
||||
|
||||
"bridges" : {
|
||||
"discord" : null,
|
||||
"irc" : null
|
||||
|
@ -27,13 +28,13 @@ Located under `<modpath/gamepath>/adv_chat/default_config.json`
|
|||
```
|
||||
|
||||
## Example Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"scheme" : {
|
||||
"minetest" : {"mention_prefix":"#FFFF00@", "mention_delim":"#FFFF00, ", "delim":"#FFFF00 : "},
|
||||
"schemes" : {
|
||||
"minetest" : {"message_prefix": "Somebody - namely ", "mention_prefix": "#FFFF00 - wrote to ", "mention_delim": "#FFFF00 and ", "content_prefix": "#FFFF00: #FFFFFF", "message_suffix": " :D"},
|
||||
"other" : null
|
||||
},
|
||||
|
||||
"bridges" : {
|
||||
"discord" : {"channelname":"allgemein", "prefix": "?", "minetest_prefix": "!","token":"S.U.Pxxs.E.R.T.9998OKEN", "blacklist":{"~~new_role~~":true}, "guild_id": 580416319703351296},
|
||||
"irc" : {"channelname":"#mtchatbridgetest", "prefix": "?", "minetest_prefix": "!", "nickname": "MT_Chat_Bridge", "network": "irc.freenode.net", "port": 7000, "ssl": true}
|
||||
|
@ -44,28 +45,40 @@ Located under `<modpath/gamepath>/adv_chat/default_config.json`
|
|||
|
||||
## Usage
|
||||
|
||||
### `scheme`
|
||||
### `schemes`
|
||||
|
||||
Specifies the chat message format, `minetest` is for the one used on the Minetest chat, and `other` is for Discord/IRC.
|
||||
Specifies the chat message format, `minetest` is for the one used on the Minetest chat, `irc` is IRC, and `discord` for Discord.
|
||||
|
||||
* `message_prefix` - Prefix for the message
|
||||
* `mention_prefix` - Prefix for mentionpart.
|
||||
* `mention_delim` - Mention delimiter.
|
||||
* `delim` - Message/sendername delimiter.
|
||||
If you want to use color escape sequences, type `\x1B(c@#66FF00)`, and replace `#66FF00` with your color of choice in hex format.
|
||||
* `content_prefix` - Message/sendername delimiter.
|
||||
* `message_suffix` - Suffix for the message
|
||||
|
||||
Messages are formatted as `sendername + mention_prefix + {mentions, mention_delim} + delim + message`
|
||||
If you want to use color escape sequences, type something like `#66FF00 colorized text here`, and replace `#66FF00` with your color of choice in hex format.
|
||||
|
||||
Messages are formatted as `message_prefix + sendername + mention_prefix + {mentions, mention_delim} + delim + message + message_suffix`
|
||||
|
||||
### `bridges`
|
||||
|
||||
Configuration for IRC/Discord chat bridges. If `irc` or `discord` are set to `false` or `null`, the corresponding chat bridges aren't created.
|
||||
|
||||
#### `discord`
|
||||
Table with the following entries :
|
||||
* `token` : Discord bot token, required
|
||||
* `channelname` : Name of bridge channel, required as well
|
||||
* `prefix`, `minetest_prefix` : Prefixes for Discord/Minetest commands, required
|
||||
* `role_blacklist`/`role_whitelist` : Blacklist/whitelist of Discord roles. If both or none are set, Discord roles are ignored.
|
||||
* `guild_id` : Guild ID, string. If swines add your bot to other servers, force it to use the server with the specified Guild ID. Optional. If unset, bot will use the guild it joined first.
|
||||
|
||||
Example :
|
||||
Table with the following entries :
|
||||
|
||||
* `token`: Discord bot token, required
|
||||
* `channelname`: Name of bridge channel, required as well
|
||||
* `prefix`, `minetest_prefix`: Prefixes for Discord/Minetest commands, required
|
||||
* `role_blacklist`/`role_whitelist`: Blacklist/whitelist of Discord roles. If both or none are set, Discord roles are ignored.
|
||||
* `guild_id`: Guild ID, string. If swines add your bot to other servers, force it to use the server with the specified Guild ID. Optional. If unset, bot will use the guild it joined first.
|
||||
* `bridge`: Optional. Forces type of process bridge to use. Choices are `"file"` and `"socket"`. Sockets are recommended but require `luasocket`.
|
||||
* `convert_internal_markdown`/`convert_minetest_markdown`: Optional boolean. Whether Markdown sent from Minetest/internal chat messages should be left untouched as if it was Discord Markdown
|
||||
* `handle_irc_styles`: Optional string. How IRC styles should be converted to Discord Markdown. Possible values: `"disabled"`, `"escape_markdown"` and `"convert"`
|
||||
* `strip_discord_markdown_in_minetest`: Optional boolean. Whether Discord Markdown should be stripped from Minetest chat.
|
||||
|
||||
Example :
|
||||
|
||||
```json
|
||||
{
|
||||
"discord": {
|
||||
|
@ -80,22 +93,33 @@ Example :
|
|||
```
|
||||
|
||||
#### `irc`
|
||||
Table with fields (all required) :
|
||||
* `network` : IRC network, for example `irc.freenode.net`
|
||||
* `port` : Port, on [Freenode](https://freenode.net/kb/answer/chat) it would be `7000` if SSL is used, or else `6667`. Just google "connecting to network" for your IRC network of choice to get detailed information.
|
||||
* `ssl` : Whether to use encryption (SSL) to communicate with the IRC network. Setting this to `true` is recommended.
|
||||
* `nickname` : Bot nickname
|
||||
* `channelname` : IRC channel name, for example `#minetest-server`
|
||||
* `prefix`, `minetest_prefix` : Prefixes for IRC/Minetest commands, required
|
||||
|
||||
Example :
|
||||
Table with fields. Required are:
|
||||
|
||||
* `network`: IRC network, for example `irc.freenode.net`
|
||||
* `port`: Port, on [Freenode](https://freenode.net/kb/answer/chat) it would be `7000` if SSL is used, or else `6667`. Just google "connecting to network" for your IRC network of choice to get detailed information.
|
||||
* `ssl`: Whether to use encryption (SSL) to communicate with the IRC network. Setting this to `true` is recommended.
|
||||
* `nickname`: Bot nickname
|
||||
* `channelname`: IRC channel name, for example `#minetest-server`
|
||||
* `prefix`, `minetest_prefix`: Prefixes for IRC bot/Minetest chatcommands, required
|
||||
|
||||
Optional fields are:
|
||||
|
||||
* `bridge`: Type of process bridge to use can be forced here. Choices are `"file"` and `"socket"`. Sockets are recommended but require `luasocket`.
|
||||
* `convert_minetest_colors`: How colors from Minetest chat messages should be converted to IRC. Possible values are `"disabled"`, `"safest"`, `"safe"` and `"hex_safe"` and `"hex"`
|
||||
* `handle_internal_markdown`: How Markdown sent from internal MT should be converted to IRC text styles. Possible values are `"disabled"`, `"strip"` and `"convert"`
|
||||
* `handle_minetest_markdown`: How Markdown sent from Minetest should be converted to IRC text styles. Possible values are `"disabled"`, `"strip"` and `"convert"`
|
||||
* `handle_discord_markdown`: How Markdown sent from Discord should be converted to IRC text styles. Possible values are `"disabled"`, `"strip"` and `"convert"`
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"irc": {
|
||||
"prefix": "?",
|
||||
"minetest_prefix": "!",
|
||||
"channelname": "#minetest-server",
|
||||
"nickname": "SERVERNAME_Chat",
|
||||
"channelname": "#minetest-server",
|
||||
"nickname": "SERVERNAME_Chat",
|
||||
"port": 7000,
|
||||
"ssl": true,
|
||||
"network": "irc.freenode.net"
|
||||
|
@ -103,21 +127,29 @@ Example :
|
|||
}
|
||||
```
|
||||
|
||||
#### `chatcommand_whitelist`/`chatcommand_blacklist`
|
||||
|
||||
Whitelist/blacklist of chatcommands which are not available from Discord or IRC. If both or none are set, all chatcommands are blacklisted.
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Consistency
|
||||
|
||||
It is recommended to **keep consistency**. To do so, channel & chat bot names could be similar across Discord and IRC. The same goes for prefixes.
|
||||
|
||||
### Prefixes
|
||||
|
||||
You should try to keep prefixes similar and memorable, while ensuring that there are no collisions. I recommend the combination of `?` for Discord/IRC commands and `!` for Minetest commands.
|
||||
Other neat combinations I have thought of are `+` and `-`, or `;` and `:`. Keep in mind that prefixes should be easy to type as well, and that others might have a different keyboard layout.
|
||||
|
||||
### Discord Avatar
|
||||
|
||||
Pixel-art Minetest skin heads always work well as avatars. For an example look you could look at my [Robby-Head](https://github.com/appgurueu/artwork/blob/master/robbyhead.png).
|
||||
There are tons of skins out there and it's fairly easy to extract the faces (but make sure you don't violate the licenses when using the images).
|
||||
A good starting point is [Addis Open MT-Skin Database](http://minetest.fensta.bplaced.net/). You can, however, of course also design it yourself. Just grab your favorite pixel-art program and draw a 8x8 head.
|
||||
You should also make sure to scale the small image up (to at least 256x256), because else Discord scales it up "for you" which makes it lose it's sharp edges.
|
||||
|
||||
### Security
|
||||
Only two basic hints : Always enable SSL, and don't give your bot token to anyone.
|
||||
And of course make sure your server isn't hacked. Messages are sent as plain text over the file bridges.
|
||||
|
||||
Only two basic hints : Always enable SSL, and don't give your bot token to anyone.
|
||||
And of course make sure your server isn't hacked. Messages are sent as plain text over the sockets or file bridges.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"scheme" : {
|
||||
"minetest" : {"mention_prefix":"#FFFF00@", "mention_delim":"#FFFF00, ", "delim":"#FFFF00 : "},
|
||||
"schemes" : {
|
||||
"minetest" : {"message_prefix": "", "mention_prefix": "#FFFF00@", "mention_delim": "#FFFF00, ", "content_prefix": "#FFFF00: #FFFFFF"},
|
||||
"other" : null
|
||||
},
|
||||
|
||||
|
|
67
discord.lua
67
discord.lua
|
@ -17,20 +17,29 @@ function delete_discord_role(linecontent)
|
|||
end
|
||||
end
|
||||
|
||||
file_ext.process_bridge_build("discord")
|
||||
local bridge
|
||||
if bridges.discord.bridge == "files" then
|
||||
bridge = build_file_bridge("discord")
|
||||
else
|
||||
bridge = build_bridge("discord")
|
||||
end
|
||||
|
||||
file_ext.process_bridge_listen("discord", function(line)
|
||||
discord_bridge = bridge
|
||||
|
||||
bridge.listen(function(line)
|
||||
local linecontent=line:sub(6)
|
||||
if string_ext.starts_with(line, "[MSG]") then
|
||||
local parts=string_ext.split(linecontent, " ", 2)
|
||||
local src=parts[1].."[discord]"
|
||||
send_to_all(src, src..scheme.other.delim..parts[2], minetest.get_color_escape_sequence(get_color(src))..src..scheme.minetest.delim..parts[2], "discord")
|
||||
local adv_msg=message.new(chatters[src], nil, parts[2])
|
||||
adv_msg.sent_to="discord"
|
||||
send_to_all(adv_msg)
|
||||
elseif string_ext.starts_with(line, "[GMS]") or string_ext.starts_with(line, "[CGM]") then -- GMS = group message or CGM = channel group message
|
||||
local parts=string_ext.split(linecontent, " ",3)
|
||||
local source=parts[1]
|
||||
local targets=string_ext.split_without_limit(parts[2], ",")
|
||||
local msg=parts[3]
|
||||
local sent_to="nobody"
|
||||
local sent_to
|
||||
if string_ext.starts_with(line, "[CGM]") then
|
||||
sent_to="discord"
|
||||
end
|
||||
|
@ -38,39 +47,29 @@ file_ext.process_bridge_listen("discord", function(line)
|
|||
for _, target in ipairs(targets) do
|
||||
targetset[target]=true
|
||||
end
|
||||
local invalid_targets, msg, mt_msg=build_message(source.."[discord]", targets, msg)
|
||||
send_to_targets(source.."[discord]", table_ext.set(targets), msg, mt_msg, sent_to)
|
||||
if (#invalid_targets) == 1 then
|
||||
file_ext.process_bridge_write("discord", "[PMS]"..source.." The target "..invalid_targets[1].." is inexistant.")
|
||||
elseif (#invalid_targets) > 1 then
|
||||
file_ext.process_bridge_write("discord", "[PMS]"..source.." The targets "..table.concat(invalid_targets, ", ").." are inexistant.")
|
||||
local adv_msg=message.new(chatters[source.."[discord]"], targets, msg)
|
||||
adv_msg.sent_to=sent_to
|
||||
message.mentionpart(adv_msg) --force check mentions
|
||||
send_to_targets(adv_msg)
|
||||
if (#adv_msg.invalid_mentions) == 1 then
|
||||
discord_bridge.write("[PMS]#FFFFFF "..source.." The target "..adv_msg.invalid_mentions[1].." is inexistant.")
|
||||
elseif (#adv_msg.invalid_mentions) > 1 then
|
||||
discord_bridge.write("[PMS]#FFFFFF "..source.." The targets "..table.concat(adv_msg.invalid_mentions, ", ").." are inexistant.")
|
||||
end
|
||||
elseif string_ext.starts_with(line, "[CMD]") then
|
||||
local parts=string_ext.split(linecontent, " ", 3)
|
||||
local parts=string_ext.split(linecontent, " ", 2)
|
||||
local source=parts[1]
|
||||
local commandname=parts[2]
|
||||
local params=parts[3]
|
||||
local command=minetest.registered_chatcommands[commandname]
|
||||
if command then
|
||||
if not table_ext.is_empty(command.privs) then
|
||||
file_ext.process_bridge_write("discord", "[ERR]"..source.." Command requires privs.")
|
||||
else
|
||||
local success, retval = command.func(source.."[discord]", params or "")
|
||||
if success then
|
||||
file_ext.process_bridge_write("discord", "[SUC]"..source.." "..(retval or "No return value."))
|
||||
else
|
||||
file_ext.process_bridge_write("discord", "[ERR]"..source.." "..(retval or "No return value."))
|
||||
end
|
||||
end
|
||||
else
|
||||
file_ext.process_bridge_write("discord", "[ERR]"..source.."`"+commandname+"` : No such command.")
|
||||
end
|
||||
local call=parts[2]
|
||||
local success, retval = call_chatcommand(source.."[discord]", call)
|
||||
local prefix = "[PMS]#FFFFFF "
|
||||
if success then prefix = "[SUC]" elseif success == false then prefix = "[ERR]" end
|
||||
discord_bridge.write(prefix..source.." "..(retval or "No return value."))
|
||||
elseif string_ext.starts_with(line, "[JOI]") or string_ext.starts_with(line, "[LIS]") then
|
||||
local parts=string_ext.split(linecontent, " ", 2) --nick & roles
|
||||
local chatter=parts[1].."[discord]"
|
||||
join(chatter, {color=parts[2], roles={}, discord=true})
|
||||
if string_ext.starts_with(line, "[JOI]") then
|
||||
send_to_all("", get_color(chatter)..chatter..minetest.get_color_escape_sequence("#FFFFFF").." joined.")
|
||||
minetest.chat_send_all(get_color(chatter)..chatter..minetest.get_color_escape_sequence("#FFFFFF").." joined.")
|
||||
end
|
||||
elseif string_ext.starts_with(line, "[EXT]") then
|
||||
chatters[linecontent.."[discord]"]=nil
|
||||
|
@ -134,24 +133,24 @@ end)
|
|||
|
||||
-- Pinging
|
||||
mt_ext.register_globalstep(1, function()
|
||||
file_ext.process_bridge_write("discord", "[PIN]")
|
||||
bridge.write("[PIN]")
|
||||
end)
|
||||
|
||||
-- Killing on_shutdown
|
||||
minetest.register_on_shutdown(function()
|
||||
file_ext.process_bridge_write("discord", "[KIL]")
|
||||
bridge.write("[KIL]")
|
||||
end)
|
||||
|
||||
file_ext.process_bridge_serve("discord")
|
||||
bridge.serve()
|
||||
|
||||
-- Start AFTER mods are loaded, so that the player sees chat messages
|
||||
minetest.register_on_mods_loaded(function()
|
||||
local java="java"
|
||||
local classpath=minetest.get_modpath("adv_chat").."/minetest-chat-bridge-bot/out/production/classes:/home/lars/.gradle/caches/modules-2/files-2.1/net.dv8tion/JDA/3.7.1_388/f534ab5132d8df986e603a404120492d4cdf815e/JDA-3.7.1_388.jar:/home/lars/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/23.5-jre/e9ce4989adf6092a3dab6152860e93d989e8cf88/guava-23.5-jre.jar:/home/lars/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/lars/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/lars/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-collections4/4.1/a4cf4688fe1c7e3a63aa636cc96d013af537768e/commons-collections4-4.1.jar:/home/lars/.gradle/caches/modules-2/files-2.1/org.json/json/20160810/aca5eb39e2a12fddd6c472b240afe9ebea3a6733/json-20160810.jar:/home/lars/.gradle/caches/modules-2/files-2.1/net.sf.trove4j/trove4j/3.0.3/42ccaf4761f0dfdfa805c9e340d99a755907e2dd/trove4j-3.0.3.jar:/home/lars/.gradle/caches/modules-2/files-2.1/club.minnced/opus-java/1.0.2/c2e69f8d9aab5eab7476df8f5558e001657009bd/opus-java-1.0.2.jar:/home/lars/.gradle/caches/modules-2/files-2.1/com.neovisionaries/nv-websocket-client/2.4/da95dda351dba317468b08f8e5575216c05102/nv-websocket-client-2.4.jar:/home/lars/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.8.1/4d060ca3190df0eda4dc13415532a12e15ca5f11/okhttp-3.8.1.jar:/home/lars/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-qual/2.0.0/518929596ee3249127502a8573b2e008e2d51ed3/checker-qual-2.0.0.jar:/home/lars/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.0.18/5f65affce1684999e2f4024983835efc3504012e/error_prone_annotations-2.0.18.jar:/home/lars/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar:/home/lars/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.14/775b7e22fb10026eed3f86e8dc556dfafe35f2d5/animal-sniffer-annotations-1.14.jar:/home/lars/.gradle/caches/modules-2/files-2.1/club.minnced/opus-java-api/1.0.2/e6e5afd72b5305356ef6d3aa95e84790cd340828/opus-java-api-1.0.2.jar:/home/lars/.gradle/caches/modules-2/files-2.1/club.minnced/opus-java-natives/1.0.2/b62c0be7a49c9bf0933d003cc0418e90518db728/opus-java-natives-1.0.2.jar:/home/lars/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.13.0/a9283170b7305c8d92d25aff02a6ab7e45d06cbe/okio-1.13.0.jar:/home/lars/.gradle/caches/modules-2/files-2.1/net.java.dev.jna/jna/4.4.0/cb208278274bf12ebdb56c61bd7407e6f774d65a/jna-4.4.0.jar"
|
||||
local jarpath=minetest.get_modpath("adv_chat").."/MinetestChatBridgeBot/build/libs/MinetestChatBridgeBot-all.jar"
|
||||
local token=bridges.discord.token or "NTc4MjM0NjM5NTc2MDcyMjEx.XPgWKA.ilzmvz-I7XTIML6Emj1jBx4ejLw"
|
||||
local text_channel=bridges.discord.channelname
|
||||
local prefixes='"'..bridges.discord.minetest_prefix..'" "'..bridges.discord.prefix..'"'
|
||||
local guild_id=bridges.discord.guild_id.." " or ""
|
||||
|
||||
file_ext.process_bridge_start("discord", java..' -Dfile.encoding=UTF-8 -classpath "'..classpath..'" appguru.Main "'..token..'" "'..text_channel..'" "%s" "%s" "%s" '..prefixes.." "..guild_id.."&")
|
||||
bridge.start(java..' -jar "'..jarpath..'" "'..token..'" "'..text_channel..'" "%s" "%s" "%s" '..prefixes.." "..guild_id.." &")
|
||||
end)
|
47
init.lua
47
init.lua
|
@ -8,41 +8,38 @@ local bridge_ifndefs={
|
|||
irc=adv_chat.bridges.irc
|
||||
}
|
||||
|
||||
if not bridge_ifndefs.bridge then
|
||||
error("OOF")
|
||||
end
|
||||
|
||||
extend_mod_string("adv_chat", string_ext.handle_ifndefs(file_ext.read(get_resource("adv_chat", "colorize_message.lua")), bridge_ifndefs))
|
||||
|
||||
if bridge_ifndefs.bridge then
|
||||
adv_chat.scheme.other=adv_chat.scheme.other or {}
|
||||
for k, v in pairs(adv_chat.scheme.minetest) do
|
||||
local mt_msg, msg=adv_chat.colorize_message(v)
|
||||
adv_chat.scheme.minetest[k]=mt_msg
|
||||
if not adv_chat.scheme.other[k] then
|
||||
adv_chat.scheme.other[k]=msg
|
||||
end
|
||||
end
|
||||
else
|
||||
for k, v in pairs(adv_chat.scheme.minetest) do
|
||||
local mt_msg=adv_chat.colorize_message(v)
|
||||
adv_chat.scheme.minetest[k]=mt_msg
|
||||
end
|
||||
end
|
||||
|
||||
extend_mod_string("adv_chat", string_ext.handle_ifndefs(file_ext.read(get_resource("adv_chat", "main.lua")), bridge_ifndefs))
|
||||
|
||||
-- Basic API stuff
|
||||
extend_mod("adv_chat", "unicode")
|
||||
extend_mod("adv_chat", "closest_color")
|
||||
extend_mod("adv_chat", "trie")
|
||||
extend_mod("adv_chat", "text_styles")
|
||||
extend_mod("adv_chat", "message")
|
||||
extend_mod("adv_chat", "hud_channels")
|
||||
|
||||
-- Chat bridges
|
||||
if adv_chat.bridges.irc then
|
||||
extend_mod("adv_chat", "irc")
|
||||
end
|
||||
if bridge_ifndefs.bridge then
|
||||
extend_mod("adv_chat", "chatcommands")
|
||||
extend_mod("adv_chat", "process_bridges")
|
||||
|
||||
local env = minetest.request_insecure_environment() or error("Error: adv_chat needs to be added to the trusted mods for chat bridges to work. See the Readme for more info.")
|
||||
adv_chat.set_os_execute(env.os.execute)
|
||||
adv_chat.set_socket(env.require("socket"))
|
||||
|
||||
if adv_chat.bridges.discord then
|
||||
extend_mod("adv_chat", "discord")
|
||||
if adv_chat.bridges.irc then
|
||||
extend_mod("adv_chat", "irc")
|
||||
end
|
||||
|
||||
if adv_chat.bridges.discord then
|
||||
extend_mod("adv_chat", "discord")
|
||||
end
|
||||
|
||||
adv_chat.build_socket_bridge = nil
|
||||
adv_chat.build_file_bridge = nil
|
||||
adv_chat.build_bridge = nil
|
||||
end
|
||||
|
||||
-- Tests - don't uncomment unless you actually want to test something
|
||||
|
|
94
irc.lua
94
irc.lua
|
@ -1,102 +1,112 @@
|
|||
register_role("irc",{color="#FFFF66"})
|
||||
|
||||
file_ext.process_bridge_build("irc")
|
||||
local bridge
|
||||
if bridges.discord.bridge == "files" then
|
||||
bridge = build_file_bridge("irc")
|
||||
else
|
||||
bridge = build_bridge("irc")
|
||||
end
|
||||
|
||||
file_ext.process_bridge_listen("irc", function(line)
|
||||
irc_bridge = bridge
|
||||
|
||||
local color = function(chattername) return minetest.get_color_escape_sequence((chatters[chattername] and chatters[chattername].color) or "#FFFFFF") end
|
||||
|
||||
bridge.listen(function(line)
|
||||
local linecontent=line:sub(6)
|
||||
if string_ext.starts_with(line, "[MSG]") then
|
||||
local parts=string_ext.split(linecontent, " ", 2)
|
||||
local src=parts[1].."[irc]"
|
||||
send_to_all(src, src..scheme.other.delim..parts[2], minetest.get_color_escape_sequence(get_color(src))..src..scheme.minetest.delim..parts[2], "irc")
|
||||
local adv_msg=message.new(chatters[src], nil, parts[2])
|
||||
adv_msg.sent_to="irc"
|
||||
send_to_all(adv_msg)
|
||||
--send_to_all(src, src..scheme.other.delim..parts[2], minetest.get_color_escape_sequence(get_color(src))..src..scheme.minetest.delim..parts[2], "irc")
|
||||
elseif string_ext.starts_with(line, "[PMS]") then
|
||||
local parts=string_ext.split(linecontent, " ", 1)
|
||||
local source=parts[1]
|
||||
local target=parts[2]
|
||||
local msg=parts[3]
|
||||
if string_ext.ends_with(target, "[discord]") then
|
||||
file_ext.process_bridge_write("discord", "[PMS]"..source.." "..target.."@you : "..msg)
|
||||
discord_bridge.write("[PMS]"..source.." "..target.."@you : "..msg)
|
||||
else
|
||||
if minetest.get_player_by_name(target_and_msg[1]) then
|
||||
minetest.chat_send_player(target_and_msg[2])
|
||||
end
|
||||
end
|
||||
elseif string_ext.starts_with(line, "[CMD]") then
|
||||
local parts=string_ext.split(linecontent, " ", 3)
|
||||
local parts=string_ext.split(linecontent, " ", 2)
|
||||
local source=parts[1]
|
||||
local commandname=parts[2]
|
||||
local params=parts[3]
|
||||
local command=minetest.registered_chatcommands[commandname]
|
||||
if command then
|
||||
if not table_ext.is_empty(command.privs) then
|
||||
file_ext.process_bridge_write("irc", "[PMS]"..source.." ".."Error: Command requires privs.")
|
||||
else
|
||||
local success, retval = command.func(source, params)
|
||||
local prefix="Unknown"
|
||||
if success then prefix="Success" elseif success ~= nil then prefix="Error" end
|
||||
file_ext.process_bridge_write("irc", "[PMS]"..source.." "..prefix.." : "..(retval or "No return value."))
|
||||
end
|
||||
else
|
||||
file_ext.process_bridge_write("irc", "[PMS]"..source.." ".."Error: No such command.")
|
||||
end
|
||||
local call=parts[2]
|
||||
local success, retval = call_chatcommand(source.."[irc]", call)
|
||||
local prefix="Unknown"
|
||||
if success then prefix="Success" elseif success ~= nil then prefix="Error" end
|
||||
irc_bridge.write("[PMS]"..source.." "..prefix.." : "..(retval or "No return value."))
|
||||
elseif string_ext.starts_with(line, "[GMS]") or string_ext.starts_with(line, "[CGM]") then -- GMS = group message or CGM = channel group message
|
||||
local parts=string_ext.split(linecontent, " ",3)
|
||||
local source=parts[1]
|
||||
local targets=string_ext.split_without_limit(parts[2], ",")
|
||||
local msg=parts[3]
|
||||
local sent_to="nobody"
|
||||
local sent_to
|
||||
if string_ext.starts_with(line, "[CGM]") then
|
||||
sent_to="irc"
|
||||
end
|
||||
targetset={}
|
||||
for _, target in ipairs(targets) do
|
||||
targetset[target]=true
|
||||
end
|
||||
local invalid_targets, msg, mt_msg=build_message(source.."[irc]", targets, msg)
|
||||
send_to_targets(source.."[irc]", table_ext.set(targets), msg, mt_msg, sent_to)
|
||||
if (#invalid_targets) == 1 then
|
||||
file_ext.process_bridge_write("irc", "[PMS]"..source.." The target "..invalid_targets[1].." is inexistant.")
|
||||
elseif (#invalid_targets) > 1 then
|
||||
file_ext.process_bridge_write("irc", "[PMS]"..source.." The targets "..table.concat(invalid_targets, ", ").." are inexistant.")
|
||||
--targetset={}
|
||||
--for _, target in ipairs(targets) do
|
||||
-- targetset[target]=true
|
||||
--end
|
||||
--local invalid_targets, msg, mt_msg=build_message(source.."[irc]", targets, msg)
|
||||
--send_to_targets(source.."[irc]", table_ext.set(targets), msg, mt_msg, sent_to)
|
||||
local adv_msg=message.new(chatters[source.."[discord]"], targets, msg)
|
||||
adv_msg.sent_to=sent_to
|
||||
message.mentionpart(adv_msg) --force check mentions
|
||||
send_to_targets(adv_msg)
|
||||
if (#adv_msg.invalid_mentions) == 1 then
|
||||
irc_bridge.write("[PMS]"..source.." The target "..adv_msg.invalid_mentions[1].." is inexistant.")
|
||||
elseif (#adv_msg.invalid_mentions) > 1 then
|
||||
irc_bridge.write("[PMS]"..source.." The targets "..table.concat(adv_msg.invalid_mentions, ", ").." are inexistant.")
|
||||
end
|
||||
elseif string_ext.starts_with(line, "[JOI]") then
|
||||
local parts=string_ext.split(linecontent, " ", 3) --nick & color & channel
|
||||
join(parts[1].."[irc]", {color=parts[2], roles={}, irc=true})
|
||||
send_to_all("", parts[1].."[irc]".." joined.", minetest.get_color_escape_sequence(parts[2])..
|
||||
parts[1].."[irc]"..
|
||||
minetest.get_color_escape_sequence("#FFFFFF").." joined.")
|
||||
local chattername=parts[1].."[irc]"
|
||||
minetest.chat_send_all(color(chattername)..
|
||||
chattername..minetest.get_color_escape_sequence("#FFFFFF").." joined.",
|
||||
minetest.get_color_escape_sequence(parts[2])..parts[1].."[irc]"..
|
||||
minetest.get_color_escape_sequence("#FFFFFF").." joined.")
|
||||
--parts[3])
|
||||
elseif string_ext.starts_with(line, "[EXT]") then
|
||||
local parts=string_ext.split(linecontent, " ", 2) --nick & reason
|
||||
local chattername=parts[1].."[irc]"
|
||||
send_to_all("", chattername.." quitted ("..parts[2]..").", minetest.get_color_escape_sequence(get_color(chattername))..
|
||||
chattername..minetest.get_color_escape_sequence("#FFFFFF").." quitted ("..parts[2]..").")
|
||||
minetest.chat_send_all(color(chattername)..
|
||||
chattername..minetest.get_color_escape_sequence("#FFFFFF").." quitted ("..parts[2]..").", minetest.get_color_escape_sequence(get_color(chattername))..
|
||||
chattername..minetest.get_color_escape_sequence("#FFFFFF").." quitted ("..parts[2]..").")
|
||||
chatters[chattername]=nil
|
||||
elseif string_ext.starts_with(line, "[BYE]") then
|
||||
local parts=string_ext.split(linecontent, " ", 2) --nick & reason
|
||||
local chattername=parts[1].."[irc]"
|
||||
send_to_all("", chattername.." left ("..parts[2]..").", minetest.get_color_escape_sequence(get_color(chattername))..
|
||||
minetest.chat_send_all(color(chattername)..chattername..minetest.get_color_escape_sequence("#FFFFFF").." left ("..parts[2]..").", minetest.get_color_escape_sequence(get_color(chattername))..
|
||||
chattername..minetest.get_color_escape_sequence("#FFFFFF").." left ("..parts[2]..").")
|
||||
chatters[chattername]=nil
|
||||
elseif string_ext.starts_with(line, "[NCK]") then
|
||||
local parts=string_ext.split(linecontent, " ", 2) --nick & newnick
|
||||
irc_users[parts[1]]=nil
|
||||
irc_users[parts[2]]=true
|
||||
minetest.chat_send_all(parts[1].."[irc] is now known as "..parts[2].."[irc]")
|
||||
local chattername=parts[1].."[irc]"
|
||||
minetest.chat_send_all(color(chattername)..chattername..minetest.get_color_escape_sequence("#FFFFFF").." is now known as "..parts[2].."[irc]")
|
||||
end
|
||||
end)
|
||||
|
||||
-- Pinging
|
||||
mt_ext.register_globalstep(1, function()
|
||||
file_ext.process_bridge_write("irc", "[PIN]")
|
||||
bridge.write("[PIN]")
|
||||
end)
|
||||
|
||||
file_ext.process_bridge_serve("irc")
|
||||
bridge.serve()
|
||||
|
||||
--"/usr/lib/jvm/jdk-11.0.1/bin/java -classpath /home/lars/IdeaProjects/minetest-chat-bridge-irc-bot/out/production/minetest-chat-bridge-irc-bot Main 7000 irc.freenode.net true MT_Chat_Bridge #mtchatbridgetest /home/lars/.minetest/worlds/world/bridges/irc/output.txt /home/lars/.minetest/worlds/world/bridges/irc/input.txt"
|
||||
-- Start AFTER mods are loaded, so that the player sees chat messages
|
||||
minetest.register_on_mods_loaded(function()
|
||||
local java="java"
|
||||
local classpath=minetest.get_modpath("adv_chat").."/minetest-chat-bridge-irc-bot/out/production/minetest-chat-bridge-irc-bot"
|
||||
local classpath=minetest.get_modpath("adv_chat").."/MinetestChatBridgeIRCBot/build/classes/java/main"
|
||||
local port=bridges.irc.port
|
||||
local network=bridges.irc.network
|
||||
local ssl=tostring(bridges.irc.ssl)
|
||||
|
@ -104,5 +114,5 @@ minetest.register_on_mods_loaded(function()
|
|||
local textchannel=bridges.irc.channelname
|
||||
local prefixes='"'..bridges.discord.minetest_prefix..'" "'..bridges.discord.prefix..'"'
|
||||
|
||||
file_ext.process_bridge_start("irc", java..' -Dfile.encoding=UTF-8 -classpath "'..classpath..'" appguru.Main '..port..' "'..network..'" '..ssl..' "'..nick..'" "'..textchannel..'" "%s" "%s" "%s" '..prefixes..' &')
|
||||
bridge.start(java..' -Dfile.encoding=UTF-8 -classpath "'..classpath..'" appguru.Main '..port..' "'..network..'" '..ssl..' "'..nick..'" "'..textchannel..'" "%s" "%s" "%s" '..prefixes..' &')
|
||||
end)
|
159
main.lua
159
main.lua
|
@ -1,8 +1,3 @@
|
|||
---
|
||||
--- Generated by EmmyLua(https://github.com/EmmyLua)
|
||||
--- Created by lars.
|
||||
---
|
||||
|
||||
--- THIS FILE USES CUSTOM STUFF (IFNDEFS) IMPLEMENTED USING MODLIB - DON'T CHANGE THE WAY IT IS EXECUTED IN init.lua
|
||||
|
||||
-- TODO (planned features)
|
||||
|
@ -17,7 +12,6 @@ player_ext.set_property_default("adv_chat.blocked",{chatters={}, roles={}})
|
|||
channels={} --channelname -> definition : {hud_pos, mode, autoremove, max_messages, max_lines, wrap_chars, smartwrap}
|
||||
roles={} -- Role -> players -> true
|
||||
chatters={} -- Chatter -> stuff
|
||||
--blocked={} -- Receiver -> what he blocks : roles and playernames, true
|
||||
to_be_sent={} --Receiver -> { {sender, message, date, time} }
|
||||
scheme={minetest={mention_prefix=minetest.get_color_escape_sequence("#FFFF66").."@", mention_delim=minetest.get_color_escape_sequence("#FFFF66")..", ",
|
||||
delim=minetest.get_color_escape_sequence("#FFFF66").." : "..minetest.get_color_escape_sequence("#FFFFFF")},
|
||||
|
@ -55,36 +49,36 @@ function send_to_chatter(sendername, chattername, message)
|
|||
else
|
||||
--IFNDEF discord
|
||||
if chatters[chattername].discord then
|
||||
file_ext.process_bridge_write("discord", "[PMS]"..get_color(chattername).." "..chattername.." "..message)
|
||||
discord_bridge.write("[PMS]"..get_color(chattername).." "..chattername.." "..message)
|
||||
end
|
||||
--ENDIF
|
||||
--IFNDEF irc
|
||||
if chatters[chattername].irc then
|
||||
file_ext.process_bridge_write("irc", "[PMS]"..chattername.." "..message)
|
||||
irc_bridge.write("[PMS]"..chattername.." "..message)
|
||||
end
|
||||
--ENDIF
|
||||
end
|
||||
end
|
||||
|
||||
function send_to_targets(sendername, targets, message, mt_message, sent_to)
|
||||
function send_to_targets(msg)
|
||||
message.mentionpart(msg)
|
||||
--IFNDEF bridge
|
||||
local discord_mentioned, irc_mentioned=targets.discord, targets.irc
|
||||
local discord_mentioned, irc_mentioned=msg.targets.discord, msg.targets.irc
|
||||
--ENDIF
|
||||
for target, _ in pairs(targets) do
|
||||
for target, _ in pairs(msg.targets) do
|
||||
if not chatters[target] then
|
||||
if roles[target] then
|
||||
table_ext.add_all(targets, roles[target].affected)
|
||||
table_ext.add_all(msg.targets, roles[target].affected)
|
||||
end
|
||||
targets[target]=nil
|
||||
msg.targets[target]=nil
|
||||
end
|
||||
end
|
||||
local mt_message=mt_message or message
|
||||
local discord_chatters={}
|
||||
local irc_chatters={}
|
||||
for chatter, _ in pairs(targets) do
|
||||
for chatter, _ in pairs(msg.targets) do
|
||||
if not is_blocked(chatter, sendername) then
|
||||
if chatters[chatter].minetest then
|
||||
minetest.chat_send_player(chatter, mt_message)
|
||||
minetest.chat_send_player(chatter, message.build(msg, "minetest"))
|
||||
else
|
||||
--IFNDEF discord
|
||||
if chatters[chatter].discord then
|
||||
|
@ -101,25 +95,21 @@ function send_to_targets(sendername, targets, message, mt_message, sent_to)
|
|||
end
|
||||
|
||||
--IFNDEF discord
|
||||
if sent_to ~= "discord" then
|
||||
if msg.sent_to ~= "discord" then
|
||||
if discord_mentioned then
|
||||
file_ext.process_bridge_write("discord", "[MSG]"..get_color(sendername).." "..message)
|
||||
else
|
||||
if #discord_chatters > 0 then
|
||||
file_ext.process_bridge_write("discord", "[PMS]"..get_color(sendername).." "..table.concat(discord_chatters, ",").." "..message)
|
||||
end
|
||||
discord_bridge.write("[MSG]"..(msg.chatter.color).." "..message.build(msg, "discord"))
|
||||
elseif #discord_chatters > 0 then
|
||||
discord_bridge.write("[PMS]"..(msg.chatter.color).." "..table.concat(discord_chatters, ",").." "..message.build(msg, "discord"))
|
||||
end
|
||||
end
|
||||
--ENDIF
|
||||
|
||||
--IFNDEF irc
|
||||
if sent_to ~= "irc" then
|
||||
if msg.sent_to ~= "irc" then
|
||||
if irc_mentioned then
|
||||
file_ext.process_bridge_write("irc", "[MSG]"..message)
|
||||
else
|
||||
if #irc_chatters > 0 then
|
||||
file_ext.process_bridge_write("irc", "[PMS]"..table.concat(irc_chatters, ",").." "..message)
|
||||
end
|
||||
irc_bridge.write("[MSG]"..message.build(msg, "irc"))
|
||||
elseif #irc_chatters > 0 then
|
||||
irc_bridge.write("[PMS]"..table.concat(irc_chatters, ",").." "..message.build(msg, "irc"))
|
||||
end
|
||||
end
|
||||
--ENDIF
|
||||
|
@ -129,6 +119,10 @@ function join(name, def)
|
|||
if not def.roles then
|
||||
def.roles={}
|
||||
end
|
||||
if not def.name then
|
||||
def.name=name
|
||||
end
|
||||
def.service = ((def.minetest and "minetest") or (def.irc and "irc")) or "discord"
|
||||
chatters[name]=def
|
||||
local to_be_received=to_be_sent[name]
|
||||
if to_be_received then
|
||||
|
@ -172,7 +166,33 @@ end
|
|||
--IFNDEF bridge
|
||||
minetest.original_chat_send_all=minetest.chat_send_all
|
||||
minetest.chat_send_all=function(msg)
|
||||
send_to_all("", minetest.strip_colors(msg), msg)
|
||||
local adv_message=message.new(nil, nil, msg)
|
||||
adv_message.internal=true
|
||||
send_to_all(adv_message)
|
||||
end
|
||||
|
||||
minetest.original_chat_send_player=minetest.chat_send_player
|
||||
minetest.chat_send_player=function(name, msg)
|
||||
local chatter=chatters[name]
|
||||
if not chatter then
|
||||
return
|
||||
end
|
||||
if chatter.minetest then
|
||||
return minetest.original_chat_send_player(name, msg)
|
||||
end
|
||||
local adv_message=message.new(nil, nil, msg)
|
||||
adv_message.internal=true
|
||||
local to_be_sent=message.build(adv_message, chatter.service)
|
||||
--IFNDEF irc
|
||||
if chatter.irc then
|
||||
irc_bridge.write("[PMS]"..chatter.name.." "..to_be_sent)
|
||||
end
|
||||
--ENDIF
|
||||
--IFNDEF discord
|
||||
if chatter.discord then
|
||||
discord_bridge.write("[PMS]#FFFFFF "..chatter.name.." "..to_be_sent)
|
||||
end
|
||||
--ENDIF
|
||||
end
|
||||
--ENDIF
|
||||
|
||||
|
@ -201,6 +221,7 @@ function remove_role(player, role, expected_value)
|
|||
end
|
||||
end
|
||||
|
||||
-- deprecated, minetest-only
|
||||
function get_color(chatter)
|
||||
if chatters[chatter] then
|
||||
return chatters[chatter].color or "#FFFFFF"
|
||||
|
@ -208,22 +229,23 @@ function get_color(chatter)
|
|||
return "#FFFFFF"
|
||||
end
|
||||
|
||||
function send_to_all(sender, msg, mt_msg, sent_to)
|
||||
function send_to_all(msg)
|
||||
--IFNDEF irc
|
||||
if sent_to ~= "irc" then
|
||||
file_ext.process_bridge_write("irc", "[MSG]"..msg)
|
||||
if msg.sent_to ~= "irc" then
|
||||
irc_bridge.write("[MSG]"..message.build(msg, "irc"))
|
||||
end
|
||||
--ENDIF
|
||||
--IFNDEF discord
|
||||
if sent_to ~= "discord" then
|
||||
file_ext.process_bridge_write("discord", "[MSG]"..get_color(sender).." "..msg)
|
||||
if msg.sent_to ~= "discord" then
|
||||
discord_bridge.write("[MSG]"..((msg.chatter and msg.chatter.color) or "#FFFFFF").." "..message.build(msg, "discord"))
|
||||
end
|
||||
--ENDIF
|
||||
if sent_to ~= "minetest" then
|
||||
local mt_msg=mt_msg or msg
|
||||
if msg.sent_to ~= "minetest" then
|
||||
local mt_msg
|
||||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
local playername=player:get_player_name()
|
||||
if not is_blocked(playername, sender) then
|
||||
if not msg.chatter or not is_blocked(playername, msg.chatter) then
|
||||
mt_msg=mt_msg or message.build(msg, "minetest")
|
||||
minetest.chat_send_player(playername, mt_msg)
|
||||
end
|
||||
end
|
||||
|
@ -327,13 +349,14 @@ on_chat_message=function(sender, msg)
|
|||
for _, part in pairs(parts) do
|
||||
table.insert(mentions, string_ext.trim(part, " "))
|
||||
end
|
||||
local invalid_targets, msg, mt_msg=build_message(sender, mentions, msg_content)
|
||||
local adv_msg=message.new(chatters[sender], mentions, msg_content)
|
||||
message.mentionpart(adv_msg)
|
||||
table.insert(mentions, sender)
|
||||
send_to_targets(sender, table_ext.set(mentions), msg, mt_msg, "nobody")
|
||||
if (#invalid_targets) == 1 then
|
||||
minetest.chat_send_player(sender, "The target "..invalid_targets[1].." is inexistant.")
|
||||
elseif (#invalid_targets) > 1 then
|
||||
minetest.chat_send_player(sender, "The targets "..table.concat(invalid_targets, ", ").." are inexistant.")
|
||||
send_to_targets(adv_msg)--sender, table_ext.set(mentions), msg, mt_msg, "nobody")
|
||||
if #adv_msg.invalid_mentions == 1 then
|
||||
minetest.chat_send_player(sender, "The target "..adv_msg.invalid_mentions[1].." is inexistant.")
|
||||
elseif #adv_msg.invalid_mentions > 1 then
|
||||
minetest.chat_send_player(sender, "The targets "..table.concat(adv_msg.invalid_mentions, ", ").." are inexistant.")
|
||||
end
|
||||
else
|
||||
local sender_color=get_color(sender)
|
||||
|
@ -341,16 +364,16 @@ on_chat_message=function(sender, msg)
|
|||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
players[player:get_player_name()]=true
|
||||
end
|
||||
local msg, mt_msg=parse_message(msg)
|
||||
mt_msg=minetest.get_color_escape_sequence(sender_color)..sender..scheme.minetest.delim..msg
|
||||
msg=sender..scheme.other.delim..msg
|
||||
send_to_all(sender, msg, mt_msg)
|
||||
local adv_msg=message.new(chatters[sender], mentions, msg_content)
|
||||
send_to_all(adv_msg)
|
||||
end
|
||||
return true
|
||||
end
|
||||
minetest.register_on_chat_message(on_chat_message)
|
||||
|
||||
minetest.register_chatcommand("msg",{
|
||||
local prefix = (cmd_ext and "chat ") or "chat_"
|
||||
|
||||
minetest.register_chatcommand(prefix.."msg",{
|
||||
params = "<name> <message>",
|
||||
description = "Send a message to a chatter as soon as they join",
|
||||
privs={},
|
||||
|
@ -380,7 +403,7 @@ button_exit[7,0;2,0.75;send;Send]
|
|||
no_prepend[]
|
||||
]]
|
||||
|
||||
minetest.register_chatcommand("say", {
|
||||
minetest.register_chatcommand(prefix.."say", {
|
||||
params="",
|
||||
description="Send chat message using entry field.",
|
||||
privs={discord_user=false, irc_user=false},
|
||||
|
@ -395,7 +418,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||
end
|
||||
end)
|
||||
|
||||
minetest.register_chatcommand("block", {
|
||||
minetest.register_chatcommand(prefix.."block", {
|
||||
params = "<name> | <role>",
|
||||
description = "Block messages from chatter or role",
|
||||
privs={},
|
||||
|
@ -419,7 +442,7 @@ minetest.register_chatcommand("block", {
|
|||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("unblock", {
|
||||
minetest.register_chatcommand(prefix.."unblock", {
|
||||
params = "<name> | <role>",
|
||||
description = "Unblock messages from chatter or role",
|
||||
privs={},
|
||||
|
@ -441,4 +464,38 @@ minetest.register_chatcommand("unblock", {
|
|||
blocked[param]=nil
|
||||
return true, type..param.." was unblocked"
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand(prefix.."login", {
|
||||
params = "<name> <password>",
|
||||
description = "Log in as (fake) player to execute chatcommands as them",
|
||||
privs = {chatter=true},
|
||||
func = function(sendername, param)
|
||||
param=string_ext.trim(param)
|
||||
if param:len() == 0 then
|
||||
return false, "No arguments given - missing name and password."
|
||||
end
|
||||
local name, password = unpack(string_ext.split(param, " ", 2))
|
||||
password = password or ""
|
||||
local auth = minetest.get_auth_handler().get_auth(name)
|
||||
if auth and minetest.check_password_entry(name, auth.password, password) then
|
||||
chatters[sendername].login = name
|
||||
return true, 'Logged in as "'..name..'"'
|
||||
end
|
||||
return false, "Wrong playername/password. : "..name..", "..password.."!="..auth.password
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand(prefix.."logout", {
|
||||
params = "",
|
||||
description = "Log out from your (fake) player account",
|
||||
privs = {chatter=true},
|
||||
func = function(sendername, param)
|
||||
if not chatters[sendername].login then
|
||||
return false, "Not logged in."
|
||||
end
|
||||
local login = chatters[sendername].login
|
||||
chatters[sendername].login = nil
|
||||
return true, 'Logged out from "'..login..'"'
|
||||
end
|
||||
})
|
|
@ -0,0 +1,227 @@
|
|||
-- TODO handle <https://example.com> and mentions like @<ID> or @<!ID> (modification of Discord bot needed)
|
||||
|
||||
message={}
|
||||
|
||||
function message.new(chatter, mentions, content)
|
||||
return {chatter=chatter, mentions=mentions, content=content}
|
||||
end
|
||||
|
||||
local function unicode(message)
|
||||
message.unicode_content = message.unicode_content or parse_unicode(message.content)
|
||||
return message.unicode_content
|
||||
end
|
||||
|
||||
local function colorized(message)
|
||||
if not message.colorized_content then
|
||||
message.colorized_content, message.uncolorized_content=colorize_message(unicode(message))
|
||||
end
|
||||
return message.colorized_content
|
||||
end
|
||||
|
||||
local function uncolorized(message)
|
||||
if not message.uncolorized_content then
|
||||
message.colorized_content, message.uncolorized_content=colorize_message(unicode(message))
|
||||
end
|
||||
message.uncolorized_content=minetest.strip_colors(message.uncolorized_content)
|
||||
return message.uncolorized_content
|
||||
end
|
||||
|
||||
local to = {
|
||||
minetest = {
|
||||
from={
|
||||
internal=function(message)
|
||||
return message.content
|
||||
end,
|
||||
minetest = colorized,
|
||||
irc = function(message)
|
||||
message.colorized_content = irc_to_minetest(colorized(message))
|
||||
return message.colorized_content
|
||||
end,
|
||||
discord = uncolorized
|
||||
}
|
||||
},
|
||||
|
||||
irc = {
|
||||
from={
|
||||
internal=function(message)
|
||||
return message.content
|
||||
end,
|
||||
minetest = function(message)
|
||||
return colorized(message)
|
||||
end,
|
||||
irc = function(message)
|
||||
return colorized(message)
|
||||
end,
|
||||
discord = function(message)
|
||||
return minetest_to_irc(colorized(message))
|
||||
end
|
||||
}
|
||||
},
|
||||
|
||||
discord = {
|
||||
from={
|
||||
internal=function(message)
|
||||
return minetest.strip_colors(message.content)
|
||||
end,
|
||||
minetest = uncolorized,
|
||||
irc = function(message)
|
||||
return uncolorized(message)
|
||||
end,
|
||||
discord = uncolorized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local builders = to
|
||||
|
||||
builders.minetest.scheme = schemes.minetest
|
||||
builders.irc.scheme = schemes.irc
|
||||
builders.discord.scheme = schemes.discord
|
||||
|
||||
function message.mentionpart(msg)
|
||||
if not msg.mentionpart then
|
||||
msg.invalid_mentions={}
|
||||
msg.targets={}
|
||||
msg.mentionpart={}
|
||||
for _, mention in ipairs(msg.mentions or {}) do
|
||||
if not msg.targets[mention] then
|
||||
msg.targets[mention]=true
|
||||
if roles[mention] then
|
||||
table.insert(msg.mentionpart,roles[mention].color)
|
||||
table.insert(msg.mentionpart, mention)
|
||||
elseif chatters[mention] then
|
||||
table.insert(msg.mentionpart, chatters[mention].color)
|
||||
table.insert(msg.mentionpart, mention)
|
||||
else
|
||||
table.insert(msg.invalid_mentions,mention)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local mentionpart_builders = {
|
||||
irc=nil,
|
||||
discord=function(msg)
|
||||
if not msg.uncolorized_mentionpart then
|
||||
msg.uncolorized_mentionpart={}
|
||||
for i=2, #msg.mentionpart, 2 do
|
||||
table.insert(msg.uncolorized_mentionpart,msg.mentionpart[i])
|
||||
end
|
||||
end
|
||||
return "uncolorized_mentionpart"
|
||||
end,
|
||||
minetest=function(msg)
|
||||
if not msg.mt_mentionpart then
|
||||
msg.mt_mentionpart={}
|
||||
for index, item in ipairs(msg.mentionpart) do
|
||||
table.insert(msg.mt_mentionpart, ((index % 2 == 0) and item) or minetest.get_color_escape_sequence(item))
|
||||
end
|
||||
end
|
||||
return "mt_mentionpart"
|
||||
end
|
||||
}
|
||||
|
||||
local function wrap_builder(source, goal, wrapper)
|
||||
local old_builder = builders[source].from[goal]
|
||||
builders[source].from[goal] = function(msg) return wrapper(old_builder(msg)) end
|
||||
end
|
||||
|
||||
if bridges.discord then
|
||||
if not bridges.discord.convert_internal_markdown then
|
||||
wrap_builder("discord", "internal", escape_markdown)
|
||||
end
|
||||
if not bridges.discord.convert_minetest_markdown then
|
||||
wrap_builder("discord", "minetest", escape_markdown)
|
||||
end
|
||||
if bridges.discord.handle_irc_styles == "escape_markdown" then
|
||||
wrap_builder("discord", "irc", escape_markdown)
|
||||
elseif bridges.discord.handle_irc_styles ~= "disabled" then
|
||||
wrap_builder("discord", "irc", irc_to_markdown)
|
||||
end
|
||||
end
|
||||
|
||||
if bridges.irc then
|
||||
|
||||
if bridges.irc.handle_discord_markdown == "strip" then
|
||||
wrap_builder("irc", "discord", strip_markdown)
|
||||
elseif bridges.irc.handle_discord_markdown ~= "disabled" then
|
||||
wrap_builder("irc", "discord", markdown_to_irc)
|
||||
end
|
||||
|
||||
if bridges.irc.handle_minetest_markdown == "strip" then
|
||||
wrap_builder("irc", "minetest", strip_markdown)
|
||||
elseif bridges.irc.handle_discord_markdown ~= "disabled" then
|
||||
wrap_builder("irc", "minetest", markdown_to_irc)
|
||||
end
|
||||
|
||||
if bridges.irc.handle_internal_markdown == "strip" then
|
||||
wrap_builder("irc", "internal", strip_markdown)
|
||||
elseif bridges.irc.handle_discord_markdown ~= "disabled" then
|
||||
wrap_builder("irc", "internal", markdown_to_irc)
|
||||
end
|
||||
|
||||
if bridges.irc.convert_minetest_colors=="disabled" then
|
||||
mentionpart_builders.irc=mentionpart_builders.discord
|
||||
else
|
||||
local old_from_minetest = builders.irc.from.minetest
|
||||
builders.irc.from.minetest=function(msg) return minetest_to_irc(old_from_minetest(msg)) end
|
||||
local old_from_internal = builders.irc.from.internal
|
||||
builders.irc.from.internal=function(msg) return minetest_to_irc(old_from_internal(msg)) end
|
||||
mentionpart_builders.irc=function(msg)
|
||||
if not msg.irc_mentionpart then
|
||||
msg.irc_mentionpart={}
|
||||
for index, item in ipairs(msg.mentionpart) do
|
||||
if index % 2 == 0 then
|
||||
table.insert(msg.irc_mentionpart, item)
|
||||
elseif item ~= "#FFFFFF" then
|
||||
table.insert(msg.irc_mentionpart, convert_color_to_irc(item:sub(2)))
|
||||
end
|
||||
table.insert(msg.irc_mentionpart, ((index % 2 == 0) and item) or (item ~= "#FFFFFF" and convert_color_to_irc(item:sub(2))))
|
||||
end
|
||||
end
|
||||
return "irc_mentionpart"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function message.mentionpart_target(msg, target)
|
||||
local builder=mentionpart_builders[target]
|
||||
message.mentionpart(msg)
|
||||
local name=builder(msg)
|
||||
local text = name.."_text"
|
||||
if not msg[text] then
|
||||
msg[text]=table.concat(msg[name], builders[target].mention_delim)
|
||||
end
|
||||
return msg[text]
|
||||
end
|
||||
|
||||
|
||||
function message.build(msg, target)
|
||||
local build=target.."_build"
|
||||
if not msg[build] then
|
||||
local builder = builders[target]
|
||||
if msg.internal then
|
||||
msg[build]=builder.from.internal(msg)
|
||||
return msg[build]
|
||||
end
|
||||
local conversion = builder.from[msg.chatter.service]
|
||||
local content = conversion(msg)
|
||||
local source = (msg.chatter.name and msg.chatter.name)
|
||||
if source and msg.chatter.color then
|
||||
if target=="minetest" then
|
||||
source=minetest.get_color_escape_sequence(msg.chatter.color)..source
|
||||
elseif target=="irc" and bridges.irc.style_conversion.color~="disabled" then
|
||||
local to_escape, color=convert_color_to_irc(msg.chatter.color:sub(2))
|
||||
if source:sub(1,1)==to_escape then
|
||||
source=string.char(0x02)..string.char(0x02)..source
|
||||
end
|
||||
source=color..source
|
||||
end
|
||||
end
|
||||
local mentions = (msg.mentions and next(msg.mentions) and builder.scheme.mention_prefix..message.mentionpart_target(msg, target)..builder.scheme.content_prefix)
|
||||
if not mentions and source then source=source..builder.scheme.content_prefix end
|
||||
msg[build]=builder.scheme.message_prefix..(source or "")..(mentions or "")..content..builder.scheme.message_suffix
|
||||
end
|
||||
return msg[build]
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue