Building plugins

การสร้าง Plugin ช่องทาง

คู่มือนี้สร้าง Plugin ช่องทางที่เชื่อมต่อ OpenClaw กับแพลตฟอร์ม รับส่งข้อความ โดยครอบคลุมความปลอดภัยของ DM, การจับคู่, เธรดการตอบกลับ และการส่งข้อความออก

สิ่งที่ Plugin ของคุณเป็นเจ้าของ

Plugin ช่องทางไม่ต้องติดตั้งเครื่องมือส่ง/แก้ไข/แสดงปฏิกิริยา โดยแกนหลักมีเครื่องมือ message ที่ใช้ร่วมกันหนึ่งรายการ Plugin ของคุณเป็นเจ้าของ:

  • การกำหนดค่า - การระบุบัญชีและวิซาร์ดการตั้งค่า
  • ความปลอดภัย - นโยบาย DM และรายการอนุญาต
  • การจับคู่ - ขั้นตอนการอนุมัติ DM
  • ไวยากรณ์เซสชัน - วิธีแมป ID การสนทนาเฉพาะผู้ให้บริการไปยังแชต พื้นฐาน, ID เธรด และทางเลือกสำรองระดับแม่
  • การส่งออก - การส่งข้อความ สื่อ และแบบสำรวจไปยังแพลตฟอร์ม
  • การจัดเธรด - วิธีกำหนดเธรดให้การตอบกลับ
  • การแสดงสถานะกำลังพิมพ์ของ Heartbeat - สัญญาณกำลังพิมพ์/ไม่ว่างที่เลือกใช้ได้สำหรับเป้าหมาย การส่ง Heartbeat

แกนหลักเป็นเจ้าของเครื่องมือข้อความที่ใช้ร่วมกัน การเชื่อมต่อพรอมต์ รูปแบบภายนอกของคีย์เซสชัน การจัดทำบัญชี :thread: ทั่วไป และการส่งต่อ

อะแดปเตอร์ข้อความ

เปิดเผยอะแดปเตอร์ message ที่มี defineChannelMessageAdapter จาก openclaw/plugin-sdk/channel-outbound ประกาศเฉพาะความสามารถในการส่งขั้นสุดท้ายแบบถาวร ที่ทรานสปอร์ตแบบเนทีฟของคุณรองรับจริง พร้อมการทดสอบสัญญาที่พิสูจน์ผลข้างเคียง ฝั่งเนทีฟและใบตอบรับที่ส่งคืน ให้การส่งข้อความ/สื่อชี้ไปยังฟังก์ชันทรานสปอร์ตเดียวกับที่อะแดปเตอร์ outbound เดิมใช้ สำหรับสัญญา API ฉบับเต็ม เมทริกซ์ความสามารถ กฎใบตอบรับ การทำให้การแสดงตัวอย่างสดเสร็จสิ้น นโยบายการตอบรับเมื่อได้รับ การทดสอบ และตารางการย้าย โปรดดู API การส่งออกของช่องทาง

หากอะแดปเตอร์ outbound ที่มีอยู่มีเมธอดส่งและข้อมูลเมตาความสามารถที่ถูกต้องอยู่แล้ว ให้สร้างอะแดปเตอร์ message ด้วย createChannelMessageAdapterFromOutbound(...) แทนการเขียนบริดจ์อีกชุดด้วยตนเอง การส่งผ่านอะแดปเตอร์จะคืนค่า MessageReceipt สำหรับ ID แบบเดิม ให้สร้างจาก listMessageReceiptPlatformIds(...) หรือ resolveMessageReceiptPrimaryId(...) แทนการเก็บฟิลด์ messageIds แยกกันแบบคู่ขนาน

ประกาศความสามารถแบบสดและตัวดำเนินการขั้นสุดท้ายอย่างแม่นยำ เพราะแกนหลักใช้ข้อมูลเหล่านี้เพื่อตัดสิน ว่าช่องทางทำอะไรได้บ้าง และความคลาดเคลื่อนระหว่างพฤติกรรมที่ประกาศกับพฤติกรรมจริงถือเป็น ความล้มเหลวของการทดสอบสัญญา:

พื้นผิว ค่า
message.live.capabilities draftPreview, previewFinalization, progressUpdates, nativeStreaming, quietFinalization
message.live.finalizer.capabilities finalEdit, normalFallback, discardPending, previewReceipt, retainOnAmbiguousFailure

ช่องทางที่ทำให้การแสดงตัวอย่างฉบับร่างเสร็จสิ้นในตำแหน่งเดิมควรกำหนดเส้นทางตรรกะรันไทม์ ผ่าน defineFinalizableLivePreviewAdapter(...) ร่วมกับ deliverWithFinalizableLivePreviewAdapter(...) และให้ความสามารถที่ประกาศ มีการทดสอบ verifyChannelMessageLiveCapabilityAdapterProofs(...) และ verifyChannelMessageLiveFinalizerProofs(...) รองรับ เพื่อไม่ให้พฤติกรรมการแสดงตัวอย่าง ความคืบหน้า การแก้ไข ทางเลือกสำรอง/การเก็บรักษา การล้างข้อมูล และใบตอบรับแบบเนทีฟคลาดเคลื่อน โดยไม่มีสัญญาณเตือน

ตัวรับขาเข้าที่เลื่อนการตอบรับของแพลตฟอร์มควรประกาศ message.receive.defaultAckPolicy และ supportedAckPolicies แทนการซ่อน เวลาการตอบรับไว้ในสถานะเฉพาะมอนิเตอร์ ครอบคลุมทุกนโยบายที่ประกาศด้วย verifyChannelMessageReceiveAckPolicyAdapterProofs(...)

ตัวช่วยตอบกลับเดิม เช่น dispatchInboundReplyWithBase และ recordInboundSessionAndDispatchReply ยังคงพร้อมใช้งานสำหรับตัวส่งต่อ ที่รองรับความเข้ากันได้ อย่าใช้ตัวช่วยเหล่านี้กับโค้ดช่องทางใหม่ ให้เริ่มจากอะแดปเตอร์ message, ใบตอบรับ และตัวช่วยวงจรชีวิตการรับ/ส่งบน openclaw/plugin-sdk/channel-outbound แทน

จุดรับขาเข้า (ทดลอง)

ช่องทางที่กำลังย้ายการให้สิทธิ์ขาเข้าสามารถใช้พาธย่อย openclaw/plugin-sdk/channel-ingress-runtime แบบทดลองจากพาธการรับของรันไทม์ ส่วนนี้รับข้อเท็จจริงของแพลตฟอร์ม รายการอนุญาตดิบ ตัวอธิบายเส้นทาง ข้อเท็จจริงของคำสั่ง และการกำหนดค่ากลุ่มการเข้าถึง จากนั้นส่งคืนภาพฉายของผู้ส่ง/เส้นทาง/คำสั่ง/การเปิดใช้งาน พร้อมกราฟจุดรับเข้าที่เรียงลำดับแล้ว ขณะที่การค้นหาบนแพลตฟอร์มและผลข้างเคียง ยังคงอยู่ใน Plugin ให้เก็บการปรับเอกลักษณ์ของ Plugin ให้อยู่ในรูปแบบมาตรฐานไว้ใน ตัวอธิบายที่ส่งให้ตัวแก้ค่า อย่าทำให้ค่าการจับคู่ดิบจากสถานะหรือการตัดสินใจ ที่แก้ค่าแล้วเป็นอนุกรม โปรดดู API จุดรับเข้าของช่องทาง สำหรับการออกแบบ API ขอบเขตความเป็นเจ้าของ และความคาดหวังในการทดสอบ

จุดรับเข้าแบบถาวรและการขจัดข้อมูลซ้ำจากการเล่นซ้ำ

ช่องทางที่นำจุดรับเข้าแบบถาวรมาใช้ควรใช้ createChannelIngressMonitor จาก openclaw/plugin-sdk/channel-outbound เว้นแต่จำเป็นต้องมีสัญญาการรับเข้า หรือการสูบข้อมูลที่แตกต่างอย่างมีนัยสำคัญ ให้เข้าคิวซองทรานสปอร์ตดิบที่ จุดคอขวดการรับเพียงจุดเดียว (ไม่ปรับให้อยู่ในรูปแบบมาตรฐาน ณ เวลาที่รับ) จำกัด การตอบรับของทรานสปอร์ตไว้หลังการผนวกแบบถาวรสำหรับทรานสปอร์ต Webhook สร้างช่องทาง แบบอนุกรมหนึ่งช่องต่อการสนทนา และทำเครื่องหมายเหตุการณ์ว่าเสร็จสมบูรณ์เมื่อการส่งต่อ รับไปใช้งาน คีย์หลักของคิวคือ (queue_name, event_id) และเมื่อเสร็จสมบูรณ์ จะทำให้แถวเป็นทูมสโตนแทนการลบ ดังนั้นการส่งซ้ำจากแพลตฟอร์มในภายหลังของ event_id เดิมจะถูกปฏิเสธอย่างถาวรตลอดช่วงเวลาการเก็บรักษาทูมสโตน โปรดดู API การส่งออกของช่องทาง สำหรับ API มอนิเตอร์และสัญญาการปิดระบบ

ทูมสโตนนั้นเป็นกฎการแบ่งชั้นสำหรับตัวป้องกันการเล่นซ้ำ (openclaw/plugin-sdk/persistent-dedupe): ช่องทางที่ระบายแล้วจะเก็บตัวป้องกัน การเล่นซ้ำแยกต่างหากเฉพาะเมื่อเอกลักษณ์หรือระยะเวลาเก็บรักษาของตัวป้องกันยาวนานกว่าของคิว — เช่น คีย์ข้อความเชิงตรรกะที่ต่างจาก ID การส่งของทรานสปอร์ต (Telegram ขจัดข้อมูลซ้ำของ chat_id:message_id เพราะการรวมแบบหน่วงสามารถทำให้ข้อความ ปรากฏขึ้นอีกครั้งภายใต้ update_id ใหม่) หรือมีช่วงเวลายาวนานกว่าการเก็บรักษา ทูมสโตนของช่องทาง หากคีย์ตัวป้องกันของคุณจะเท่ากับ event_id ของการระบาย ให้ลบตัวป้องกันเมื่อนำการระบายมาใช้ และกำหนดขนาด completedTtlMs/completedMaxEntries ให้ครอบคลุมช่วงเวลาของตัวป้องกันเดิมแทน การป้องกันที่ไม่ใช่การขจัดข้อมูลซ้ำ เช่น ขอบเขตอายุ ไม่เกี่ยวข้องกับกฎนี้ ID ข้อความขาออกที่เสถียรใช้รีจิสทรีเสียงสะท้อนขาออก ที่ใช้ร่วมกันจาก openclaw/plugin-sdk/channel-outbound แทนแคช TTL เฉพาะช่องทาง

คลาสทรานสปอร์ตและการเก็บรักษา

จำแนกทรานสปอร์ตตามการรับประกันการกู้คืนที่ขอบเขตการรับ:

  • Webhook หรือการส่งเหตุการณ์ที่จำกัดด้วยการตอบรับ: ตอบรับหรือส่งคืนความสำเร็จเฉพาะ หลังการผนวกแบบถาวรเท่านั้น ความล้มเหลวในการผนวกต้องทำให้การส่งยังมีสิทธิ์ ลองใหม่หรือทำให้ขอบเขตการรับล้มเหลว คลาสนี้รวมถึง Slack, SMS, Zalo, Microsoft Teams, Google Chat, LINE และ Synology Chat
  • การส่งแบบโพลหรือสตรีมที่รอให้เสร็จ: เลื่อนเคอร์เซอร์ระยะไกลหรือส่ง การตอบรับของทรานสปอร์ตหลังการผนวกเท่านั้น เมื่อไม่มีเคอร์เซอร์ที่ชัดเจน ให้คง การเรียกกลับการรับไว้แบบอนุกรมและรอให้เสร็จ เพื่อไม่ให้ความล้มเหลวในการผนวกทำให้ ลูปการรับทำงานล่วงหน้า Telegram แบบโพล, Signal และ Tlon ใช้คลาสนี้ ส่วนการส่งผ่าน Webhook ของ Telegram ใช้กฎที่จำกัดด้วยการตอบรับด้านบน
  • ซ็อกเก็ตที่เล่นซ้ำไม่ได้: IRC, Mattermost, Twitch และ Zalo Personal ไม่สามารถขอ ให้แพลตฟอร์มส่งเหตุการณ์ที่ยอมรับแล้วซ้ำได้ คิวถาวรของแพลตฟอร์มเหล่านี้ป้องกันช่วงเวลา ที่กระบวนการขัดข้องและรองรับการกู้คืนเมื่อเริ่มระบบภายในใหม่ ส่วนทูมสโตน ของรายการที่เสร็จสมบูรณ์แทบไม่มีผลต่อการเล่นซ้ำจากแพลตฟอร์ม

ใช้ 30 วันเป็นข้อกำหนด TTL ของทูมสโตนสำหรับทั้งระบบ ไม่ใช่ค่าเริ่มต้นของ SDK โดยปกติช่วงเวลาการส่งซ้ำที่มีปริมาณสูงใช้ขีดจำกัดรายการที่เสร็จสมบูรณ์ 20,000 รายการ ส่วนทรานสปอร์ตที่รอให้เสร็จและเล่นซ้ำไม่ได้ซึ่งมีปริมาณต่ำกว่าโดยปกติใช้ 1,000-2,000 รายการ ข้อยกเว้นปัจจุบันรวมถึงขีดจำกัด 4,096 รายการของ LINE, TTL รายการที่เสร็จสมบูรณ์ 24 ชั่วโมงของ SMS และการเก็บรักษารายการที่เสร็จสมบูรณ์แบบจำกัดจำนวนเท่านั้นของ Tlon ขีดจำกัดแถวที่ล้มเหลวอาจต่ำกว่าขีดจำกัดรายการที่เสร็จสมบูรณ์ด้วย ทั้ง TTL และขีดจำกัด จะตัดแถวออก ดังนั้นการเก็บรักษาที่มีผลจริงจะสิ้นสุดเมื่อถึงขอบเขตใดขอบเขตหนึ่งก่อน ให้เบี่ยงเบนเฉพาะเมื่อมีกรอบเวลาการลองใหม่ของแพลตฟอร์มที่บันทึกไว้ ช่วงเวลาตัวป้องกัน การเล่นซ้ำที่คงไว้จากเวอร์ชันที่เผยแพร่ ปริมาณหรือโควตาดิสก์ที่คาดไว้ หรือทรานสปอร์ต ที่เล่นซ้ำไม่ได้ และครอบคลุมสัญญาการเก็บรักษาด้วยการทดสอบ

ผลข้างเคียงแบบอย่างน้อยหนึ่งครั้ง

การส่งต่อระหว่างการระบายจะเรียกใช้ผลข้างเคียงของคำสั่งก่อนที่แถวจุดรับเข้าจะกลายเป็น ทูมสโตนที่เสร็จสมบูรณ์ หากกระบวนการขัดข้องระหว่างสองขั้นตอนนี้ แถวจะถูกเล่นซ้ำและ อาจเรียกใช้ผลข้างเคียงอีกครั้ง ช่วงเวลาขัดข้องแบบอย่างน้อยหนึ่งครั้งนี้คือสัญญาเริ่มต้น สำหรับงานที่ไม่เป็นไอดอมโพเทนต์ เช่น การเขียนการกำหนดค่า การล้างพื้นที่จัดเก็บ หรือการตอบรับที่มองเห็นได้นอกช่องทางตอบกลับ ให้ใช้ createIngressEffectOnce(...) จาก openclaw/plugin-sdk/ingress-effect-once ส่ง eventId ของจุดรับเข้าที่เสถียร พร้อมชื่อเอฟเฟกต์ให้แต่ละการเรียก สร้างตัวช่วยหนึ่งรายการต่อคิว/บัญชีของจุดรับเข้า และใช้ namespacePrefix ที่เสถียรและไม่ซ้ำกันสำหรับขอบเขตนั้น เนื่องจาก ID เหตุการณ์ ของทรานสปอร์ตอาจไม่ซ้ำกันเฉพาะภายในคิว ตัวช่วยจะคอมมิตการอ้างสิทธิ์แบบถาวร หลังเอฟเฟกต์สำเร็จเท่านั้น เอฟเฟกต์ที่ส่งข้อผิดพลาดจะปล่อยการอ้างสิทธิ์เพื่อให้การลองระบายใหม่ เรียกใช้ได้อีกครั้ง ขณะที่ผู้เรียกพร้อมกันจะรอการอ้างสิทธิ์ที่ทำงานอยู่ ข้อผิดพลาดของสถานะถาวร จะเรียก onDiskError เมื่อมีให้ และปฏิเสธแทนการถอยกลับไปใช้หน่วยความจำ ของกระบวนการ

ตั้งค่า ttlMs ของตัวช่วยให้เท่ากับอย่างน้อยระยะเวลาเก็บรักษาทูมสโตน ของจุดรับเข้าของช่องทางบวกกับระยะเวลาหน่วงสูงสุดระหว่างการคอมมิตเอฟเฟกต์และการทำให้แถว เสร็จสมบูรณ์ ซึ่งรวมถึงช่วงหยุดทำงานและการลองระบายใหม่ที่มีขอบเขต TTL ของระเบียนเอฟเฟกต์ เริ่มเมื่อคอมมิต ส่วนการเก็บรักษาทูมสโตนเริ่มภายหลังเมื่อเสร็จสมบูรณ์ หากอายุของแถว ที่รอดำเนินการไม่มีขอบเขต จะไม่มี TTL แบบจำกัดค่าใดครอบคลุมช่วงหยุดทำงานได้โดยพลการ หลังจากทูมสโตนไม่สามารถเล่นแถวซ้ำได้อีก ระเบียนเอฟเฟกต์ที่เก่ากว่านั้นจะเป็นข้อมูลส่วนเกิน กำหนดขนาด stateMaxEntries ให้รองรับทุกคีย์เหตุการณ์/เอฟเฟกต์ที่แตกต่างกันซึ่งอาจมีอยู่ ในช่วงเวลาการเก็บรักษานั้น โดยคำนึงถึงขีดจำกัดรายการที่เสร็จสมบูรณ์ของคิวและจำนวนเอฟเฟกต์ สูงสุดต่อเหตุการณ์ ขีดจำกัดที่ต่ำกว่าจะขับระเบียนเก่าที่สุดออกก่อน TTL และทำให้เอฟเฟกต์นั้น เรียกใช้ได้อีกครั้ง ช่วงเวลาแบบอย่างน้อยหนึ่งครั้งที่เหลืออยู่ยังคงเกิดขึ้นได้ หากกระบวนการหยุดทำงาน หรือการคงข้อมูลล้มเหลวหลังเอฟเฟกต์สำเร็จแต่ก่อนคอมมิตการอ้างสิทธิ์ หรือหากระเบียนหมดอายุ ขณะที่แถวจุดรับเข้ายังคงรอดำเนินการ

สัญญาการเริ่มระบบใหม่ตามขอบเขตบัญชี

โดยค่าเริ่มต้น การเปลี่ยนแปลงการกำหนดค่าช่องทางจะเริ่มช่องทางทั้งหมดใหม่ ช่องทางแบบหลายบัญชี อาจตั้งค่า reload.accountScopedRestart: true ได้เฉพาะเมื่อการแก้ไข การกำหนดค่าอ่านฟิลด์ที่ใช้ร่วมกันทั้งช่องทางพร้อมกับบัญชีที่เลือกเท่านั้น และไม่อ่าน บัญชีข้างเคียง และ Gateway สามารถหยุดและเริ่มรันไทม์ (channel, accountId) หนึ่งรายการโดยไม่แทนที่รันไทม์ข้างเคียง

พาธตามขอบเขตใช้เฉพาะกับการเปลี่ยนแปลงภายใต้ channels.<channel>.accounts.<non-default-id>.* การเปลี่ยนแปลงฟิลด์ช่องทาง ที่ใช้ร่วมกัน, accounts.default, บัญชีที่ถูกลบหรือแก้ค่าไม่ได้ และการเปลี่ยนแปลง แบบผสมที่อาจกระทบการสืบทอด จะถูกยกระดับเป็นการเริ่มทั้งช่องทางใหม่ Plugin ที่ไม่เลือกใช้จะใช้พาธทั้งช่องทางเสมอ

สำหรับช่องทางที่ใช้การระบายจุดรับเข้าแบบถาวร พาธหยุดของมอนิเตอร์บัญชี ต้องทำให้การรับเข้าของทรานสปอร์ตทั้งหมดที่ยอมรับแล้วเสร็จสิ้นก่อน จากนั้นจึงกำจัดและรอ การระบายของมอนิเตอร์ การเริ่มบัญชีจะเปิดคิวเดียวกันที่กำหนดคีย์ตามบัญชี ซึ่งการระบาย ครั้งแรกจะกู้คืนแถวถาวรที่ยังไม่ได้ส่งต่อ อย่าเพิ่มรอบการเล่นซ้ำชุดที่สองสำหรับการโหลดใหม่ โดยเฉพาะ การกู้คืนคิวคือพาธการเริ่มระบบใหม่ตามมาตรฐาน

ให้ถือว่าแฟล็กนี้เป็นการอ้างความสามารถ ไม่ใช่การตั้งค่าประสิทธิภาพ การทดสอบสัญญา ควรพิสูจน์ว่าการเพิ่มและแก้ไขบัญชีที่ระบุชื่อหนึ่งบัญชีไม่เปลี่ยนการกำหนดค่าที่แก้ค่าแล้ว ของบัญชีข้างเคียง การหยุดหนึ่งบัญชีทำให้เฉพาะมอนิเตอร์และการระบายของบัญชีนั้นเสร็จสิ้น และมอนิเตอร์ใหม่กู้คืนแถวของบัญชีนั้นได้เพียงครั้งเดียว หากพิสูจน์การรับประกันข้อใดไม่ได้ ให้งดแฟล็กนี้

ตัวบ่งชี้สถานะกำลังพิมพ์

หากช่องทางรองรับตัวบ่งชี้สถานะกำลังพิมพ์นอกการตอบกลับขาเข้า ให้เปิดเผย heartbeat.sendTyping(...) บน Plugin ช่องทาง แกนหลักจะเรียกส่วนนี้ด้วย เป้าหมายการส่ง Heartbeat ที่แก้ค่าแล้วก่อนเริ่มการทำงานของโมเดล Heartbeat และใช้ วงจรชีวิตการคงสถานะกำลังพิมพ์/การล้างข้อมูลที่ใช้ร่วมกัน เพิ่ม heartbeat.clearTyping(...) เมื่อแพลตฟอร์มต้องการสัญญาณหยุดที่ชัดเจน

พารามิเตอร์แหล่งที่มาของสื่อ

หากช่องทางเพิ่มพารามิเตอร์เครื่องมือข้อความที่นำพาแหล่งที่มาของสื่อ ให้เปิดเผย ชื่อพารามิเตอร์เหล่านั้นผ่าน plugin.actions.describeMessageTool(...).mediaSourceParams แกนหลักใช้รายการที่ระบุชัดเจนนี้สำหรับการปรับพาธ sandbox ให้อยู่ในรูปแบบมาตรฐานและ นโยบายการเข้าถึงสื่อขาออก เพื่อให้ Plugin ไม่จำเป็นต้องมีกรณีพิเศษในแกนหลักที่ใช้ร่วมกัน สำหรับพารามิเตอร์รูปประจำตัว ไฟล์แนบ หรือภาพหน้าปกที่เฉพาะเจาะจงกับผู้ให้บริการ

ควรใช้แมปที่มีแอ็กชันเป็นคีย์ เช่น { "set-profile": ["avatarUrl", "avatarPath"] } เพื่อไม่ให้แอ็กชันที่ไม่เกี่ยวข้องรับช่วงอาร์กิวเมนต์สื่อของแอ็กชันอื่น ส่วนอาร์เรย์แบบแบน ยังใช้ได้กับพารามิเตอร์ที่ตั้งใจให้ใช้ร่วมกันในทุกแอ็กชันที่เปิดเผย

ช่องทางที่ต้องเปิดเผย URL สาธารณะชั่วคราวเพื่อให้แพลตฟอร์มดึงข้อมูลสื่อ สามารถใช้ createHostedOutboundMediaStore(...) จาก openclaw/plugin-sdk/outbound-media ร่วมกับที่เก็บสถานะของ Plugin ได้ ให้การแยกวิเคราะห์ เส้นทางของแพลตฟอร์มและการบังคับใช้โทเค็นอยู่ใน Plugin ช่องทาง ส่วนตัวช่วยที่ใช้ร่วมกัน รับผิดชอบเฉพาะการโหลดสื่อ เมทาดาทาการหมดอายุ แถวของส่วนย่อย และการล้างข้อมูล

การจัดรูปเพย์โหลดแบบเนทีฟ

หากช่องทางต้องจัดรูปแบบเฉพาะผู้ให้บริการสำหรับ message(action="send") ควรใช้ actions.prepareSendPayload(...) เก็บการ์ด บล็อก เอ็มเบดแบบเนทีฟ หรือ ข้อมูลถาวรอื่นไว้ภายใต้ payload.channelData.<channel> และให้แกนหลักส่ง ผ่านอะแดปเตอร์ขาออก/ข้อความ ใช้ actions.handleAction(...) สำหรับการส่ง เฉพาะในฐานะทางเลือกสำรองเพื่อความเข้ากันได้สำหรับเพย์โหลดที่ไม่สามารถทำให้เป็นอนุกรมและ ลองส่งใหม่ได้

ไวยากรณ์การสนทนาของเซสชัน

หากแพลตฟอร์มเก็บขอบเขตเพิ่มเติมไว้ใน ID การสนทนา ให้การแยกวิเคราะห์นั้น อยู่ใน Plugin ด้วย messaging.resolveSessionConversation(...) นี่คือ ฮุกมาตรฐานสำหรับแมป rawId ไปยัง ID การสนทนาฐาน, ID เธรดที่ไม่บังคับ, baseConversationId ที่ระบุชัดเจน และ parentConversationCandidates ใดๆ เมื่อส่งคืน parentConversationCandidates ให้เรียงจากพาเรนต์ที่มีขอบเขตแคบที่สุดไปยังการสนทนาที่กว้างที่สุด/ฐาน

messaging.resolveParentConversationCandidates(...) เป็นทางเลือกสำรองเพื่อความเข้ากันได้ ที่เลิกใช้แล้วสำหรับ Plugin ที่ต้องการเพียงพาเรนต์สำรองเพิ่มเติมจาก ID ทั่วไป/ดิบ หากมีทั้งสองฮุก แกนหลักจะใช้ resolveSessionConversation(...).parentConversationCandidates ก่อน และจะ ถอยไปใช้ resolveParentConversationCandidates(...) เฉพาะเมื่อฮุกมาตรฐาน ไม่ได้ระบุค่าเหล่านั้น

Plugin ที่รวมมาในชุดซึ่งต้องใช้การแยกวิเคราะห์เดียวกันก่อนรีจิสทรีช่องทางเริ่มทำงาน สามารถเปิดเผยไฟล์ session-key-api.ts ระดับบนสุด พร้อมการส่งออก resolveSessionConversation(...) ที่ตรงกัน (ดู Plugin Feishu และ Telegram) แกนหลักใช้พื้นผิวที่ปลอดภัยสำหรับการบูตนั้น เฉพาะเมื่อรีจิสทรี Plugin รันไทม์ยังไม่พร้อมใช้งาน

ใช้ openclaw/plugin-sdk/channel-route เมื่อโค้ด Plugin ต้องปรับฟิลด์ ที่มีลักษณะคล้ายเส้นทางให้เป็นมาตรฐาน เปรียบเทียบเธรดย่อยกับเส้นทางพาเรนต์ หรือสร้าง คีย์ขจัดรายการซ้ำที่เสถียรจาก { channel, to, accountId, threadId } ตัวช่วยนี้ ปรับ ID เธรดแบบตัวเลขให้เป็นมาตรฐานด้วยวิธีเดียวกับแกนหลัก จึงควรใช้แทนการเปรียบเทียบ String(threadId) แบบเฉพาะกิจ Plugin ที่มีไวยากรณ์เป้าหมายเฉพาะผู้ให้บริการ ควรเปิดเผย messaging.resolveOutboundSessionRoute(...) เพื่อให้แกนหลักได้รับ อัตลักษณ์เซสชันและเธรดแบบเนทีฟของผู้ให้บริการโดยไม่ต้องใช้ชิมตัวแยกวิเคราะห์

การรองรับการผูกการสนทนาตามขอบเขตบัญชี

ตั้งค่า conversationBindings.supportsCurrentConversationBinding เมื่อช่องทาง รองรับการผูกการสนทนาปัจจุบันแบบทั่วไป createChatChannelPlugin(...) ตั้งค่าความสามารถแบบคงที่นี้เป็น true โดยค่าเริ่มต้น

หากการรองรับแตกต่างกันตามบัญชีที่กำหนดค่าไว้ ให้ใช้งาน conversationBindings.isCurrentConversationBindingSupported({ accountId }) เพิ่มเติม แกนหลักจะประเมินฮุกแบบซิงโครนัสนี้หลังจากเปิดใช้ความสามารถแบบคงที่แล้วเท่านั้น การส่งคืน false ทำให้ความสามารถ การผูก การค้นหา การแสดงรายการ การแตะ และการยกเลิกการผูกการสนทนาปัจจุบันแบบทั่วไป ใช้งานไม่ได้สำหรับบัญชีนั้น หากละเว้นฮุก ระบบจะใช้ความสามารถแบบคงที่กับทุกบัญชี

หาคำตอบจากการกำหนดค่าบัญชีหรือสถานะรันไทม์ที่โหลดไว้แล้ว ฮุกนี้ ควบคุมเฉพาะการผูกการสนทนาปัจจุบันแบบทั่วไป ไม่ได้แทนที่ กฎการผูกที่กำหนดค่าไว้หรือการกำหนดเส้นทางเซสชันที่ Plugin เป็นเจ้าของ การทดสอบสัญญา ควรครอบคลุมอย่างน้อยหนึ่งบัญชีที่รองรับและหนึ่งบัญชีที่ไม่รองรับ ผ่าน สัญญา ChannelPlugin["conversationBindings"] ที่ส่งออกโดย openclaw/plugin-sdk/channel-core

การอนุมัติและความสามารถของช่องทาง

Plugin ช่องทางส่วนใหญ่ไม่จำเป็นต้องมีโค้ดเฉพาะการอนุมัติ แกนหลักเป็นเจ้าของ /approve ในแชตเดียวกัน เพย์โหลดปุ่มอนุมัติที่ใช้ร่วมกัน และการส่งทางเลือกสำรองทั่วไป ChannelPlugin.approvals ถูกนำออกแล้ว ให้ใส่ข้อเท็จจริงด้านการส่ง/เนทีฟ/การเรนเดอร์/การยืนยันตัวตน สำหรับการอนุมัติไว้ในอ็อบเจ็กต์ approvalCapability เดียวแทน plugin.auth ใช้สำหรับ เข้าสู่ระบบ/ออกจากระบบเท่านั้น — แกนหลักจะไม่อ่านฮุกการยืนยันตัวตนสำหรับการอนุมัติจากอ็อบเจ็กต์นั้นอีกต่อไป

ใช้ approvalCapability.delivery เฉพาะสำหรับการกำหนดเส้นทางการอนุมัติแบบเนทีฟหรือ การระงับทางเลือกสำรอง และใช้ approvalCapability.render เฉพาะเมื่อช่องทางจำเป็นต้องใช้ เพย์โหลดการอนุมัติแบบกำหนดเองแทนตัวเรนเดอร์ที่ใช้ร่วมกันจริงๆ

การยืนยันตัวตนสำหรับการอนุมัติ

  • approvalCapability.authorizeActorAction และ approvalCapability.getActionAvailabilityState เป็นจุดเชื่อมมาตรฐาน สำหรับการยืนยันตัวตนเพื่ออนุมัติ
  • ใช้ getActionAvailabilityState สำหรับความพร้อมใช้งานของการยืนยันตัวตนเพื่ออนุมัติในแชตเดียวกัน ให้ผู้อนุมัติที่กำหนดค่าไว้ยังคงพร้อมใช้งานสำหรับ /approve แม้ปิดใช้งาน การส่งแบบเนทีฟ ให้ใช้สถานะพื้นผิวเริ่มต้นแบบเนทีฟสำหรับคำแนะนำด้านการส่ง/การตั้งค่า แทน
  • หากช่องทางเปิดเผยการอนุมัติการดำเนินการแบบเนทีฟ ให้ใช้ approvalCapability.getExecInitiatingSurfaceState สำหรับสถานะ พื้นผิวเริ่มต้น/ไคลเอนต์เนทีฟ เมื่อแตกต่างจากการยืนยันตัวตนเพื่ออนุมัติ ในแชตเดียวกัน แกนหลักใช้ฮุกเฉพาะการดำเนินการนั้นเพื่อแยก enabled ออกจาก disabled, ตัดสินว่าช่องทางเริ่มต้นรองรับการอนุมัติการดำเนินการ แบบเนทีฟหรือไม่ และรวมช่องทางไว้ในคำแนะนำทางเลือกสำรองของไคลเอนต์เนทีฟ createApproverRestrictedNativeApprovalCapability(...) เติมค่านี้ให้สำหรับ กรณีทั่วไป
  • หากช่องทางสามารถอนุมานอัตลักษณ์ DM ที่เสถียรและมีลักษณะเหมือนเจ้าของจากการกำหนดค่าที่มีอยู่ ให้ใช้ createResolvedApproverActionAuthAdapter จาก openclaw/plugin-sdk/approval-runtime เพื่อจำกัด /approve ในแชตเดียวกัน โดยไม่เพิ่มตรรกะแกนหลักที่เฉพาะกับการอนุมัติ
  • หากการยืนยันตัวตนเพื่ออนุมัติแบบกำหนดเองตั้งใจอนุญาตเฉพาะทางเลือกสำรองในแชตเดียวกัน ให้ส่งคืน markImplicitSameChatApprovalAuthorization({ authorized: true }) จาก openclaw/plugin-sdk/approval-auth-runtime มิฉะนั้นแกนหลักจะถือว่า ผลลัพธ์เป็นการให้อำนาจผู้อนุมัติอย่างชัดเจน
  • หากคอลแบ็กแบบเนทีฟที่ช่องทางเป็นเจ้าของแก้ไขการอนุมัติโดยตรง ให้ใช้ isImplicitSameChatApprovalAuthorization(...) ก่อนแก้ไข เพื่อให้ทางเลือกสำรอง โดยนัยยังคงผ่านการให้อำนาจผู้ดำเนินการตามปกติของช่องทาง

วงจรชีวิตของเพย์โหลดและคำแนะนำการตั้งค่า

  • ใช้ outbound.shouldSuppressLocalPayloadPrompt หรือ outbound.beforeDeliverPayload สำหรับพฤติกรรมวงจรชีวิตของเพย์โหลดเฉพาะช่องทาง เช่น การซ่อนพรอมต์การอนุมัติภายในที่ซ้ำกัน หรือการส่งตัวบ่งชี้ว่ากำลังพิมพ์ ก่อนส่ง
  • ใช้ approvalCapability.describeExecApprovalSetup เมื่อช่องทางต้องการ ให้การตอบกลับในเส้นทางที่ปิดใช้งานอธิบายตัวเลือกการกำหนดค่าที่แน่นอนซึ่งต้องใช้เพื่อเปิดใช้ การอนุมัติการดำเนินการแบบเนทีฟ ฮุกได้รับ { channel, channelLabel, accountId }; ช่องทางที่มีบัญชีแบบระบุชื่อควรเรนเดอร์พาธตามขอบเขตบัญชี เช่น channels.<channel>.accounts.<id>.execApprovals.* แทนค่าเริ่มต้น ระดับบนสุด
  • ใช้ approvalCapability.describePluginApprovalSetup เมื่อคำแนะนำกรณี การอนุมัติของ Plugin ล้มเหลว สามารถแสดงได้อย่างปลอดภัยสำหรับความล้มเหลวแบบไม่มีเส้นทางและหมดเวลา ของการอนุมัติ Plugin createApproverRestrictedNativeApprovalCapability(...) จะไม่ อนุมานค่านี้จาก describeExecApprovalSetup; ให้ส่งตัวช่วยเดียวกันอย่างชัดเจน เฉพาะเมื่อการอนุมัติ Plugin และการดำเนินการใช้การตั้งค่าเนทีฟเดียวกันจริงๆ

การส่งการอนุมัติแบบเนทีฟ

หากช่องทางต้องการการส่งการอนุมัติแบบเนทีฟ ให้โค้ดช่องทางมุ่งเน้นที่ การปรับเป้าหมายให้เป็นมาตรฐาน รวมถึงข้อเท็จจริงด้านการขนส่ง/การนำเสนอ ใช้ createChannelExecApprovalProfile, createChannelNativeOriginTargetResolver, createChannelApproverDmTargetResolver และ createApproverRestrictedNativeApprovalCapability จาก openclaw/plugin-sdk/approval-runtime เก็บข้อเท็จจริงเฉพาะช่องทางไว้เบื้องหลัง approvalCapability.nativeRuntime โดยควรผ่าน createChannelApprovalNativeRuntimeAdapter(...) หรือ createLazyChannelApprovalNativeRuntimeAdapter(...) เพื่อให้แกนหลักสามารถประกอบ ตัวจัดการและรับผิดชอบการกรองคำขอ การกำหนดเส้นทาง การขจัดรายการซ้ำ การหมดอายุ การสมัครรับข้อมูล Gateway และการแจ้งเตือนว่าถูกกำหนดเส้นทางไปที่อื่น

nativeRuntime ถูกแยกเป็นจุดเชื่อมขนาดเล็กหลายรายการ:

  • availability — บัญชีได้รับการกำหนดค่าแล้วหรือไม่ และควรจัดการคำขอหรือไม่
  • presentation — แมปโมเดลมุมมองการอนุมัติที่ใช้ร่วมกันเป็น เพย์โหลดเนทีฟที่รอดำเนินการ/แก้ไขแล้ว/หมดอายุ หรือแอ็กชันสุดท้าย
  • transport — เตรียมเป้าหมาย รวมถึงส่ง/อัปเดต/ลบข้อความ การอนุมัติแบบเนทีฟ
  • interactions — ฮุกการผูก/ยกเลิกการผูก/ล้างแอ็กชันที่ไม่บังคับสำหรับปุ่ม หรือรีแอ็กชันแบบเนทีฟ รวมถึงฮุก cancelDelivered ที่ไม่บังคับ ให้ใช้งาน cancelDelivered เมื่อ deliverPending ลงทะเบียนสถานะในกระบวนการหรือสถานะถาวร (เช่น ที่เก็บเป้าหมายรีแอ็กชัน) เพื่อให้สามารถปล่อยสถานะนั้นได้ หากการหยุด ตัวจัดการยกเลิกการส่งก่อน bindPending ทำงาน หรือเมื่อ bindPending ไม่ส่งคืนแฮนเดิล
  • observe — ฮุกการวินิจฉัยการส่งที่ไม่บังคับ

ตัวช่วยการอนุมัติอื่นๆ:

  • ใช้ createNativeApprovalChannelRouteGates จาก openclaw/plugin-sdk/approval-native-runtime เมื่อช่องทางรองรับทั้ง การส่งแบบเนทีฟจากต้นทางเซสชันและเป้าหมายส่งต่อการอนุมัติที่ระบุชัดเจน ตัวช่วยนี้ รวมการเลือกการกำหนดค่าการอนุมัติ การจัดการ mode, ตัวกรองเอเจนต์/เซสชัน การผูกบัญชี การจับคู่เป้าหมายเซสชัน และการจับคู่รายการเป้าหมายไว้ที่เดียว ขณะที่ผู้เรียกยังคงเป็นเจ้าของ ID ช่องทาง โหมดส่งต่อเริ่มต้น การค้นหา บัญชี การตรวจสอบว่าเปิดใช้การขนส่ง การปรับเป้าหมายให้เป็นมาตรฐาน และการแก้ไขเป้าหมาย จากต้นทางของเทิร์น อย่าใช้ตัวช่วยนี้สร้างค่าเริ่มต้นนโยบายช่องทางที่แกนหลักเป็นเจ้าของ ให้ส่งโหมดเริ่มต้นที่ช่องทางระบุไว้ในเอกสารอย่างชัดเจน
  • createChannelNativeOriginTargetResolver ใช้ตัวจับคู่เส้นทางช่องทางที่ใช้ร่วมกัน โดยค่าเริ่มต้นสำหรับเป้าหมาย { to, accountId, threadId } ส่ง targetsMatch เฉพาะเมื่อช่องทางมีกฎความเทียบเท่าเฉพาะผู้ให้บริการ เช่น การจับคู่คำนำหน้าไทม์สแตมป์ของ Slack ส่ง normalizeTargetForMatch เมื่อ ช่องทางต้องทำให้ ID ผู้ให้บริการเป็นรูปแบบมาตรฐานก่อนตัวจับคู่เส้นทาง เริ่มต้นหรือคอลแบ็ก targetsMatch แบบกำหนดเองทำงาน โดยยังคงเก็บ เป้าหมายเดิมไว้สำหรับการส่ง ใช้ normalizeTarget เฉพาะเมื่อเป้าหมาย การส่งที่แก้ไขแล้วควรถูกทำให้เป็นรูปแบบมาตรฐานด้วย
  • หากช่องทางต้องการอ็อบเจ็กต์ที่รันไทม์เป็นเจ้าของ เช่น ไคลเอนต์ โทเค็น แอป Bolt หรือตัวรับ Webhook ให้ลงทะเบียนผ่าน openclaw/plugin-sdk/channel-runtime-context รีจิสทรีบริบทรันไทม์ แบบทั่วไปช่วยให้แกนหลักบูตตัวจัดการที่ขับเคลื่อนด้วยความสามารถจากสถานะ เริ่มต้นของช่องทางได้ โดยไม่ต้องเพิ่มกาวตัวห่อเฉพาะการอนุมัติ
  • ใช้ createChannelApprovalHandler หรือ createChannelNativeApprovalRuntime ระดับล่างกว่า เฉพาะเมื่อจุดเชื่อมที่ขับเคลื่อน ด้วยความสามารถยังแสดงความต้องการได้ไม่เพียงพอ
  • ช่องทางการอนุมัติแบบเนทีฟต้องกำหนดเส้นทางทั้ง accountId และ approvalKind ผ่านตัวช่วยเหล่านั้น accountId ทำให้นโยบายการอนุมัติแบบหลายบัญชี อยู่ในขอบเขตบัญชีบอตที่ถูกต้อง และ approvalKind ทำให้พฤติกรรมการอนุมัติ แบบดำเนินการเทียบกับแบบ Plugin พร้อมใช้งานต่อช่องทาง โดยไม่มีสาขาที่ฮาร์ดโค้ดใน แกนหลัก
  • แกนหลักเป็นเจ้าของการแจ้งเตือนการเปลี่ยนเส้นทางการอนุมัติด้วย Plugin ช่องทางไม่ควรส่ง ข้อความติดตามของตนเองว่า "การอนุมัติถูกส่งไปยัง DM / ช่องทางอื่น" จาก createChannelNativeApprovalRuntime; ให้เปิดเผยการกำหนดเส้นทางต้นทาง + DM ของผู้อนุมัติอย่างถูกต้องผ่านตัวช่วยความสามารถการอนุมัติที่ใช้ร่วมกัน และให้ แกนหลักรวบรวมการส่งจริงก่อนโพสต์การแจ้งเตือนใดๆ กลับไปยัง แชตที่เริ่มต้น
  • รักษาชนิด ID การอนุมัติที่ส่งไว้ตั้งแต่ต้นจนจบ ไคลเอนต์เนทีฟไม่ควร คาดเดาหรือเขียนการกำหนดเส้นทางการอนุมัติแบบดำเนินการเทียบกับแบบ Plugin ใหม่จากสถานะ ภายในช่องทาง
  • ส่ง approvalKind ที่ระบุชัดเจนนั้นไปยัง resolveApprovalOverGateway วิธีนี้ใช้ บริการ approval.resolve มาตรฐาน และส่งคืนผู้ชนะที่บันทึกไว้เมื่อ พื้นผิวอื่นตอบก่อน อินพุต resolveMethod แบบระบุชัดเจนรุ่นเก่า ยังคงอยู่สำหรับตัวควบคุมที่มีคำสั่งเป็นฐาน แอ็กชันเนทีฟใหม่ต้องไม่ใช้ค่านี้หรือ อนุมานชนิดจาก ID
  • ชนิดการอนุมัติที่ต่างกันสามารถเปิดเผยพื้นผิวเนทีฟที่ต่างกันโดยตั้งใจ ตัวอย่างที่รวมมาในชุดปัจจุบัน: Matrix ใช้การกำหนดเส้นทาง DM/ช่องทางแบบเนทีฟ และ UX รีแอ็กชันเดียวกันสำหรับการอนุมัติแบบดำเนินการและแบบ Plugin ขณะยังอนุญาตให้ การยืนยันตัวตนแตกต่างกันตามชนิดการอนุมัติ ส่วน Slack ยังคงเปิดใช้การกำหนดเส้นทางการอนุมัติแบบเนทีฟ สำหรับทั้ง ID แบบดำเนินการและแบบ Plugin
  • createApproverRestrictedNativeApprovalAdapter ยังคงมีอยู่ในฐานะ ตัวห่อเพื่อความเข้ากันได้ แต่โค้ดใหม่ควรใช้ตัวสร้างความสามารถ และเปิดเผย approvalCapability บน Plugin

เส้นทางย่อยของรันไทม์การอนุมัติที่แคบกว่า

สำหรับจุดเข้าช่องทางที่มีการใช้งานสูง ควรใช้เส้นทางย่อยที่แคบกว่าเหล่านี้แทน barrel approval-runtime ที่กว้างกว่า เมื่อจำเป็นต้องใช้เพียงส่วนเดียวของกลุ่มนั้น:

  • openclaw/plugin-sdk/approval-auth-runtime
  • openclaw/plugin-sdk/approval-client-runtime
  • openclaw/plugin-sdk/approval-delivery-runtime
  • openclaw/plugin-sdk/approval-gateway-runtime
  • openclaw/plugin-sdk/approval-reference-runtime
  • openclaw/plugin-sdk/approval-handler-adapter-runtime
  • openclaw/plugin-sdk/approval-handler-runtime
  • openclaw/plugin-sdk/approval-native-runtime
  • openclaw/plugin-sdk/approval-reply-runtime
  • openclaw/plugin-sdk/channel-runtime-context

ในทำนองเดียวกัน ควรใช้ openclaw/plugin-sdk/reply-runtime, openclaw/plugin-sdk/reply-dispatch-runtime, openclaw/plugin-sdk/reply-reference และ openclaw/plugin-sdk/reply-chunking แทนพื้นผิวครอบคลุมที่กว้างกว่า เมื่อ ไม่จำเป็นต้องใช้ทั้งหมด

พาธย่อยสำหรับการตั้งค่า

  • openclaw/plugin-sdk/setup-runtime ครอบคลุมตัวช่วยการตั้งค่าที่ปลอดภัยสำหรับรันไทม์: createSetupTranslator, อะแดปเตอร์แพตช์การตั้งค่าที่นำเข้าได้อย่างปลอดภัย (createPatchedAccountSetupAdapter, createEnvPatchedAccountSetupAdapter, createSetupInputPresenceValidator), เอาต์พุตหมายเหตุการค้นหา, promptResolvedAllowFrom, splitSetupEntries และตัวสร้าง พร็อกซีการตั้งค่าที่มอบหมายงานต่อ
  • openclaw/plugin-sdk/channel-setup ครอบคลุมตัวสร้างการตั้งค่า สำหรับการติดตั้งแบบไม่บังคับ รวมถึงองค์ประกอบพื้นฐานที่ปลอดภัยสำหรับการตั้งค่าบางส่วน ได้แก่ createOptionalChannelSetupSurface, createOptionalChannelSetupAdapter, createOptionalChannelSetupWizard, DEFAULT_ACCOUNT_ID, createTopLevelChannelDmPolicy, setSetupChannelEnabled และ splitSetupEntries
  • ใช้ขอบเขตเชื่อมต่อ openclaw/plugin-sdk/setup ที่กว้างกว่าก็ต่อเมื่อคุณต้องการ ตัวช่วยการตั้งค่า/การกำหนดค่าที่ใช้ร่วมกันและมีขนาดใหญ่กว่าด้วย เช่น moveSingleAccountChannelSectionToDefaultAccount(...)

หากช่องของคุณเพียงต้องการแจ้งว่า "ติดตั้ง Plugin นี้ก่อน" บนพื้นผิว การตั้งค่า ให้เลือกใช้ createOptionalChannelSetupSurface(...) อะแดปเตอร์/วิซาร์ด ที่สร้างขึ้นจะปฏิเสธโดยค่าเริ่มต้นเมื่อเขียนการกำหนดค่าและดำเนินการให้เสร็จสิ้น และจะใช้ ข้อความแจ้งว่าต้องติดตั้งเดียวกันในการตรวจสอบ การดำเนินการให้เสร็จสิ้น และข้อความ ลิงก์เอกสาร

หากช่องของคุณรองรับการตั้งค่าหรือการยืนยันตัวตนที่ขับเคลื่อนด้วยตัวแปรสภาพแวดล้อม ให้เปิดเผยผ่าน สคีมาการกำหนดค่าช่องและตัวบรรยายการตั้งค่า เก็บ envVars ของรันไทม์ช่องหรือ ค่าคงที่ภายในไว้สำหรับข้อความที่แสดงแก่ผู้ดูแลระบบเท่านั้น

หากช่องของคุณอาจปรากฏใน status, channels list, channels status หรือ การสแกน SecretRef ก่อนที่รันไทม์ Plugin จะเริ่มทำงาน ให้เพิ่ม openclaw.setupEntry ใน package.json จุดเริ่มต้นนั้นควรนำเข้าได้อย่างปลอดภัยในพาธคำสั่ง แบบอ่านอย่างเดียว และควรส่งคืนข้อมูลเมตาของช่อง อะแดปเตอร์การกำหนดค่า ที่ปลอดภัยสำหรับการตั้งค่า อะแดปเตอร์สถานะ และข้อมูลเมตาเป้าหมายข้อมูลลับของช่องที่จำเป็นสำหรับ ข้อมูลสรุปเหล่านั้น ห้ามเริ่มไคลเอนต์ ตัวรับฟัง หรือรันไทม์การขนส่งจาก รายการตั้งค่า

รักษาพาธนำเข้าของรายการหลักของช่องให้แคบด้วยเช่นกัน การค้นพบสามารถประเมิน รายการและโมดูล Plugin ของช่องเพื่อลงทะเบียนความสามารถโดยไม่ต้อง เปิดใช้งานช่อง ไฟล์อย่างเช่น channel-plugin-api.ts ควรส่งออก ออบเจ็กต์ Plugin ของช่องโดยไม่นำเข้าวิซาร์ดการตั้งค่า ไคลเอนต์ การขนส่ง ตัวรับฟังซ็อกเก็ต ตัวเรียกใช้กระบวนการย่อย หรือโมดูลเริ่มต้นบริการ วางส่วนรันไทม์เหล่านั้นไว้ในโมดูลที่โหลดจาก registerFull(...), ตัวตั้งค่า รันไทม์ หรืออะแดปเตอร์ความสามารถแบบโหลดภายหลัง

พาธย่อยแบบแคบอื่น ๆ ของช่อง

สำหรับพาธด่วนอื่น ๆ ของช่อง ให้เลือกใช้ตัวช่วยแบบแคบแทนพื้นผิวเดิม ที่กว้างกว่า:

  • openclaw/plugin-sdk/account-core, openclaw/plugin-sdk/account-id, openclaw/plugin-sdk/account-resolution และ openclaw/plugin-sdk/account-helpers สำหรับการกำหนดค่าหลายบัญชีและ การย้อนกลับไปใช้บัญชีเริ่มต้น
  • openclaw/plugin-sdk/inbound-envelope และ openclaw/plugin-sdk/channel-inbound สำหรับเส้นทาง/ซองขาเข้าและ การเชื่อมต่อการบันทึกและส่งต่อ
  • openclaw/plugin-sdk/channel-targets สำหรับตัวช่วยแยกวิเคราะห์เป้าหมาย
  • openclaw/plugin-sdk/channel-outbound สำหรับตัวแทนการระบุตัวตน/การส่งขาออก และการวางแผนเพย์โหลดที่มีชนิดข้อมูล
  • buildThreadAwareOutboundSessionRoute(...) จาก openclaw/plugin-sdk/channel-core เมื่อเส้นทางขาออกควรรักษา replyToId/threadId ที่ระบุไว้อย่างชัดเจน หรือกู้คืนเซสชัน :thread: ปัจจุบันหลังจากคีย์เซสชันฐานยังคงตรงกัน Plugin ของผู้ให้บริการสามารถ แทนที่ลำดับความสำคัญ ลักษณะการทำงานของส่วนต่อท้าย และการปรับ ID เธรดให้เป็นมาตรฐานได้ เมื่อ แพลตฟอร์มของตนมีความหมายเชิงการส่งมอบเธรดแบบเนทีฟ
  • openclaw/plugin-sdk/thread-bindings-runtime สำหรับวงจรชีวิตการผูกเธรด และการลงทะเบียนอะแดปเตอร์

โดยทั่วไปช่องที่ใช้เฉพาะการยืนยันตัวตนสามารถหยุดที่พาธเริ่มต้นได้: แกนหลักจัดการ การอนุมัติ และ Plugin เพียงเปิดเผยความสามารถขาออก/การยืนยันตัวตน ช่อง การอนุมัติแบบเนทีฟ เช่น Matrix, Slack, Telegram และการขนส่งแชตแบบกำหนดเอง ควรใช้ตัวช่วยแบบเนทีฟที่ใช้ร่วมกันแทนการสร้างวงจรชีวิตการอนุมัติ ขึ้นเอง

นโยบายการกล่าวถึงขาเข้า

แยกการจัดการการกล่าวถึงขาเข้าออกเป็นสองชั้น:

  • การรวบรวมหลักฐานที่ Plugin เป็นเจ้าของ
  • การประเมินนโยบายที่ใช้ร่วมกัน

ใช้ openclaw/plugin-sdk/channel-mention-gating สำหรับการตัดสินใจตามนโยบายการกล่าวถึง ใช้ openclaw/plugin-sdk/channel-inbound เฉพาะเมื่อคุณต้องการบาร์เรล ตัวช่วยขาเข้าที่กว้างกว่า

เหมาะสำหรับตรรกะภายใน Plugin:

  • การตรวจจับการตอบกลับบอต
  • การตรวจจับการอ้างข้อความของบอต
  • การตรวจสอบการมีส่วนร่วมในเธรด
  • การยกเว้นข้อความบริการ/ระบบ
  • แคชแบบเนทีฟของแพลตฟอร์มที่จำเป็นต่อการยืนยันการมีส่วนร่วมของบอต

เหมาะสำหรับตัวช่วยที่ใช้ร่วมกัน:

  • requireMention
  • ผลลัพธ์การกล่าวถึงอย่างชัดเจน
  • รายการอนุญาตการกล่าวถึงโดยนัย
  • การข้ามด้วยคำสั่ง
  • การตัดสินใจข้ามขั้นสุดท้าย

ลำดับการทำงานที่แนะนำ:

  1. คำนวณข้อเท็จจริงการกล่าวถึงภายใน
  2. ส่งข้อเท็จจริงเหล่านั้นไปยัง resolveInboundMentionDecision({ facts, policy })
  3. ใช้ decision.effectiveWasMentioned, decision.shouldBypassMention และ decision.shouldSkip ในด่านตรวจขาเข้าของคุณ
typescript
   implicitMentionKindWhen,  matchesMentionWithExplicit,  resolveInboundMentionDecision,} from "openclaw/plugin-sdk/channel-inbound"; const wasMentioned = matchesMentionWithExplicit({  text,  mentionRegexes,  explicit: {    hasAnyMention,    isExplicitlyMentioned,    canResolveExplicit,  },}); const facts = {  canDetectMention: true,  wasMentioned,  hasAnyMention,  implicitMentionKinds: [    ...implicitMentionKindWhen("reply_to_bot", isReplyToBot),    ...implicitMentionKindWhen("quoted_bot", isQuoteOfBot),  ],}; const implicitMentions = resolveChannelImplicitMentions({  cfg,  channel: channelId,  accountId,}); const decision = resolveInboundMentionDecision({  facts,  policy: {    isGroup,    requireMention,    implicitMentions,    allowTextCommands,    hasControlCommand,    commandAuthorized,  },}); if (decision.shouldSkip) return;

matchesMentionWithExplicit(...) ส่งคืนค่าบูลีน hasAnyMention, isExplicitlyMentioned และ canResolveExplicit มาจากข้อมูลเมตาการกล่าวถึงแบบเนทีฟ ของช่องเอง (เอนทิตีข้อความ แฟล็กตอบกลับบอต และสิ่งที่คล้ายกัน); ระบุค่า false/undefined เมื่อแพลตฟอร์มของคุณตรวจจับค่าเหล่านี้ไม่ได้

api.runtime.channel.mentions เปิดเผยตัวช่วยการกล่าวถึงที่ใช้ร่วมกันชุดเดียวกันสำหรับ Plugin ช่องที่รวมมาในชุดและพึ่งพาการฉีดรันไทม์อยู่แล้ว: buildMentionRegexes, matchesMentionPatterns, matchesMentionWithExplicit, implicitMentionKindWhen, resolveInboundMentionDecision

หากคุณต้องการเพียง implicitMentionKindWhen และ resolveInboundMentionDecision ให้นำเข้าจาก openclaw/plugin-sdk/channel-mention-gating เพื่อหลีกเลี่ยงการโหลด ตัวช่วยรันไทม์ขาเข้าที่ไม่เกี่ยวข้อง

คำแนะนำแบบทีละขั้นตอน

  • แพ็กเกจและแมนิเฟสต์

    สร้างไฟล์ Plugin มาตรฐาน ฟิลด์ channels ใน openclaw.plugin.json (ไม่ใช่ฟิลด์ kind) คือสิ่งที่ระบุว่าแมนิเฟสต์ เป็นเจ้าของช่อง สำหรับพื้นผิวข้อมูลเมตาแพ็กเกจทั้งหมด โปรดดู การตั้งค่าและการกำหนดค่า Plugin:

    package.json
    {"name": "@myorg/openclaw-acme-chat","version": "1.0.0","type": "module","openclaw": {  "extensions": ["./index.ts"],  "setupEntry": "./setup-entry.ts",  "channel": {    "id": "acme-chat",    "label": "Acme Chat",    "blurb": "เชื่อมต่อ OpenClaw กับ Acme Chat"  }}}
    openclaw.plugin.json
    {"id": "acme-chat","channels": ["acme-chat"],"name": "Acme Chat","description": "Plugin ช่อง Acme Chat","configSchema": {  "type": "object",  "additionalProperties": false,  "properties": {}},"channelConfigs": {  "acme-chat": {    "schema": {      "type": "object",      "additionalProperties": false,      "properties": {        "token": { "type": "string" },        "allowFrom": {          "type": "array",          "items": { "type": "string" }        }      }    },    "uiHints": {      "token": {        "label": "โทเค็นบอต",        "sensitive": true      }    }  }}}

    configSchema ตรวจสอบความถูกต้องของ plugins.entries.acme-chat.config ใช้สำหรับ การตั้งค่าที่ Plugin เป็นเจ้าของและไม่ใช่การกำหนดค่าบัญชีช่อง channelConfigs.acme-chat.schema ตรวจสอบความถูกต้องของ channels.acme-chat และเป็น แหล่งข้อมูลพาธเย็นที่สคีมาการกำหนดค่า การตั้งค่า และพื้นผิว UI ใช้ก่อนที่ รันไทม์ Plugin จะโหลด โปรดดู แมนิเฟสต์ Plugin สำหรับข้อมูลอ้างอิง ฟิลด์ระดับบนสุดทั้งหมด

  • สร้างออบเจ็กต์ Plugin ของช่อง

    อินเทอร์เฟซ ChannelPlugin มีพื้นผิวอะแดปเตอร์แบบไม่บังคับจำนวนมาก เริ่มจาก ขั้นต่ำ ได้แก่ id, config และ setup แล้วเพิ่มอะแดปเตอร์เมื่อคุณต้องการ ใช้งาน

    สร้าง src/channel.ts:

    src/channel.ts
    import {  createChatChannelPlugin,  createChannelPluginBase,} from "openclaw/plugin-sdk/channel-core";import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core";import { acmeChatApi } from "./client.js"; // ไคลเอนต์ API ของแพลตฟอร์มคุณ type ResolvedAccount = {  accountId: string | null;  token: string;  allowFrom: string[];  dmPolicy: string | undefined;}; function resolveAccount(  cfg: OpenClawConfig,  accountId?: string | null,): ResolvedAccount {  const section = (cfg.channels as Record<string, any>)?.["acme-chat"];  const token = section?.token;  if (!token) throw new Error("acme-chat: ต้องระบุโทเค็น");  return {    accountId: accountId ?? null,    token,    allowFrom: section?.allowFrom ?? [],    dmPolicy: section?.dmSecurity,  };} export const acmeChatPlugin = createChatChannelPlugin&lt;ResolvedAccount&gt;({  base: createChannelPluginBase({    id: "acme-chat",    // การแก้ไข/ตรวจสอบบัญชีอยู่ใน `config` ไม่ใช่ `setup`    // `setup` ครอบคลุมการเขียนระหว่างการเริ่มต้นใช้งาน (applyAccountConfig, validateInput)    config: {      listAccountIds: () => ["default"],      resolveAccount,      inspectAccount(cfg, accountId) {        const section =          (cfg.channels as Record<string, any>)?.["acme-chat"];        return {          enabled: Boolean(section?.token),          configured: Boolean(section?.token),          tokenStatus: section?.token ? "available" : "missing",        };      },    },    setup: {      applyAccountConfig: ({ cfg, input }) => ({        ...cfg,        channels: {          ...cfg.channels,          "acme-chat": { ...(cfg.channels as any)?.["acme-chat"], ...input },        },      }),    },  }),   // ความปลอดภัยของ DM: ผู้ที่สามารถส่งข้อความถึงบอตได้  security: {    dm: {      channelKey: "acme-chat",      resolvePolicy: (account) => account.dmPolicy,      resolveAllowFrom: (account) => account.allowFrom,      defaultPolicy: "allowlist",    },  },   // การจับคู่: ขั้นตอนการอนุมัติสำหรับผู้ติดต่อ DM รายใหม่  pairing: {    text: {      idLabel: "ชื่อผู้ใช้ Acme Chat",      message: "ส่งรหัสนี้เพื่อยืนยันตัวตนของคุณ:",      notify: async ({ target, code }) => {        await acmeChatApi.sendDm(target, `รหัสจับคู่: ${code}`);      },    },  },   // การจัดเธรด: วิธีส่งคำตอบ  threading: { topLevelReplyToMode: "reply" },   // ขาออก: ส่งข้อความไปยังแพลตฟอร์ม  outbound: {    attachedResults: {      channel: "acme-chat",      sendText: async (params) => {        const result = await acmeChatApi.sendMessage(          params.to,          params.text,        );        return { messageId: result.id };      },    },    base: {      sendMedia: async (params) => {        await acmeChatApi.sendFile(params.to, params.filePath);      },    },  },});

    สำหรับช่องทางที่รองรับทั้งคีย์ DM ระดับบนสุดแบบมาตรฐานและคีย์แบบซ้อนที่เลิกใช้แล้ว ให้ใช้ตัวช่วยจาก plugin-sdk/channel-config-helpers: resolveChannelDmAccess, resolveChannelDmPolicy, resolveChannelDmAllowFrom และ normalizeChannelDmPolicy จะจัดให้ค่าภายในบัญชีมีลำดับความสำคัญเหนือค่ารากที่สืบทอดมา ใช้ตัวแก้ไขเดียวกันร่วมกับการซ่อมแซมของ doctor ผ่าน normalizeLegacyDmAliases เพื่อให้รันไทม์และการย้ายข้อมูลอ่านสัญญาเดียวกัน

    สิ่งที่ createChatChannelPlugin จัดการให้

    แทนที่จะติดตั้งใช้งานอินเทอร์เฟซอะแดปเตอร์ระดับต่ำด้วยตนเอง ให้ส่ง ตัวเลือกแบบประกาศ แล้วตัวสร้างจะประกอบตัวเลือกเหล่านั้นเข้าด้วยกัน:

    ตัวเลือก สิ่งที่เชื่อมต่อ
    security.dm ตัวแก้ไขความปลอดภัย DM แบบกำหนดขอบเขตจากฟิลด์การกำหนดค่า
    pairing.text ขั้นตอนการจับคู่ DM แบบข้อความพร้อมการแลกเปลี่ยนรหัส
    threading ตัวแก้ไขโหมดตอบกลับ (แบบคงที่ กำหนดขอบเขตตามบัญชี หรือกำหนดเอง)
    outbound.attachedResults ฟังก์ชันส่งที่คืนข้อมูลเมตาของผลลัพธ์ (รหัสข้อความ) ต้องมีรหัส channel คู่กันเพื่อให้แกนหลักประทับผลลัพธ์การส่งที่ส่งคืนได้

    นอกจากนี้ยังส่งออบเจ็กต์อะแดปเตอร์ดิบแทนตัวเลือกแบบประกาศได้ หากต้องการควบคุมอย่างเต็มรูปแบบ

    อะแดปเตอร์ขาออกแบบดิบอาจกำหนดฟังก์ชัน chunker(text, limit, ctx) ctx.formatting ซึ่งเป็นตัวเลือกจะเก็บการตัดสินใจด้านการจัดรูปแบบ ณ เวลาส่ง เช่น maxLinesPerMessage ให้นำไปใช้ก่อนส่ง เพื่อให้การจัดเธรดการตอบกลับ และขอบเขตของส่วนข้อความได้รับการแก้ไขเพียงครั้งเดียวโดยการส่งขาออกร่วมกัน บริบทการส่งยังรวม replyToIdSource (implicit หรือ explicit) เมื่อแก้ไขเป้าหมายการตอบกลับแบบเนทีฟแล้ว เพื่อให้ตัวช่วยเพย์โหลดสามารถคง แท็กตอบกลับที่ระบุอย่างชัดเจนไว้โดยไม่ใช้ช่องตอบกลับโดยนัยแบบใช้ครั้งเดียว

  • เชื่อมต่อจุดเริ่มต้น

    สร้าง index.ts:

    index.ts
    import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js"; export default defineChannelPluginEntry({  id: "acme-chat",  name: "Acme Chat",  description: "ปลั๊กอินช่องทาง Acme Chat",  plugin: acmeChatPlugin,  registerCliMetadata(api) {    api.registerCli(      ({ program }) => {        program          .command("acme-chat")          .description("การจัดการ Acme Chat");      },      {        descriptors: [          {            name: "acme-chat",            description: "การจัดการ Acme Chat",            hasSubcommands: false,          },        ],      },    );  },  registerFull(api) {    api.registerGatewayMethod(/* ... */);  },});

    ใส่ตัวอธิบาย CLI ที่ช่องทางเป็นเจ้าของไว้ใน registerCliMetadata(...) เพื่อให้ OpenClaw แสดงรายการเหล่านั้นในวิธีใช้ระดับรากได้โดยไม่ต้องเปิดใช้งานรันไทม์ช่องทางแบบเต็ม ขณะที่การโหลดแบบเต็มตามปกติยังคงรับตัวอธิบายชุดเดียวกันไปใช้สำหรับการลงทะเบียน คำสั่งจริง เก็บ registerFull(...) ไว้สำหรับงานเฉพาะรันไทม์ defineChannelPluginEntry จัดการการแยกโหมดการลงทะเบียนโดยอัตโนมัติ หาก registerFull(...) ลงทะเบียนเมธอด RPC ของ Gateway ให้ใช้ คำนำหน้าเฉพาะ Plugin เนมสเปซผู้ดูแลระบบของแกนหลัก (config.*, exec.approvals.*, wizard.*, update.*) ยังคงสงวนไว้และ แก้ไขเป็น operator.admin เสมอ ดูตัวเลือกทั้งหมดได้ที่ จุดเริ่มต้น

  • เพิ่มจุดเริ่มต้นสำหรับการตั้งค่า

    สร้าง setup-entry.ts สำหรับการโหลดแบบเบาระหว่างการเริ่มต้นใช้งาน:

    setup-entry.ts
    import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js"; export default defineSetupPluginEntry(acmeChatPlugin);

    OpenClaw จะโหลดรายการนี้แทนรายการแบบเต็มเมื่อช่องทางถูกปิดใช้งาน หรือยังไม่ได้กำหนดค่า วิธีนี้ช่วยหลีกเลี่ยงการโหลดโค้ดรันไทม์ขนาดใหญ่ระหว่างขั้นตอนการตั้งค่า ดูรายละเอียดที่ การตั้งค่าและการกำหนดค่า

    ช่องทางในเวิร์กสเปซที่รวมมาด้วยซึ่งแยกการส่งออกที่ปลอดภัยสำหรับการตั้งค่าไปยังโมดูล เสริม สามารถใช้ defineBundledChannelSetupEntry(...) จาก openclaw/plugin-sdk/channel-entry-contract เมื่อจำเป็นต้องมี ตัวตั้งค่ารันไทม์ขณะตั้งค่าอย่างชัดเจนด้วย

  • จัดการข้อความขาเข้า

    Plugin ต้องรับข้อความจากแพลตฟอร์มและส่งต่อไปยัง OpenClaw รูปแบบทั่วไปคือ Webhook ที่ตรวจสอบคำขอและ ส่งคำขอผ่านตัวจัดการขาเข้าของช่องทาง:

    typescript
    registerFull(api) {  api.registerHttpRoute({    path: "/acme-chat/webhook",    auth: "plugin", // การยืนยันตัวตนที่ Plugin จัดการ (ตรวจสอบลายเซ็นด้วยตนเอง)    handler: async (req, res) => {      const event = parseWebhookPayload(req);       // ตัวจัดการขาเข้าจะส่งข้อความไปยัง OpenClaw      // การเชื่อมต่อที่แน่นอนขึ้นอยู่กับ SDK ของแพลตฟอร์ม -      // ดูตัวอย่างจริงในแพ็กเกจ Plugin Microsoft Teams หรือ Google Chat ที่รวมมาด้วย      await handleAcmeChatInbound(api, event);       res.statusCode = 200;      res.end("ok");      return true;    },  });}
  • ทดสอบ

    เขียนการทดสอบที่อยู่ร่วมกันใน src/channel.test.ts:

    src/channel.test.ts
    import { describe, it, expect } from "vitest";import { acmeChatPlugin } from "./channel.js"; describe("ปลั๊กอิน acme-chat", () => {  it("แก้ไขบัญชีจากการกำหนดค่า", () => {    const cfg = {      channels: {        "acme-chat": { token: "test-token", allowFrom: ["user1"] },      },    } as any;    const account = acmeChatPlugin.config.resolveAccount(cfg, undefined);    expect(account.token).toBe("test-token");  });   it("ตรวจสอบบัญชีโดยไม่สร้างข้อมูลลับขึ้นจริง", () => {    const cfg = {      channels: { "acme-chat": { token: "test-token" } },    } as any;    const result = acmeChatPlugin.config.inspectAccount!(cfg, undefined);    expect(result.configured).toBe(true);    expect(result.tokenStatus).toBe("available");  });   it("รายงานการกำหนดค่าที่ขาดหายไป", () => {    const cfg = { channels: {} } as any;    const result = acmeChatPlugin.config.inspectAccount!(cfg, undefined);    expect(result.configured).toBe(false);  });});
    bash
    pnpm test <bundled-plugin-root>/acme-chat/

    สำหรับตัวช่วยทดสอบที่ใช้ร่วมกัน โปรดดู การทดสอบ

  • โครงสร้างไฟล์

    text
    <bundled-plugin-root>/acme-chat/├── package.json              # ข้อมูลเมตา openclaw.channel├── openclaw.plugin.json      # แมนิเฟสต์พร้อมสคีมาการกำหนดค่า├── index.ts                  # defineChannelPluginEntry├── setup-entry.ts            # defineSetupPluginEntry├── api.ts                    # การส่งออกสาธารณะ (ไม่บังคับ)├── runtime-api.ts            # การส่งออกรันไทม์ภายใน (ไม่บังคับ)└── src/    ├── channel.ts            # ChannelPlugin ผ่าน createChatChannelPlugin    ├── channel.test.ts       # การทดสอบ    ├── client.ts             # ไคลเอ็นต์ API ของแพลตฟอร์ม    └── runtime.ts            # ที่เก็บรันไทม์ (หากจำเป็น)

    หัวข้อขั้นสูง

    ขั้นตอนถัดไป

    ที่เกี่ยวข้อง

    Was this useful?
    On this page

    On this page