Flutter
แพ็กเกจ kavchat_flutter เพิ่มแชทบริการลูกค้าตามสคริปต์ให้แอป Flutter พร้อมปุ่ม การจับคู่ข้อความที่พิมพ์เอง แบบฟอร์มเปิดคำร้อง สำเนาบอตที่ทำงานได้แม้ไม่มีอินเทอร์เน็ต และการแสดงผลจากขวาไปซ้ายสำหรับภาษาฮีบรูและอาหรับ
การติดตั้ง
dependencies:
kavchat_flutter: ^0.0.11แพ็กเกจนี้มาพร้อม http, shared_preferences (สำหรับแคชเวอร์ชันและบทสนทนาที่บันทึกไว้) และ url_launcher (สำหรับลิงก์ในสคริปต์) หากต้องการเปิดลิงก์ tel: บน Android 11 ขึ้นไป ให้เพิ่มรายการ <queries> สำหรับสคีม tel ใน AndroidManifest.xml และเพิ่มอีกรายการสำหรับ https เพื่อเปิดลิงก์เว็บไซต์ (ดู README ของ url_launcher) ส่วน iOS ต้องใช้ LSApplicationQueriesSchemes เฉพาะเมื่อเรียก canLaunchUrl เองเท่านั้น
การใช้งาน
final kavchat = KavchatController(
publicKey: 'pk_live_...',
// apiBase: 'https://api.kavchat.com', // default
// locale: 'he', // default: device language if translated, else the bot language
onScreen: (screen, targetId) => navigatorKey.currentState?.pushNamed('/$screen', arguments: targetId),
);
Scaffold(
floatingActionButton: KavchatLauncher(controller: kavchat), // hidden while the bot is off
);
// or open it yourself
showKavchat(context, kavchat);
// or embed the screen in your own route
KavchatChat(controller: kavchat, onClose: () => Navigator.pop(context));สร้างคอนโทรลเลอร์เพียงครั้งเดียว (เช่น ใน State หรือ provider) และเรียก dispose() เมื่อออบเจ็กต์ที่เก็บคอนโทรลเลอร์นั้นสิ้นสุดการใช้งาน
KavchatLauncher เป็น FloatingActionButton โดยกำหนดตำแหน่งด้วย Scaffold.floatingActionButtonLocation ค่า kavchat.settings?.position (start หรือ end) ระบุด้านที่เลือกไว้ในแดชบอร์ด
เมื่อตั้งค่าข้อความข้างปุ่มแชทในหน้าจัดการ (เป็นกล่องคำพูด ข้อความโค้งเหนือปุ่ม ปุ่มที่ขยายเป็นรูปแคปซูลเพื่อแสดงข้อความ หรือเส้นที่ยื่นขึ้นจากปุ่มพร้อมตัวเลือกแรกของบอต) KavchatLauncher จะแสดงข้อความหลังพ้นระยะหน่วงที่ตั้งไว้ ไม่เกินวันละครั้ง การแตะตัวเลือกบนเส้นจะเปิดแชทพร้อมเลือกตัวเลือกนั้น เหมือนกับเลือกจากในแชท การปิดข้อความหรือเปิดแชทจะซ่อนข้อความจนถึงวันถัดไป ข้อความจะไม่แสดงขณะแชทเปิดอยู่ หากอุปกรณ์ตั้งค่าให้ลดการเคลื่อนไหว ข้อความจะแสดงโดยไม่มีการเคลื่อนไหว เมื่อเปิดตัวเลือก "ไม่แสดงบนโทรศัพท์" ข้อความจะแสดงเฉพาะบนแท็บเล็ต (หน้าจอด้านสั้นมีขนาดตั้งแต่ 600 จุดขึ้นไป) บนโทรศัพท์ เส้นจะแสดงได้ไม่เกินสองตัวเลือก ใช้ KavchatLauncher(controller: kavchat, attention: false) เพื่อปิดการแสดงข้อความนี้
หากไอคอนปุ่มในธีมเป็นรูปใบหน้า ใบหน้าจะกะพริบตาเป็นระยะเมื่อปุ่มอยู่นิ่ง โดยจะไม่กะพริบตาขณะแชทเปิดอยู่ แอปทำงานอยู่เบื้องหลัง หรืออุปกรณ์ตั้งค่าให้ลดการเคลื่อนไหว
บอตจะไม่แสดง (ปุ่มลอยไม่ปรากฏ และ KavchatChat แสดงว่า "ไม่พร้อมใช้งาน") เมื่อเวอร์ชันมีค่า enabled: false ซึ่งหมายถึงบอตถูกปิด เปิดให้เฉพาะผู้ใช้ทดสอบแต่ผู้ใช้รายนี้ไม่ได้อยู่ในกลุ่มทดสอบ หรือใช้โควตาหมดแล้ว (reason: 'quota')
โทเค็นระบุตัวตน
สำหรับผู้ใช้ที่เข้าสู่ระบบ ให้ลงนาม JWT แบบสั้นด้วยคีย์ลับบนเซิร์ฟเวอร์ของคุณ (ห้ามทำในแอป ดู การระบุตัวตนผู้ใช้) แล้วส่งโทเค็นดังนี้:
KavchatController(publicKey: 'pk_live_...', userToken: jwt, userName: 'Dana');
await kavchat.setUser(token: newJwt, name: 'Dana'); // after login
await kavchat.setUser(token: null); // after logoutโทเค็นจะถูกส่งในส่วนหัว Authorization: Bearer <token> ทุกครั้งที่เรียก เมื่อมีโทเค็น แบบฟอร์มคำร้องจะไม่บังคับกรอกอีเมลหรือเบอร์โทร และระบบจะระบุได้ว่าผู้ใช้อยู่ในกลุ่มผู้ใช้ทดสอบหรือไม่ setUser จะโหลดเวอร์ชันใหม่ และเมื่อเปลี่ยนผู้ใช้จะเริ่มบทสนทนาใหม่ บทสนทนาที่บันทึกในอุปกรณ์จึงไม่ถูกนำไปใช้กับบัญชีอื่น
ลิงก์ หน้าจอ และแบบฟอร์มคำร้อง
- ปุ่มประเภท
linkเปิดด้วยurl_launcherโดยรองรับเฉพาะที่อยู่https:และtel:ส่วนลิงก์ในข้อความของบอตรองรับเฉพาะhttps: - ปุ่มประเภท
screenจะเรียกonScreen(screen, targetId)หากไม่มีฟังก์ชันนี้ ปุ่มจะไม่ทำอะไร - ปุ่ม "ติดต่อทีมงาน" เปิดแบบฟอร์มในตัว (ชื่อ อีเมล เบอร์โทร หัวข้อ และข้อความ พร้อมแนบบทสนทนา) และแสดงหมายเลขคำร้องหลังส่ง
รายการหน้าจอในตัวแก้ไข
ประกาศหน้าจอที่แอปเปิดได้เพียงครั้งเดียวใน KavchatController(screens: ...) SDK จะส่งรายการไปยัง Kavchat เอง จากนั้นตัวแก้ไขสคริปต์จะแสดงชื่อหน้าจอเหล่านี้ในรายการแบบเลื่อนลงแทนช่องกรอกข้อความ นอกจากนี้ ตัวแก้ไขจะขอรหัสรายการเมื่อหน้าจอนั้นต้องใช้ และแจ้งเตือนเมื่อปุ่มนำไปยังหน้าจอที่ไม่ได้ประกาศไว้ ไม่ต้องพิมพ์รายการหน้าจอเองในแดชบอร์ด
final kavchat = KavchatController(
publicKey: 'pk_live_...',
screens: const [
KavchatScreen(id: 'orders', label: 'My orders'),
KavchatScreen(id: 'product', label: 'Product page', needsTarget: true), // the button carries a targetId
],
onScreen: (screen, targetId) => navigatorKey.currentState?.pushNamed('/$screen', arguments: targetId),
);
// later, to replace the list: await kavchat.registerScreens([...]);idคือค่าที่ส่งไปยังonScreenใช้ตัวอักษรละติน ตัวเลข_,.และ-ได้ไม่เกิน 40 ตัวอักษร ส่วนlabelคือชื่อที่ผู้ดูแลเห็น ยาวได้ไม่เกิน 60 ตัวอักษร รองรับสูงสุด 100 หน้าจอ รายการที่ไม่ถูกต้องและรหัสที่ซ้ำกันจะถูกตัดออกneedsTarget: ปุ่มจะมีรหัสรายการด้วย (เช่น รหัสสินค้า) และส่งไปยังonScreenในค่าtargetId- รายการจะถูกส่งในเบื้องหลังหลังโหลดเวอร์ชันเสร็จ เฉพาะเมื่อรายการเปลี่ยนแปลงหรือสัปดาห์ละครั้ง หากส่งไม่สำเร็จจะไม่มีการแจ้งเตือน และจะลองส่งอีกครั้งเมื่อเปิดแอปครั้งถัดไป ผู้ใช้จะไม่เห็นอะไร
- หน้าจอที่ลงทะเบียนไว้จะแสดงในแดชบอร์ดที่ บอต, การตั้งค่า, "หน้าจอแอป" พร้อมแพลตฟอร์มที่รายงานแต่ละหน้าจอ หากนำหน้าจอออกจากแอปแล้ว สามารถลบออกจากรายการนี้ได้
RTL
แชทกำหนด Directionality ของตัวเองตามภาษาอินเทอร์เฟซ โดย he และ ar ใช้ทิศทางจากขวาไปซ้าย ส่วนภาษาอื่นใช้จากซ้ายไปขวา โดยไม่เปลี่ยนทิศทางของแอป แต่ละกล่องข้อความยังเลือกทิศทางตามอักขระตัวแรกที่มีทิศทางชัดเจน เพื่อให้อ่านเนื้อหาที่มีหลายภาษาปะปนกันได้ถูกต้อง ภาษาอินเทอร์เฟซแยกจากภาษาสคริปต์ บอตภาษาอังกฤษจึงแสดงปุ่มภาษาอาหรับ เช่น "ย้อนกลับ" ได้เมื่อตั้งค่า locale: 'ar'
สีและแบรนด์
สีมาจากการตั้งค่าบอต primaryColor ข้อความบนสีนั้นจะเป็นสีดำหรือสีขาวตามความต่างระดับสีของ WCAG และหากสีนั้นอ่อนเกินไป จะเปลี่ยนเป็นสีเข้มในจุดที่ใช้เป็นสีข้อความ เปลี่ยนสีได้ดังนี้:
KavchatLauncher(controller: kavchat, theme: KavchatThemeData.fromColor(const Color(0xFF0F766E)));
KavchatChat(controller: kavchat, theme: KavchatThemeData.fromHex('#0f766e').copyWith(background: Colors.white));ป้าย "ขับเคลื่อนโดย Kavchat by maxsave" แสดงในทุกแพ็กเกจ พร้อมลิงก์ไปยัง kavchat.com สำหรับ Premium สามารถซ่อนป้ายได้ในการตั้งค่าบอต
แชทกับเจ้าหน้าที่
เริ่มใช้งานได้โดยไม่ต้องตั้งค่าเพิ่มเติม เมื่อเปิดแชทกับเจ้าหน้าที่ในหน้าจัดการ ตัวเลือกนี้จะปรากฏในแชทของแอปเหมือนกับวิดเจ็ตบนเว็บไซต์
- ตัวเลือก: "คุยกับเจ้าหน้าที่" จะแสดงก่อน "ติดต่อทีมงาน" และแสดงเฉพาะเมื่อเซิร์ฟเวอร์ยืนยันว่ามีเจ้าหน้าที่ว่าง ระบบจะตรวจสอบอีกครั้งเมื่อแตะปุ่ม นอกเวลาทำการหรือเมื่อเจ้าหน้าที่ทุกคนไม่ว่าง จะแสดงเฉพาะแบบฟอร์มคำร้องพร้อมคำอธิบายสั้น ๆ และเวลาที่ทีมงานจะกลับมา หากทราบ เมื่อใช้โควตารายเดือนหมดแล้ว แบบฟอร์มจะแสดงโดยไม่มีคำอธิบาย
- การขอแชท: ผู้ใช้ที่ไม่ได้เข้าสู่ระบบจะฝากอีเมลหรือเบอร์โทรไว้ เผื่อไม่มีเจ้าหน้าที่ว่างภายในเวลารอ โดยเพิ่มชื่อและข้อความแรกได้ด้วย หากมีแผนกเปิดอยู่มากกว่าหนึ่งแผนก ให้เลือกแผนก ผู้ใช้ที่ระบุตัวตนแล้ว (
userToken) ไม่ต้องกรอกข้อมูลติดต่อ หากไม่มีเจ้าหน้าที่รับแชทได้ ระบบจะเปิดแบบฟอร์มคำร้องพร้อมข้อมูลที่กรอกไว้แล้ว - ระหว่างรอ: แสดงข้อความ "กำลังรอเจ้าหน้าที่" พร้อมนับถอยหลังตามเวลารอที่ตั้งไว้ และปุ่ม "ฝากข้อความแทน" กับ "จบการสนทนา" ผู้ใช้พิมพ์ข้อความระหว่างรอได้ เมื่อหมดเวลา บทสนทนาจะเปลี่ยนเป็นคำร้องโดยอัตโนมัติ พร้อมแสดงหมายเลขคำร้องและคำอธิบายว่าไม่มีเจ้าหน้าที่ว่างภายในเวลารอ
- การสนทนา: แสดงชื่อเจ้าหน้าที่ ข้อความ สถานะกำลังพิมพ์ การโอนไปยังเจ้าหน้าที่คนอื่น และการจบการสนทนา ทุกอย่างที่มาจากเซิร์ฟเวอร์จะแสดงเป็นข้อความธรรมดา ไม่มีลิงก์
- แบบเรียลไทม์: เชื่อมต่อด้วย Server-Sent Events ผ่านการตอบกลับแบบสตรีมของ
httpหากการเชื่อมต่อหลุด แชทจะเชื่อมต่อใหม่โดยค่อย ๆ เพิ่มระยะห่างระหว่างการลอง และดึงข้อมูลที่พลาดไปทั้งหมด จึงไม่มีข้อความสูญหายหรือแสดงซ้ำ บนเครือข่ายที่บล็อกการเชื่อมต่อประเภทนี้และบน Flutter Web แชทจะใช้การตรวจสอบซ้ำ (long-poll) ทุกข้อความมีรหัสกำกับ การลองส่งใหม่จึงไม่สร้างข้อความซ้ำ ข้อความที่ส่งไม่สำเร็จจะมีเครื่องหมายระบุ และแตะเพื่อส่งใหม่ได้ - การทำงานเบื้องหลังและการเปิดแอปใหม่: เมื่อแอปเข้าสู่เบื้องหลัง (
paused,hidden) การเชื่อมต่อจะปิด เมื่อกลับมาใช้งาน (resumed) แชทจะดึงข้อมูลที่พลาดไป ข้อมูลรับรองการเข้าถึงแชทมีอายุสูงสุด 12 ชั่วโมงหรือจนกว่าการสนทนาจะจบ และเก็บไว้ในพื้นที่จัดเก็บที่เลือกไว้ เมื่อเปิดแอปใหม่ บทสนทนาที่ยังไม่จบจึงกลับมาแสดงบนหน้าจอ ข้อมูลรับรองนี้จะไม่ถูกเก็บใน URL หรือเขียนลงล็อก - เวอร์ชันนี้ไม่มีการแจ้งเตือนแบบพุช: ข้อความจากเจ้าหน้าที่จะมาถึงเฉพาะขณะที่แอปเปิดอยู่
แนะนำให้เก็บข้อมูลรับรองการเข้าถึงไว้ในพื้นที่จัดเก็บที่ปลอดภัย (Keychain บน iOS, Keystore บน Android) เช่น ใช้ flutter_secure_storage หากไม่ได้กำหนด secureStorage ข้อมูลรับรองจะถูกเก็บในพื้นที่เดียวกับบทสนทนา ปุ่มคุยกับเจ้าหน้าที่ที่สร้างเองจะเปิดแบบฟอร์มขอแชทเมื่อมีเจ้าหน้าที่ว่าง หากไม่มีเจ้าหน้าที่ว่าง จะเปิดแบบฟอร์มคำร้องพร้อมคำอธิบาย:
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureKavchatStorage implements KavchatStorage {
final _s = const FlutterSecureStorage();
@override
Future<String?> read(String key) => _s.read(key: key);
@override
Future<void> write(String key, String value) => _s.write(key: key, value: value);
@override
Future<void> remove(String key) => _s.delete(key: key);
}
final kavchat = KavchatController(
publicKey: 'pk_live_...',
secureStorage: SecureKavchatStorage(),
livePage: 'checkout',
onLivePhase: (phase) => debugPrint('live chat: $phase'),
);
// your own button
FilledButton(onPressed: kavchat.openLive, child: const Text('Chat with us'));ตรรกะเดียวกันที่ไม่ใช้ Flutter อยู่ใน kavchat_engine (KavchatClient, LiveSession และเครื่องสถานะที่ใช้ร่วมกัน liveApplyEvent, liveLines และ liveStatus ซึ่งผ่านชุดข้อมูลทดสอบเดียวกับวิดเจ็ตบนเว็บไซต์)
คำถาม เงื่อนไข และลีด
ไม่ต้องตั้งค่าในแอป สคริปต์สามารถขอข้อมูล (ชื่อ เบอร์โทร อีเมล ตัวเลข หรือข้อความ) เลือกขั้นตอนถัดไปตามเงื่อนไข (Premium) และส่งข้อมูลลีดได้ ทั้งหมดทำงานบนอุปกรณ์
- คีย์บอร์ดที่เหมาะกับข้อมูล: เมื่อบอตรอคำตอบ ช่องข้อความจะแสดงคีย์บอร์ดสำหรับเบอร์โทร อีเมล หรือตัวเลข พร้อมการเติมข้อความอัตโนมัติ หากคำตอบไม่ถูกต้อง บอตจะอธิบายว่าขาดอะไรและทำเครื่องหมายที่ช่องนั้น คำถามที่ข้ามได้จะแสดงปุ่ม "ข้าม"
- ลีด: เมื่อมีข้อความขอความยินยอม จะแสดงปุ่ม "ส่ง" และ "ไม่ ขอบคุณ" หากไม่มีการเชื่อมต่อเครือข่าย ข้อมูลลีดจะถูกบันทึกในอุปกรณ์และส่งเมื่อกลับมาเชื่อมต่อ โดยไม่ส่งซ้ำ
- การกรอกข้อมูลล่วงหน้า: คำตอบที่ผู้ใช้ให้ไว้แล้วจะถูกนำไปกรอกในแบบฟอร์มคำร้องและแบบฟอร์มขอแชทกับเจ้าหน้าที่ พร้อมแนบไปกับคำร้องและบทสนทนากับเจ้าหน้าที่ ชื่อ อีเมล และเบอร์โทรจากโทเค็นระบุตัวตนถือเป็นคำตอบที่ทราบแล้ว บอตจึงไม่ถามซ้ำ
- หน้าจอปัจจุบัน:
livePageยังระบุหน้าจอที่ใช้ตรวจสอบเงื่อนไขและส่งไปพร้อมข้อมูลลีด เมื่อผู้ใช้เปลี่ยนไปหน้าจออื่น ให้เรียกsetPage('checkout')
คำตอบจาก AI (Premium)
ไม่ต้องตั้งค่าในแอป เมื่อเปิดคำตอบจาก AI ในหน้าจัดการ (โดยใช้คีย์จากผู้ให้บริการ AI ของคุณเอง) คำถามที่ไม่มีสคริปต์ใดตอบได้จะถูกส่งไปยัง Kavchat ระหว่างรอสูงสุด 20 วินาที แชทจะแสดงว่าบอตกำลังพิมพ์ คำตอบจะแสดงเป็นข้อความธรรมดาพร้อมป้ายเล็ก ๆ ว่า "คำตอบจาก AI" ตามภาษาอินเทอร์เฟซ ด้านล่างมีปุ่มหัวข้อที่เกี่ยวข้องหากมี และปุ่ม "ติดต่อทีมงาน"
หากไม่มีคำตอบ หรือคำขอล้มเหลว ใช้เวลานานเกินไป หรือเกินขีดจำกัด (10 คำตอบต่อบทสนทนาในทุก 15 นาที) แชทจะแสดงข้อความ "ไม่พบคำตอบ" ตามปกติ คำถามจะยังปรากฏในรายงานคำถามที่ไม่มีคำตอบ และคำตอบจาก AI จะถูกเก็บในบทสนทนาที่บันทึกไว้เหมือนข้อความอื่น
การใช้งานออฟไลน์และแคช
เวอร์ชันจะถูกเก็บในแคชพร้อม ETag (If-None-Match, 304) เมื่อไม่มีเครือข่ายหรือเซิร์ฟเวอร์เกิดข้อผิดพลาด ระบบจะใช้สำเนาที่บันทึกไว้และแสดงข้อความแจ้ง บทสนทนาจะถูกบันทึกแยกตามบอตและกู้คืนได้ภายใน 24 ชั่วโมง ตราบใดที่เวอร์ชันไม่เปลี่ยน ลำดับการสนทนาจะถูกรายงานไปยังเซิร์ฟเวอร์หลังการกระทำล่าสุด 2 วินาที และเมื่อแอปเข้าสู่เบื้องหลัง