maiden是一个系统集合,可帮助您构建与聊天服务器交互的应用程序和库。它可以帮助您构建聊天机器人或通用聊天客户端。它还提供了各种部件,可以使为新的聊天协议编写客户端变得更加容易。
如果您只关心使用maiden来设置某种机器人,那么执行此操作的步骤相当简单。首先,我们要加载maiden以及您想要在机器人中使用的所有模块和组件。
(ql:quickload '( maiden maiden -irc maiden -commands maiden -silly))
然后我们将创建一个核心,并按照我们的意愿添加消费者实例。
(defvar *core* ( maiden :make-core
'(: maiden -irc :nickname " maiden Test" :host "irc.freenode.net" :channels ("##testing"))
: maiden -commands
: maiden -silly))
make-core 命令采用要添加的使用者的包名称(作为字符串或符号),或使用者的直接类名称。在前一种情况下,它将尝试自行找到适当的消费者类名称。
就是这样。 make-core
将创建一个核心,实例化所有消费者,将它们添加到其中,然后启动一切。为maiden提供的模块的战利品将利用某种配置或持久存储。其管理参见存储子系统。
为了使用maiden作为框架,您首先需要像往常一样为项目定义自己的系统和包。现在我们只使用maiden -user
包来玩。接下来我们要定义一个消费者。这可以通过define-consumer
来完成。
(in-package #: maiden -user)
(define-consumer ping-notifier (agent)
())
通常您需要定义一个代理。代理只能在核心上存在一次。稍后我们将为客户提供一个示例。现在,从现在开始,我们可以定义我们自己的方法和函数,这些方法和函数专门用于消费者类或作用于消费者类,就像您在一般 CLOS 编程中所习惯的那样。接下来,我们将定义自己的事件,用于向系统发送“ping 请求”。
(define-event ping (passive-event)
())
该事件被定义为passive-event
因为它不直接请求采取操作,而是通知系统正在发生的 ping。现在,为了真正使消费者与事件系统交互,我们还需要定义处理程序。这可以通过define-handler
来完成。
(define-handler (ping-notifier ping-receiver ping) (c ev)
(v:info :ping "Received a ping: ~a" ev))
这在我们的ping-notifier
消费者上定义了一个名为ping-receiver
处理程序。它还指定它将侦听ping
类型的事件。随后的 arglist 表示消费者实例绑定到c
,事件实例绑定到ev
。然后主体简单地使用 Verbose 记录一条信息消息。
让我们快速测试一下。
(defvar *core* (make-core 'ping-notifier))
(do-issue *core* ping)
这应该按预期将状态消息打印到 REPL。这就是使用该系统的大部分内容。请注意,为了做实际有用的事情,您可能需要利用maiden项目除了核心之外提供的一些预先存在的子系统。这些将在用户、通道、帐户、命令、网络、存储等方面为您提供帮助。另请记住,您也可以利用 Deeds 本身提供的功能,例如过滤处理程序的表达式。
现在让我们看一下一种原始的客户端。客户端将能够简单地通过事件写入文件。
(define-consumer file-client (client)
((file :initarg :file :accessor file))
(:default-initargs :file (error "FILE required.")))
(define-event write-event (client-event active-event)
((sequence :initarg :sequence))
(:default-initargs :sequence (error "SEQUENCE required.")))
我们已将write-event
设为client-event
因为它需要特定于我们要写入的客户端,并且我们将其设为active-event
因为它请求发生某些事情。现在让我们定义我们的处理程序,它将负责实际将序列写入文件。
(define-handler (file-client writer write-event) (c ev sequence)
:match-consumer 'client
(with-open-file (stream (file c) :direction :output :if-exists :append :if-does-not-exist :create)
(write-sequence sequence stream)))
:match-consumer
选项修改处理程序的过滤器,使过滤器仅传递其client
端槽包含与当前处理程序实例所属的相同file-client
实例的事件。这很重要,因为file-client
的每个实例都将在核心上接收其自己的处理程序实例。如果没有此选项, write-event
将由file-client
的每个实例处理,无论该事件针对哪个实例。另请注意,我们向处理程序的参数列表添加了一个sequence
参数。该参数将填充事件中的适当槽。如果找不到这样的插槽,则会发出错误信号。
是时候测试一下了。我们将重用上面的核心。
(add-to-core *core* '(file-client :file "~/foo" :name :foo)
'(file-client :file "~/bar" :name :bar))
(do-issue *core* write-event :sequence "foo" :client (consumer :foo *core*))
(do-issue *core* write-event :sequence "bar" :client (consumer :bar *core*))
(alexandria:read-file-into-string "~/foo") ; => "foo"
(alexandria:read-file-into-string "~/bar") ; => "bar"
正如您所看到的,事件根据我们想要的客户端定向到适当的处理程序实例,因此文件包含我们期望的内容。
最后,值得一提的是,还可以在运行时动态添加和删除处理程序,甚至可以对与特定使用者不相关的处理程序执行此操作。当您需要等待来自某处的响应事件时,这通常很有用。为了处理异步执行此操作的逻辑并保留命令式流程的印象, maiden提供了(就像 Deeds 所做的那样)一个with-awaiting
宏。它可以按如下方式使用:
(with-awaiting (core event-type) (ev some-field)
(do-issue core initiating-event)
:timeout 20
some-field)
with-awaiting
与define-handler
非常相似,不同之处在于它不带名称,并且它需要一个核心或消费者实例,而不是开头的消费者名称。它还需要一个额外的选项,否则不会被使用,即:timeout
。另一个必需的额外内容是 arglist 之后的“设置表单”。为了正确管理一切并确保系统中不会出现竞争条件,您必须启动将在此设置表单中提示最终响应事件的进程。如果您在此之前启动它,则响应事件可能会在系统中设置临时处理程序之前发送出去,并且看起来就像它从未到达一样。
这几乎就是所有的基础知识了。如上所述,看看该项目包含的子系统,因为它们将帮助您解决围绕聊天系统等的各种常见任务和问题。
在了解maiden之前,值得先了解“行为”,即使只是表面层面的。 maiden建立在它的基础上。
core
是maiden配置的中心部分。它负责管理和编排系统的其他组件。您可以在同一个 lisp 映像中同时运行多个内核,甚至可以在它们之间共享组件。
更具体地说,核心由事件循环和一组消费者组成。事件循环负责将事件传递给处理程序。消费者负责将处理程序附加到事件循环。因此,您最有可能希望在核心上执行的操作是:通过issue
向其发出事件,通过add-consumer
添加消费者,或者通过remove-consumer
从中删除消费者。
为了让您更轻松地创建一个有用的核心并添加消费者,您可以使用make-core
和add-to-core
函数。
event
是代表系统中的变化的对象。事件可用于表示已发生的更改,或表示发生更改的请求。这些分别称为passive-event
和active-event
。
一般来说,您会通过以下方式使用事件:
consumer
是代表系统中的组件的类。每个消费者都可以有多个与其绑定的处理程序,这些处理程序将对系统中的事件做出反应。消费者有两种基本的超类型: agent
和client
。代理是只应在核心上存在一次的消费者,因为它们实现的功能对于以某种方式复用是没有意义的。另一方面,客户端代表某种与外部系统的桥梁,自然应该允许在同一核心上拥有多个实例。
因此,开发一组命令或某种接口可能会导致代理,而与 XMPP 等服务的接口可能会导致客户端。
定义消费者应该使用define-consumer
进行,它类似于标准defclass
,但确保正确设置超类和元类。
handler
是包含函数的对象,当特定事件发送到核心时,该函数执行某些操作。每个处理程序都与特定的使用者绑定,并在使用者被删除或添加到核心时被删除或添加到核心的事件循环中。
处理程序定义是通过define-handler
、 define-function-handler
、 define-instruction
或define-query
之一进行的。每个都相继建立在最后一个的基础上,为常见需求提供更广泛的速记。请注意,处理程序实际接收其事件的方式可能有所不同。查看 Deeds 文档以了解哪些处理程序类可用。
maiden项目中包含几个扩展核心功能的子系统。
maiden项目还包括一些可以立即使用的标准客户端。
最后,该项目有一堆代理模块,提供对创建聊天机器人等有用的功能。它们也可以立即使用。