最近想做个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的问题。
扫码手机观看或分享: