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-receiver
บนตัวแจ้ง ping-notifier
ของเรา นอกจากนี้ยังระบุว่าจะรับฟังเหตุการณ์ประเภท ping
อีกด้วย หลังจากนั้น arglist บอกว่าอินสแตนซ์ของผู้บริโภคเชื่อมโยงกับ c
และอินสแตนซ์ของเหตุการณ์กับ ev
จากนั้นเนื้อหาจะบันทึกข้อความแสดงข้อมูลโดยใช้ Verbose
มาทดสอบสิ่งนี้กันอย่างรวดเร็ว
(defvar *core* (make-core 'ping-notifier))
(do-issue *core* ping)
ที่ควรพิมพ์ข้อความสถานะไปยัง REPL ตามที่คาดไว้ และนั่นคือทุกสิ่งทุกอย่างส่วนใหญ่สำหรับการใช้ระบบนี้ โปรดทราบว่าเพื่อที่จะทำสิ่งที่มีประโยชน์จริงๆ คุณอาจต้องการใช้ระบบย่อยที่มีอยู่แล้วบางส่วนที่ maiden Project มอบให้นอกเหนือจากแกนกลาง สิ่งเหล่านี้จะช่วยคุณเกี่ยวกับผู้ใช้ ช่องทาง บัญชี คำสั่ง เครือข่าย ที่เก็บข้อมูล และอื่นๆ นอกจากนี้ โปรดทราบว่าคุณสามารถใช้คุณลักษณะที่ 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
ให้กับ arglist ของตัวจัดการ อาร์กิวเมนต์นี้จะเต็มไปด้วยช่องที่เหมาะสมจากเหตุการณ์ หากไม่พบช่องดังกล่าว แสดงว่าเกิดข้อผิดพลาด
ถึงเวลาทดสอบแล้ว เราจะนำแกนจากด้านบนกลับมาใช้ใหม่
(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 เสนอมาโคร with-awaiting
เช่นเดียวกับที่ Deeds ทำ มันสามารถใช้ได้ดังต่อไปนี้:
(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 มีหน้าที่จัดการและประสานส่วนประกอบอื่นๆ ของระบบ คุณสามารถมีหลายคอร์ที่ทำงานพร้อมกันภายในอิมเมจเสียงกระเพื่อมเดียวกัน และยังสามารถแชร์ส่วนประกอบระหว่างคอร์เหล่านั้นได้ด้วย
โดยเฉพาะอย่างยิ่ง Core ประกอบด้วย event-loop และกลุ่มผู้บริโภค event-loop มีหน้าที่รับผิดชอบในการส่งเหตุการณ์ไปยังตัวจัดการ ผู้บริโภคมีหน้าที่รับผิดชอบในการแนบตัวจัดการเข้ากับลูปเหตุการณ์ การดำเนินการที่คุณน่าจะอยากทำบนคอร์มากที่สุดคือ: การออกเหตุการณ์ตาม issue
การเพิ่ม Consumer เข้าไปโดย add-consumer
หรือการลบ Consumer ออกจากคอร์โดย remove-consumer
เพื่อให้ง่ายต่อการสร้างแกนหลักที่เป็นประโยชน์โดยเพิ่มผู้บริโภคเข้าไป คุณสามารถใช้ฟังก์ชัน make-core
และ add-to-core
ได้
event
คือวัตถุที่แสดงถึงการเปลี่ยนแปลงในระบบ เหตุการณ์สามารถใช้เพื่อแสดงถึงการเปลี่ยนแปลงที่เกิดขึ้น หรือเพื่อแสดงคำขอให้มีการเปลี่ยนแปลงที่จะเกิดขึ้น สิ่งเหล่านี้เรียกว่า passive-event
s และ active-event
ตามลำดับ
โดยทั่วไป คุณจะใช้เหตุการณ์ในลักษณะต่อไปนี้:
consumer
คือคลาสที่แสดงถึงส่วนประกอบในระบบ ผู้บริโภคแต่ละรายสามารถมีตัวจัดการจำนวนมากผูกติดอยู่ ซึ่งจะตอบสนองต่อเหตุการณ์ในระบบ ผู้บริโภคแบ่งออกเป็นประเภทย่อยพื้นฐานสองประเภท ได้แก่ agent
และ client
Agent คือผู้บริโภคที่ควรมีอยู่บนคอร์เพียงครั้งเดียวเท่านั้น เนื่องจากพวกเขาใช้ฟังก์ชันการทำงานที่ไม่สมเหตุสมผลที่จะมัลติเพล็กซ์ในทางใดทางหนึ่ง ในทางกลับกัน ไคลเอนต์เป็นตัวแทนของสะพานบางประเภทไปยังระบบภายนอก และโดยธรรมชาติแล้วควรได้รับอนุญาตให้มีหลายอินสแตนซ์บนคอร์เดียวกัน
ดังนั้นการพัฒนาชุดคำสั่งหรืออินเทอร์เฟซบางประเภทอาจนำไปสู่เอเจนต์ ในขณะที่การเชื่อมต่อกับบริการเช่น XMPP จะนำไปสู่ไคลเอ็นต์
การกำหนดผู้บริโภคควรเกิดขึ้นด้วย define-consumer
ซึ่งคล้ายกับมาตรฐาน defclass
แต่ต้องแน่ใจว่าซูเปอร์คลาสและเมตาคลาสได้รับการตั้งค่าอย่างเหมาะสม
handler
s คืออ็อบเจ็กต์ที่เก็บฟังก์ชันที่ดำเนินการบางอย่างเมื่อมีเหตุการณ์เฉพาะเกิดขึ้นบนคอร์ ตัวจัดการแต่ละตัวจะเชื่อมโยงกับคอนซูเมอร์เฉพาะ และจะถูกลบออกหรือเพิ่มเข้ากับลูปเหตุการณ์ของคอร์ เมื่อคอนซูเมอร์ถูกลบหรือเพิ่มเข้ากับคอร์
คำจำกัดความของตัวจัดการเกิดขึ้นผ่านหนึ่งใน define-handler
, define-function-handler
, define-instruction
หรือ define-query
ซึ่งแต่ละอันก็ต่อยอดจากอันสุดท้ายเพื่อให้มีคำย่อที่กว้างกว่าสำหรับข้อกำหนดทั่วไป โปรดทราบว่าวิธีการที่ตัวจัดการรับเหตุการณ์จริงอาจแตกต่างกัน ดูเอกสารประกอบของ Deeds เพื่อดูว่ามีคลาสตัวจัดการใดบ้าง
สิ่งที่รวมอยู่ในโปรเจ็กต์ maiden คือระบบย่อยสองสามระบบที่ขยายฟังก์ชันการทำงานหลัก
โปรเจ็กต์ maiden ยังรวมไคลเอนต์มาตรฐานบางตัวที่สามารถใช้งานได้ทันที
สุดท้ายนี้ โปรเจ็กต์มีโมดูลตัวแทนจำนวนมากที่ให้ฟังก์ชันการทำงานที่มีประโยชน์สำหรับการสร้างแชทบอทและอื่นๆ สามารถใช้งานได้ทันทีเช่นกัน