You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

568 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'dart:async';
import 'package:bytedesk_kefu/http/bytedesk_device_api.dart';
import 'package:bytedesk_kefu/http/bytedesk_thread_api.dart';
import 'package:bytedesk_kefu/model/app.dart';
import 'package:bytedesk_kefu/model/jsonResult.dart';
import 'package:bytedesk_kefu/model/thread.dart';
import 'package:bytedesk_kefu/model/userJsonResult.dart';
import 'package:bytedesk_kefu/model/wechatResult.dart';
import 'package:bytedesk_kefu/ui/channel/provider/channel_provider.dart';
import 'package:bytedesk_kefu/ui/chat/page/chat_webview_page.dart';
import 'package:bytedesk_kefu/ui/chat/provider/chat_im_provider.dart';
import 'package:bytedesk_kefu/ui/chat/provider/chat_thread_provider.dart';
import 'package:bytedesk_kefu/ui/faq/provider/help_provider.dart';
import 'package:bytedesk_kefu/ui/feedback/provider/feedback_provider.dart';
import 'package:bytedesk_kefu/ui/leavemsg/provider/leavemsg_provider.dart';
import 'package:bytedesk_kefu/ui/ticket/provider/ticket_provider.dart';
import 'package:flutter/services.dart';
import 'package:bytedesk_kefu/http/bytedesk_user_api.dart';
import 'package:bytedesk_kefu/ui/chat/provider/chat_kf_provider.dart';
import 'package:bytedesk_kefu/util/bytedesk_utils.dart';
import 'package:bytedesk_kefu/util/bytedesk_constants.dart';
import 'package:sp_util/sp_util.dart';
import 'package:flutter/material.dart';
import 'model/user.dart';
class BytedeskKefu {
//
static const MethodChannel _channel = const MethodChannel('bytedesk_kefu');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
// 下面为 自定义接口
static void init(String appKey, String subDomain) async {
anonymousLogin(appKey, subDomain);
}
// 支持自定义用户名方便跟APP业务系统对接
static void initWithUsername(
String username, String appKey, String subDomain) async {
initWithUsernameAndNickname(username, "", appKey, subDomain);
}
static void initWithUsernameAndNickname(
String username, String nickname, String appKey, String subDomain) async {
initWithUsernameAndNicknameAndAvatar(
username, nickname, "", appKey, subDomain);
}
static void initWithUsernameAndNicknameAndAvatar(String username,
String nickname, String avatar, String appKey, String subDomain) async {
/// sp初始化
await SpUtil.getInstance();
// 首先检测是否是第一次,如果是第一此启动
String? spusername = SpUtil.getString(BytedeskConstants.username);
String? sppassword = SpUtil.getString(BytedeskConstants.password);
if (spusername!.isNotEmpty) {
// 登录
userLogin(spusername, sppassword!, appKey, subDomain);
} else {
// 调用注册接口
// 默认密码同用户名
String password = username;
await BytedeskUserHttpApi()
.registerUser(username, nickname, password, avatar, subDomain);
// 注册成功之后,调用登录接口
String usernameCompose = username + "@" + subDomain;
userLogin(usernameCompose, password, appKey, subDomain);
}
}
// 判断是否已经登录
static bool isLogin() {
String? accessToken = SpUtil.getString(BytedeskConstants.accessToken);
if (accessToken!.isEmpty) {
return false;
}
return true;
}
// 访客匿名登录接口
static void anonymousLogin(String appKey, String subDomain) async {
/// sp初始化
await SpUtil.getInstance();
// 首先检测是否是第一次,如果是第一此启动
String? username = SpUtil.getString(BytedeskConstants.username);
String? unionid = SpUtil.getString(BytedeskConstants.unionid);
if (username!.isEmpty && unionid!.isEmpty) {
// 第一此启动, 则调用注册接口,否则调用登录接口
User user = await BytedeskUserHttpApi().registerAnonymous(subDomain);
visitorLogin(user.username!, appKey, subDomain);
} else if (username.isNotEmpty) {
// 用户名登录,非微信登录
visitorLogin(username, appKey, subDomain);
} else if (unionid!.isNotEmpty) {
// 微信登录
unionidOAuth(unionid);
} else {
// 验证码登录其他账号,非当前匿名登录账户
otherOAuth();
}
}
// 匿名访客登录
static void visitorLogin(String username, String appkey, String subDomain) {
String password = username;
login(
username, password, appkey, subDomain, BytedeskConstants.ROLE_VISITOR);
}
// 开发者自定义用户名登录接口
static void userLogin(
String username, String password, String appkey, String subDomain) {
login(
username, password, appkey, subDomain, BytedeskConstants.ROLE_VISITOR);
}
// 用户名登录
static void login(String username, String password, String appkey,
String subDomain, String role) async {
//
SpUtil.putString(BytedeskConstants.role, role);
if (role == BytedeskConstants.ROLE_ADMIN) {
if (!username.contains("@")) {
username = username + "@" + subDomain;
}
}
await BytedeskUserHttpApi().oauth(username, password);
// 登录成功之后,建立长连接
connect();
// if (role == BytedeskConstants.ROLE_ADMIN) {
// // TODO: 如果是客服账号,加载个人信息
// }
// 上传设备信息
await BytedeskDeviceHttpApi().setDeviceInfo();
}
// 微信unionid登录
static void unionidOAuth(String unionid) async {
//
await BytedeskUserHttpApi().unionIdOAuth(unionid);
// 登录成功之后,建立长连接
connect();
// 上传设备信息
await BytedeskDeviceHttpApi().setDeviceInfo();
}
// 直接建立长连接
static void otherOAuth() async {
// 登录成功之后,建立长连接
connect();
// 上传设备信息
await BytedeskDeviceHttpApi().setDeviceInfo();
}
// 建立长连接
static bool connect() {
return BytedeskUtils.mqttConnect();
}
// 重连
static bool reconnect() {
return BytedeskUtils.mqttReConnect();
}
// 断开
static void disconnect() {
BytedeskUtils.mqttDisconnect();
}
// 判断长连接状态
static bool isConnected() {
return BytedeskUtils.isMqttConnected();
}
// 技能组客服会话
static void startWorkGroupChat(
BuildContext context, String wid, String title) {
startChatDefault(
context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, false);
}
static void startWorkGroupChatV2Robot(
BuildContext context, String wid, String title) {
startChatDefault(
context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, true);
}
// 发送附言消息
static void startWorkGroupChatPostscript(
BuildContext context, String wid, String title, String postScript) {
startChat(context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, '',
postScript, false, null, null);
}
// 电商接口,携带商品参数
// type/title/content/price/url/imageUrl/id/categoryCode
static void startWorkGroupChatShop(
BuildContext context, String wid, String title, String commodity) {
startChatShop(context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title,
commodity, null);
}
///调用订单信息
static void startWorkGroupChatOrderCallback(BuildContext context, String wid,
String title, ValueSetter<String> customCallback,Function btnCallBack) {
startChatShop(context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title,
'', customCallback,btnCallBack: btnCallBack);
}
///用于更新订单数据
static void updateGoods(String info) {
BytedeskUtils.goodsInfo.value = info;
}
static void startWorkGroupChatShopCallback(BuildContext context, String wid,
String title, String commodity, ValueSetter<String> customCallback) {
startChatShop(context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title,
commodity, customCallback);
}
// 指定客服会话
static void startAppointedChat(
BuildContext context, String uid, String title) {
startChatDefault(
context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title, false);
}
// 发送附言消息
static void startAppointedChatPostscript(
BuildContext context, String uid, String title, String postScript) {
startChat(context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title, '',
postScript, false, null, null);
}
// 电商接口,携带商品参数
static void startAppointedChatShop(
BuildContext context, String uid, String title, String commodity) {
startChatShop(context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title,
commodity, null);
}
static void startAppointedChatShopCallback(BuildContext context, String uid,
String title, String commodity, ValueSetter<String> customCallback) {
startChatShop(context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title,
commodity, customCallback);
}
// 默认设置商品信息和附言为空
static void startChatDefault(BuildContext context, String uuid, String type,
String title, bool isV2Robot) {
startChat(context, uuid, type, title, '', '', isV2Robot, null, null);
}
// 电商对话-自定义类型(技能组、指定客服)
static void startChatShop(BuildContext context, String uuid, String type,
String title, String commodity, ValueSetter<String>? customCallback,{Function? btnCallBack}) {
startChat(context, uuid, type, title, commodity, '', false, customCallback, btnCallBack);
}
// 发送附言消息-自定义类型(技能组、指定客服)
static void startChatPostscript(BuildContext context, String uuid,
String type, String title, String postScript) {
startChat(context, uuid, type, title, '', postScript, false, null, null);
}
// 客服会话-自定义类型(技能组、指定客服)
static void startChat(
BuildContext context,
String uuid,
String type,
String title,
String commodity,
String postScript,
bool isV2Robot,
ValueSetter<String>? customCallback,
Function? btnBack,
) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new ChatKFProvider(
wid: uuid,
aid: uuid,
type: type,
title: title,
custom: commodity,
postscript: postScript,
isV2Robot: isV2Robot,
customCallback: customCallback,
btnBack: btnBack,
);
}));
}
// 从历史会话或者点击通知栏进入
static void startChatThread(BuildContext context, Thread thread,
{String title = ''}) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new ChatThreadProvider(
thread: thread,
title: title,
);
}));
}
// 应用内打开H5客服页面
static void startH5Chat(BuildContext context, String url, String title) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new ChatWebViewPage(
url: url,
title: title,
);
}));
}
// 应用内打开网页
static void openWebView(BuildContext context, String url, String title) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new ChatWebViewPage(
url: url,
title: title,
);
}));
}
// TODO: 好友一对一聊天
static void startContactChat(BuildContext context, String cid, String title) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new ChatIMProvider(
// cid: cid,
title: title,
);
}));
}
// TODO: 好友一对一,携带商品参数
// static void startContactChatShop(
// BuildContext context, String cid, String title, String commodity) {
// Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
// return new ChatIMProvider(
// // cid: cid,
// title: title,
// custom: commodity,
// );
// }));
// }
// TODO: 客服端-进入接待访客对话页面
static void startChatThreadIM(BuildContext context, Thread thread) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new ChatIMProvider(
thread: thread,
isThread: true,
);
}));
}
// 常见问题列表
static void showFaq(BuildContext context, String uid) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new HelpProvider(uid: uid);
}));
}
// 意见反馈
static void showFeedback(BuildContext context, String uid) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new FeedbackProvider(uid: uid);
}));
}
// TODO: 提交工单
static void showTicket(BuildContext context, String uid) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new TicketProvider(uid: uid);
}));
}
// TODO: 留言
static void showLeaveMessage(
BuildContext context, String uuid, String type, String tip) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new LeaveMsgProvider(
wid: uuid,
aid: uuid,
type: type,
tip: tip,
);
}));
}
// 频道消息
static void showChannel(BuildContext context, Thread thread) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new ChannelProvider(
thread: thread,
);
}));
}
// 获取个人资料
static Future<User> getProfile() async {
return BytedeskUserHttpApi().getProfile();
}
// 设置用户昵称
static Future<User> updateNickname(String nickname) async {
return BytedeskUserHttpApi().updateNickname(nickname);
}
// 设置用户头像
static Future<User> updateAvatar(String avatar) async {
return BytedeskUserHttpApi().updateAvatar(avatar);
}
// 设置用户备注
static Future<User> updateDescription(String description) async {
return BytedeskUserHttpApi().updateDescription(description);
}
// 一个接口,设置:昵称、头像、备注
static Future<User> updateProfile(
String nickname, String avatar, String description) async {
return BytedeskUserHttpApi().updateProfile(nickname, avatar, description);
}
// 查询技能组在线状态
static Future<String> getWorkGroupStatus(String workGroupWid) async {
return BytedeskUserHttpApi().getWorkGroupStatus(workGroupWid);
}
// 查询客服在线状态
static Future<String> getAgentStatus(String agentUid) async {
return BytedeskUserHttpApi().getAgentStatus(agentUid);
}
// 查询当前用户-某技能组wid或指定客服未读消息数目
static Future<String> getUnreadCount(String wid) async {
return BytedeskUserHttpApi().getUnreadCount(wid);
}
// 访客端-查询访客所有未读消息数目
static Future<String> getUnreadCountVisitor() async {
return BytedeskUserHttpApi().getUnreadCountVisitor();
}
// 客服端-查询客服所有未读消息数目
static Future<String> getUnreadCountAgent() async {
return BytedeskUserHttpApi().getUnreadCountAgent();
}
// 访客历史会话
static Future<List<Thread>> getVisitorThreads(int page, int size) async {
return BytedeskThreadHttpApi().getVisitorThreads(page, size);
}
// 消息提示设置
static bool? getPlayAudioOnSendMessage() {
return SpUtil.getBool(BytedeskConstants.PLAY_AUDIO_ON_SEND_MESSAGE,
defValue: true);
}
static void setPlayAudioOnSendMessage(bool flag) {
SpUtil.putBool(BytedeskConstants.PLAY_AUDIO_ON_SEND_MESSAGE, flag);
}
static bool? getPlayAudioOnReceiveMessage() {
return SpUtil.getBool(BytedeskConstants.PLAY_AUDIO_ON_RECEIVE_MESSAGE,
defValue: true);
}
static void setPlayAudioOnReceiveMessage(bool flag) {
SpUtil.putBool(BytedeskConstants.PLAY_AUDIO_ON_RECEIVE_MESSAGE, flag);
}
static bool? getVibrateOnReceiveMessage() {
return SpUtil.getBool(BytedeskConstants.VIBRATE_ON_RECEIVE_MESSAGE,
defValue: true);
}
static void setVibrateOnReceiveMessage(bool flag) {
SpUtil.putBool(BytedeskConstants.VIBRATE_ON_RECEIVE_MESSAGE, flag);
}
// 上传ios devicetoken
static Future<void> uploadIOSDeviceToken(
String appkey, String deviceToken) async {
BytedeskDeviceHttpApi()
.updateIOSDeviceToken(appkey, BytedeskConstants.build, deviceToken);
}
// build的值只有两种情况测试设置为debug 或 线上设置为release
// static Future<void> uploadIOSDeviceToken(
// String appkey, String build, String deviceToken) async {
// BytedeskDeviceHttpApi().updateIOSDeviceToken(appkey, build, deviceToken);
// }
// build的值只有两种情况测试设置为debug 或 线上设置为release
static Future<void> deleteIOSDeviceToken(String build) async {
BytedeskDeviceHttpApi().deleteIOSDeviceToken(build);
}
// The package version. `CFBundleShortVersionString` on iOS, `versionName` on Android.
static Future<String> getAppVersion() {
return BytedeskDeviceHttpApi().getAppVersion();
}
// The build number. `CFBundleVersion` on iOS, `versionCode` on Android.
static Future<String> getAppBuildNumber() {
return BytedeskDeviceHttpApi().getAppBuildNumber();
}
// 从服务器检测当前APP是否有新版
static Future<App> checkAppVersion(String androidKey, String iosKey) {
if (BytedeskUtils.isWeb) {
// FIXME: 仅用于占位,待修改
return BytedeskUserHttpApi().checkAppVersion(iosKey);
} else if (BytedeskUtils.isAndroid) {
return BytedeskUserHttpApi().checkAppVersion(androidKey);
}
return BytedeskUserHttpApi().checkAppVersion(iosKey);
}
// 退出登录
static Future<void> logout() {
// 断开长链接
disconnect();
// 通知服务器,并清空本地数据
return BytedeskUserHttpApi().logout();
}
// 以下为专用接口,普通用户请忽略
// 通过token获取手机号-良师app-专用
static Future<String> getAliyunOneKeyLoginMobile(String token) async {
return BytedeskUserHttpApi().getAliyunOneKeyLoginMobile(token);
}
// 匿名登录之后,绑定手机号-良师app-专用
static Future<JsonResult> bindMobile(String mobile) async {
return BytedeskUserHttpApi().bindMobile(mobile);
}
// 微信登录之后,获取微信用户信息-良师app-专用
static Future<WeChatResult> getWechatUserinfo(String code) async {
return BytedeskUserHttpApi().getWechatUserinfo(code);
}
// 手机端注册微信登录用户-并绑定手机号-良师app-专用
static Future<UserJsonResult> registerWechatMobile(String mobile,
String nickname, String avatar, String unionid, String openid) async {
return BytedeskUserHttpApi()
.registerWechatMobile(mobile, nickname, avatar, unionid, openid);
}
// 将unionid绑定到已经存在的手机账号-良师app-专用
static Future<UserJsonResult> bindWeChatMobile(
String mobile, String unionid) async {
return BytedeskUserHttpApi().bindWeChatMobile(mobile, unionid);
}
}