最近想做个Chat Server,然后通过LLM进行回复,核心是想给自己做一个电脑助手,可以管理和执行一系列作业。

对话系统选择了Openfire,Openfire是基于XMPP协议的,也就是使用xml封装的消息,谈不上性能多好,但是作为聊天使用的话,足够了。
openfire提供了官方的客户端工具Spark,也可以使用代码进行连接,如果要自己的写代码的话,需要引入如下依赖包:

<dependencies>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-java8</artifactId>
<version>4.5.0-alpha1</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-tcp</artifactId>
<version>4.5.0-alpha1</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-im</artifactId>
<version>4.5.0-alpha1</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-extensions</artifactId>
<version>4.5.0-alpha1</version>
</dependency>
</dependencies>

一个简单的例子:

import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
import org.jivesoftware.smack.chat2.OutgoingChatMessageListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;

public class ChatDemo {
public static void main(String[] args) throws Exception {
XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
.setUsernameAndPassword("fcbai01","fcbai01")
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
.setXmppDomain("localhost")
.setHost("localhost")
.build();

AbstractXMPPConnection connection = new XMPPTCPConnection(config);
connection.connect(); //Establishes a connection to the server
connection.login(); //Logs in

ChatManager chatManager = ChatManager.getInstanceFor(connection);
EntityBareJid jid = JidCreate.entityBareFrom("fcbai@localhost");
Chat chat = chatManager.chatWith(jid);

chatManager.addOutgoingListener(new OutgoingChatMessageListener() {
@Override
public void newOutgoingMessage(EntityBareJid entityBareJid, MessageBuilder messageBuilder, Chat chat) {
System.out.println(1);
}
});

chatManager.addIncomingListener(new IncomingChatMessageListener() {
@Override
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
System.out.println("New message from " + from + ": " + message.getBody());
}
});

chat.send("Hello!");

while (true){
Thread.sleep(1000 *1);
}
}
}

更多可以看这里:https://www.baeldung.com/xmpp-smack-chat-client

另外也可以自己实现插件,一个例子:

import java.io.File;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketInterceptor;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;

/**
* 过滤插件:当body中有fuck时,将消息截断,不转发消息。
*
*/
public class MessageFilterPlugin implements Plugin ,PacketInterceptor{
// A:生成一个日志实例,用于打印日志,日志被打印在openfire_src\target\openfire\logs目录中
private static final Logger Log = LoggerFactory.getLogger(MessageFilterPlugin.class);
//B: 消息拦截器
private InterceptorManager interceptorManager;

//C: 插件初始化函数
@Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {

Log.info("MessageFilter init");
// 将当前插件加入到消息拦截管理器(interceptorManager )中,当消息到来或者发送出去的时候,会触发本插件的interceptPacket方法。
interceptorManager = InterceptorManager.getInstance();
interceptorManager.addInterceptor(this);
}

//D: 插件销毁函数
@Override
public void destroyPlugin() {
Log.info("MessageFilter destory");
// 当插件被卸载的时候,主要通过openfire管理控制台卸载插件时,被调用。注意interceptorManager的addInterceptor和removeInterceptor需要成对调用。
interceptorManager.removeInterceptor(this);
}

//E 插件拦截处理函数
@Override
public void interceptPacket(Packet packet, Session session,
boolean incoming, boolean processed)
throws PacketRejectedException {
// incoming表示本条消息刚进入openfire。processed为false,表示本条消息没有被openfire处理过。这说明这是一条处女消息,也就是没有被处理过的消息。
if (incoming && processed == false) {
// packet可能是IQ、Presence、Message,这里当packet是message的时候,进行处理。
if (packet instanceof Message) {
// 将packet强制转换为Message
Message msg = (Message)packet;
// 取得message中的body内容,就是消息正文
String body = msg.getBody();
// 如果内容中包含fuck,则拒绝处理消息
if(body != null && body.contains("fuck")){
// F: 这里通过抛出异常的方式,来阻止程序流程继续执行下去。
PacketRejectedException rejectedException = new PacketRejectedException();

rejectedException.setRejectionMessage("fuck is error");

throw rejectedException;
}

}
}

}

}

openfire的启动class是:

org/jivesoftware/openfire/starter/ServerStarter.java

由于它的加载逻辑是先打包,然后使用动态加载,所以调试不是特别方便,需要先配置一下:

System.setProperty("openfire.lib.dir", "distribution-base/lib");
System.setProperty("openfireHome", "distribution-base");

这样的情况下,每一次修改,其实都需要重新打包一次。

在使用自带的客户端连接的时候,会有网络连接问题,修改一下Spark脚本的最后一行,添加:-Djava.net.preferIPv4Stack=true,最后的格式:

return_code=0
if [ "$has_space_options" = "true" ]; then
$INSTALL4J_JAVA_PREFIX exec "$app_java_home/bin/java" -client "-Dappdir=$prg_dir/" "-Djava.net.preferIPv4Stack=true" "-Dsun.java2d.noddraw=true" "-Djava.library.path=$prg_dir/\lib\windows" "$vmov_1" "$vmov_2" "$vmov_3" "$vmov_4" "$vmov_5" $INSTALL4J_ADD_VM_PARAMS -classpath "$local_classpath" install4j.org.jivesoftware.launcher.Startup "$@"
return_code=$?
else
$INSTALL4J_JAVA_PREFIX exec "$app_java_home/bin/java" -client "-Dappdir=$prg_dir/" "-Djava.net.preferIPv4Stack=true" "-Dsun.java2d.noddraw=true" "-Djava.library.path=$prg_dir/\lib\windows" $INSTALL4J_ADD_VM_PARAMS -classpath "$local_classpath" install4j.org.jivesoftware.launcher.Startup "$@"
return_code=$?
fi

这样可以解决Socket的问题。


扫码手机观看或分享: