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項目還包括一些可以立即使用的標準客戶端。
最後,該專案有一堆代理模組,提供對創建聊天機器人等有用的功能。它們也可以立即使用。