mirror of
https://github.com/chatopera/cosin.git
synced 2025-06-16 18:30:03 +08:00
https://github.com/chatopera/cskefu/issues/732 add Facebook Messenger Plugin
This commit is contained in:
parent
6facf37fb0
commit
25ff3a2052
4
contact-center/app/.gitignore
vendored
4
contact-center/app/.gitignore
vendored
@ -1,10 +1,6 @@
|
|||||||
# dev profile
|
# dev profile
|
||||||
src/main/resources/application-dev.properties
|
src/main/resources/application-dev.properties
|
||||||
|
|
||||||
# ignore channel views within plugins
|
|
||||||
src/main/resources/templates/admin/channel/*
|
|
||||||
!src/main/resources/templates/admin/channel/im
|
|
||||||
|
|
||||||
# ignore app views within plugins
|
# ignore app views within plugins
|
||||||
src/main/resources/templates/apps/callout
|
src/main/resources/templates/apps/callout
|
||||||
src/main/resources/templates/apps/callcenter
|
src/main/resources/templates/apps/callcenter
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
plugins/*
|
|
||||||
!plugins/chatbot
|
|
||||||
!plugins/README.md
|
|
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Chatopera Inc, <https://www.chatopera.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.chatopera.cc.basic.Constants;
|
||||||
|
import com.chatopera.cc.basic.MainContext;
|
||||||
|
import com.chatopera.cc.basic.MainUtils;
|
||||||
|
import com.chatopera.cc.controller.Handler;
|
||||||
|
import com.chatopera.cc.model.FbMessenger;
|
||||||
|
import com.chatopera.cc.model.Organ;
|
||||||
|
import com.chatopera.cc.model.SNSAccount;
|
||||||
|
import com.chatopera.cc.persistence.repository.FbMessengerRepository;
|
||||||
|
import com.chatopera.cc.persistence.repository.OrganRepository;
|
||||||
|
import com.chatopera.cc.persistence.repository.SNSAccountRepository;
|
||||||
|
import com.chatopera.cc.proxy.OrganProxy;
|
||||||
|
import com.chatopera.cc.util.Menu;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.ModelMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/admin/messenger")
|
||||||
|
public class MessengerChannelController extends Handler {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessengerChannelController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbMessengerRepository fbMessengerRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrganRepository organRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrganProxy organProxy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SNSAccountRepository snsAccountRepository;
|
||||||
|
|
||||||
|
private Map<String, Organ> getOwnOrgan(HttpServletRequest request) {
|
||||||
|
return organProxy.findAllOrganByParentAndOrgi(super.getOrgan(request), super.getOrgi(request));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/index")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView index(ModelMap map, HttpServletRequest request) {
|
||||||
|
Map<String, Organ> organs = getOwnOrgan(request);
|
||||||
|
List<FbMessenger> fbMessengers = fbMessengerRepository.findByOrganIn(organs.keySet());
|
||||||
|
Organ currentOrgan = super.getOrgan(request);
|
||||||
|
map.addAttribute("fbMessengers", fbMessengers);
|
||||||
|
map.addAttribute("organs", organs);
|
||||||
|
map.addAttribute("organ", currentOrgan);
|
||||||
|
return request(super.createView("/admin/channel/messenger/index"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/add")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView add(ModelMap map, HttpServletRequest request) {
|
||||||
|
Organ currentOrgan = super.getOrgan(request);
|
||||||
|
map.addAttribute("organ", currentOrgan);
|
||||||
|
return request(super.createView("/admin/channel/messenger/add"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/save")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView save(ModelMap map, HttpServletRequest request, @Valid FbMessenger fbMessenger) {
|
||||||
|
String msg = "save_ok";
|
||||||
|
Organ currentOrgan = super.getOrgan(request);
|
||||||
|
FbMessenger fbMessengerOne = fbMessengerRepository.findOneByPageId(fbMessenger.getPageId());
|
||||||
|
if (fbMessengerOne != null) {
|
||||||
|
msg = "save_no_PageId";
|
||||||
|
} else {
|
||||||
|
fbMessenger.setId(MainUtils.getUUID());
|
||||||
|
fbMessenger.setOrgan(currentOrgan.getId());
|
||||||
|
|
||||||
|
if (fbMessenger.getStatus() == null) {
|
||||||
|
fbMessenger.setStatus("disabled");
|
||||||
|
}
|
||||||
|
fbMessenger.setCreatetime(new Date());
|
||||||
|
fbMessenger.setUpdatetime(new Date());
|
||||||
|
fbMessenger.setAiid(null);
|
||||||
|
fbMessengerRepository.save(fbMessenger);
|
||||||
|
|
||||||
|
SNSAccount snsAccount = new SNSAccount();
|
||||||
|
snsAccount.setId(MainUtils.genID());
|
||||||
|
snsAccount.setCreatetime(new Date());
|
||||||
|
snsAccount.setOrgi(super.getOrgi(request));
|
||||||
|
snsAccount.setName(fbMessenger.getName());
|
||||||
|
snsAccount.setOrgan(currentOrgan.getId());
|
||||||
|
snsAccount.setSnsid(fbMessenger.getPageId());
|
||||||
|
snsAccount.setSnstype(MainContext.ChannelType.MESSENGER.toString());
|
||||||
|
snsAccountRepository.save(snsAccount);
|
||||||
|
}
|
||||||
|
return request(super.createView("redirect:/admin/messenger/index.html?msg=" + msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/edit")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView edit(ModelMap map, HttpServletRequest request, @Valid String id) {
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOne(id);
|
||||||
|
|
||||||
|
Organ fbOrgan = organRepository.getOne(fbMessenger.getOrgan());
|
||||||
|
map.addAttribute("organ", fbOrgan);
|
||||||
|
map.addAttribute("fb", fbMessenger);
|
||||||
|
|
||||||
|
return request(super.createView("/admin/channel/messenger/edit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/update")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView update(ModelMap map, HttpServletRequest request, @Valid FbMessenger fbMessenger) {
|
||||||
|
String msg = "update_ok";
|
||||||
|
FbMessenger oldMessenger = fbMessengerRepository.findOne(fbMessenger.getId());
|
||||||
|
oldMessenger.setName(fbMessenger.getName());
|
||||||
|
if (fbMessenger.getStatus() != null) {
|
||||||
|
oldMessenger.setStatus(fbMessenger.getStatus());
|
||||||
|
} else {
|
||||||
|
oldMessenger.setStatus(Constants.MESSENGER_CHANNEL_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldMessenger.setToken(fbMessenger.getToken());
|
||||||
|
oldMessenger.setVerifyToken(fbMessenger.getVerifyToken());
|
||||||
|
oldMessenger.setUpdatetime(new Date());
|
||||||
|
|
||||||
|
fbMessengerRepository.save(oldMessenger);
|
||||||
|
|
||||||
|
return request(super.createView("redirect:/admin/messenger/index.html?msg=" + msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/delete")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView delete(ModelMap map, HttpServletRequest request, @Valid String id) {
|
||||||
|
String msg = "delete_ok";
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.getOne(id);
|
||||||
|
fbMessengerRepository.delete(id);
|
||||||
|
|
||||||
|
snsAccountRepository.findBySnsid(fbMessenger.getPageId()).ifPresent(snsAccount -> {
|
||||||
|
snsAccountRepository.delete(snsAccount);
|
||||||
|
});
|
||||||
|
|
||||||
|
return request(super.createView("redirect:/admin/messenger/index.html?msg=" + msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/setting")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView setting(ModelMap map, HttpServletRequest request, @Valid String id) {
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOne(id);
|
||||||
|
Organ fbOrgan = organRepository.getOne(fbMessenger.getOrgan());
|
||||||
|
|
||||||
|
map.mergeAttributes(fbMessenger.parseConfigMap());
|
||||||
|
map.addAttribute("organ", fbOrgan);
|
||||||
|
map.addAttribute("fb", fbMessenger);
|
||||||
|
|
||||||
|
return request(super.createView("/admin/channel/messenger/setting"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/setting/save", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView saveSetting(ModelMap map, HttpServletRequest request, @Valid String id, @RequestBody MultiValueMap<String, String> formData) {
|
||||||
|
String msg = "update_ok";
|
||||||
|
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOne(id);
|
||||||
|
if (fbMessenger != null) {
|
||||||
|
fbMessenger.setConfigMap(formData.toSingleValueMap());
|
||||||
|
fbMessengerRepository.save(fbMessenger);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request(super.createView("redirect:/admin/messenger/index.html?msg=" + msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/setStatus")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
@ResponseBody
|
||||||
|
public String setStatus(ModelMap map, HttpServletRequest request, @Valid String id, @Valid String status) {
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOne(id);
|
||||||
|
fbMessenger.setStatus(status);
|
||||||
|
fbMessengerRepository.save(fbMessenger);
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
|||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.chatopera.cc.basic.MainContext;
|
||||||
|
import com.chatopera.cc.model.AgentUser;
|
||||||
|
import com.chatopera.cc.model.OnlineUser;
|
||||||
|
import com.chatopera.cc.peer.PeerContext;
|
||||||
|
import com.chatopera.cc.persistence.repository.OnlineUserRepository;
|
||||||
|
import com.chatopera.cc.socketio.message.ChatMessage;
|
||||||
|
import com.chatopera.compose4j.Functional;
|
||||||
|
import com.chatopera.compose4j.Middleware;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Messenger 消息处理
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class MessengerChannelMessager implements Middleware<PeerContext> {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessengerChannelMessager.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OnlineUserRepository onlineUserRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessengerMessageProxy messengerMessageProxy;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(final PeerContext ctx, final Functional next) {
|
||||||
|
if ((!ctx.isSent()) && ctx.getChannel() == MainContext.ChannelType.MESSENGER) {
|
||||||
|
AgentUser agentUser = ctx.getMessage().getAgentUser();
|
||||||
|
|
||||||
|
final OnlineUser onlineUser = onlineUserRes.findOneByUseridAndOrgi(
|
||||||
|
agentUser.getUserid(), agentUser.getOrgi());
|
||||||
|
|
||||||
|
if (onlineUser != null) {
|
||||||
|
handle(ctx, onlineUser);
|
||||||
|
} else {
|
||||||
|
logger.info(
|
||||||
|
"[apply] can not online user or its contactsid with agentUserId {}, userid {} and orgi {}",
|
||||||
|
agentUser.getId(), agentUser.getUserid(), agentUser.getOrgi());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理消息体
|
||||||
|
*
|
||||||
|
* @param ctx
|
||||||
|
* @param onlineUser
|
||||||
|
*/
|
||||||
|
private void handle(final PeerContext ctx, final OnlineUser onlineUser) {
|
||||||
|
if (ctx.getMessage().getChannelMessage() instanceof ChatMessage) {
|
||||||
|
final ChatMessage chatMessage = (ChatMessage) ctx.getMessage().getChannelMessage();
|
||||||
|
logger.info(
|
||||||
|
"[apply] chat message type {}, content {}", chatMessage.getMsgtype(),
|
||||||
|
chatMessage.getMessage());
|
||||||
|
if (StringUtils.equals(chatMessage.getMsgtype(), MainContext.MediaType.TEXT.toString())) {
|
||||||
|
Document document = Jsoup.parse(chatMessage.getMessage());
|
||||||
|
Elements pngs = document.select("img[src]");
|
||||||
|
if (pngs.size() > 0) {
|
||||||
|
for (Element element : pngs) {
|
||||||
|
String imgUrl = element.attr("src");
|
||||||
|
if (StringUtils.isNotBlank(imgUrl)) {
|
||||||
|
messengerMessageProxy.sendImage(onlineUser.getAppid(), onlineUser.getUserid(), imgUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
messengerMessageProxy.send(onlineUser.getAppid(), onlineUser.getUserid(), document.text());
|
||||||
|
}
|
||||||
|
} else if (StringUtils.equals(chatMessage.getMsgtype(), MainContext.MediaType.IMAGE.toString())) {
|
||||||
|
messengerMessageProxy.sendImage(onlineUser.getAppid(), onlineUser.getUserid(), chatMessage.getMessage());
|
||||||
|
}
|
||||||
|
logger.info("[apply] message is sent.");
|
||||||
|
ctx.setSent(true);
|
||||||
|
} else {
|
||||||
|
if (StringUtils.isNotBlank(ctx.getMessage().getMessage())) {
|
||||||
|
messengerMessageProxy.send(onlineUser.getAppid(), onlineUser.getUserid(), ctx.getMessage().getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.chatopera.bot.exception.ChatbotException;
|
||||||
|
import com.chatopera.cc.acd.ACDServiceRouter;
|
||||||
|
import com.chatopera.cc.basic.Constants;
|
||||||
|
import com.chatopera.cc.basic.MainContext;
|
||||||
|
import com.chatopera.cc.basic.MainUtils;
|
||||||
|
import com.chatopera.cc.exception.CSKefuException;
|
||||||
|
import com.chatopera.cc.model.*;
|
||||||
|
import com.chatopera.cc.persistence.repository.*;
|
||||||
|
import com.chatopera.cc.plugins.chatbot.ChatbotProxy;
|
||||||
|
import com.chatopera.cc.proxy.OnlineUserProxy;
|
||||||
|
import com.chatopera.cc.socketio.message.ChatMessage;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessengerChatbot {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessengerChatbot.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChatbotProxy chatbotProxy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AgentUserRepository agentUserRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChatMessageRepository chatMessageRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SNSAccountRepository snsAccountRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AgentServiceRepository agentServiceRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessengerMessageProxy messengerMessageProxy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OnlineUserRepository onlineUserRes;
|
||||||
|
|
||||||
|
public boolean receiveMessage(String chatbotId, String fromId, String snsID, OnlineUser onlineUser, MainContext.MediaType msgType, String msg) throws ChatbotException, MalformedURLException {
|
||||||
|
logger.info("[receiveMessage] message: chatbotId {},fromId {},toId {},msg {} ", chatbotId, fromId, snsID, msg);
|
||||||
|
// 在线客服访客咨询记录
|
||||||
|
Date now = new Date();
|
||||||
|
Optional<SNSAccount> optionalSNSAccount = snsAccountRes.findBySnsid(snsID);
|
||||||
|
SNSAccount snsAccount = optionalSNSAccount.get();
|
||||||
|
CousultInvite invite = OnlineUserProxy.consult(onlineUser.getAppid(), Constants.SYSTEM_ORGI);
|
||||||
|
AgentUser agentUser = agentUserRes.findOneByUseridAndStatusNotAndChannelAndOrgi(fromId, MainContext.AgentUserStatusEnum.END.toString(), MainContext.ChannelType.MESSENGER.toString(), Constants.SYSTEM_ORGI).orElseGet(() -> {
|
||||||
|
AgentUser au = new AgentUser(
|
||||||
|
onlineUser.getUserid(),
|
||||||
|
MainContext.ChannelType.MESSENGER.toString(),
|
||||||
|
onlineUser.getId(),
|
||||||
|
onlineUser.getUsername(),
|
||||||
|
Constants.SYSTEM_ORGI,
|
||||||
|
onlineUser.getAppid());
|
||||||
|
|
||||||
|
au.setServicetime(now);
|
||||||
|
au.setCreatetime(now);
|
||||||
|
au.setUpdatetime(now);
|
||||||
|
au.setLogindate(now);
|
||||||
|
au.setSessionid(onlineUser.getSessionid());
|
||||||
|
au.setRegion(onlineUser.getRegion());
|
||||||
|
au.setUsername(onlineUser.getUsername());
|
||||||
|
au.setSkill(snsAccount.getOrgan());
|
||||||
|
au.setAppid(snsAccount.getSnsid());
|
||||||
|
au.setOrgi(Constants.SYSTEM_ORGI);
|
||||||
|
au.setNickname(onlineUser.getUsername());
|
||||||
|
au.setStatus(MainContext.AgentUserStatusEnum.INSERVICE.toString());
|
||||||
|
|
||||||
|
// 聊天机器人处理的请求
|
||||||
|
au.setOpttype(MainContext.OptType.CHATBOT.toString());
|
||||||
|
au.setAgentno(chatbotId); // 聊天机器人ID
|
||||||
|
au.setAgentname(invite != null ? invite.getAiname() : "机器人客服");
|
||||||
|
au.setCity(onlineUser.getCity());
|
||||||
|
au.setProvince(onlineUser.getProvince());
|
||||||
|
au.setCountry(onlineUser.getCountry());
|
||||||
|
AgentService agentService = ACDServiceRouter.getAcdChatbotService().processChatbotService(
|
||||||
|
invite != null ? invite.getAiname() : "机器人客服", au, Constants.SYSTEM_ORGI);
|
||||||
|
au.setAgentserviceid(agentService.getId());
|
||||||
|
// 标记为机器人坐席
|
||||||
|
au.setChatbotops(true);
|
||||||
|
// 保存到MySQL
|
||||||
|
agentUserRes.save(au);
|
||||||
|
|
||||||
|
return au;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (agentUser.isChatbotops()) {
|
||||||
|
ChatMessage data = new ChatMessage();
|
||||||
|
data.setMessage(msg);
|
||||||
|
data.setUserid(fromId);
|
||||||
|
data.setUsession(fromId); // 绑定唯一用户
|
||||||
|
data.setSessionid(agentUser.getSessionid());
|
||||||
|
data.setMessage(MainUtils.processEmoti(data.getMessage())); // 处理表情
|
||||||
|
data.setTouser(chatbotId);
|
||||||
|
data.setUsername(agentUser.getUsername());
|
||||||
|
data.setAiid(chatbotId);
|
||||||
|
data.setAgentserviceid(agentUser.getAgentserviceid());
|
||||||
|
data.setChannel(agentUser.getChannel());
|
||||||
|
data.setOrgi(Constants.SYSTEM_ORGI);
|
||||||
|
data.setContextid(agentUser.getAgentserviceid()); // 一定要设置 ContextID
|
||||||
|
data.setCalltype(MainContext.CallType.IN.toString());
|
||||||
|
data.setMsgtype(msgType.toString());
|
||||||
|
|
||||||
|
chatMessageRes.save(data);
|
||||||
|
|
||||||
|
if (MainContext.MediaType.TEXT == msgType) {
|
||||||
|
// 发送消息给Bot
|
||||||
|
chatbotProxy.publishMessage(data, Constants.CHATBOT_EVENT_TYPE_CHAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchManualCustomerService(String fromId) {
|
||||||
|
agentUserRes.findOneByUseridAndStatusNotAndChannelAndOrgi(fromId, MainContext.AgentUserStatusEnum.END.toString(), MainContext.ChannelType.MESSENGER.toString(), Constants.SYSTEM_ORGI).ifPresent(agentUser -> {
|
||||||
|
if (agentUser.isChatbotops()) {
|
||||||
|
Date now = new Date();
|
||||||
|
AgentService agentService = agentServiceRes.findOne(agentUser.getAgentserviceid());
|
||||||
|
agentService.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||||
|
agentService.setEndtime(now);
|
||||||
|
agentServiceRes.save(agentService);
|
||||||
|
agentUser.setAgentserviceid(null);
|
||||||
|
agentUser.setChatbotops(false);
|
||||||
|
agentUser.setAgentno(null);
|
||||||
|
agentUserRes.save(agentUser);
|
||||||
|
|
||||||
|
snsAccountRes.findBySnsid(agentUser.getAppid()).ifPresent(snsAccount -> {
|
||||||
|
OnlineUser onlineUser = onlineUserRes.findOneByUseridAndOrgi(fromId, Constants.SYSTEM_ORGI);
|
||||||
|
try {
|
||||||
|
messengerMessageProxy.scheduleMessengerAgentUser(agentUser, onlineUser, snsAccount, Constants.SYSTEM_ORGI);
|
||||||
|
} catch (CSKefuException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.chatopera.cc.basic.Constants;
|
||||||
|
import com.chatopera.cc.basic.MainContext;
|
||||||
|
import com.chatopera.cc.model.FbMessenger;
|
||||||
|
import com.chatopera.cc.model.OnlineUser;
|
||||||
|
import com.chatopera.cc.persistence.repository.FbMessengerRepository;
|
||||||
|
import com.chatopera.cc.persistence.repository.OnlineUserRepository;
|
||||||
|
import com.chatopera.cc.plugins.chatbot.ChatbotConstants;
|
||||||
|
import com.chatopera.cc.plugins.chatbot.ChatbotContext;
|
||||||
|
import com.chatopera.cc.socketio.message.ChatMessage;
|
||||||
|
import com.chatopera.compose4j.Functional;
|
||||||
|
import com.chatopera.compose4j.Middleware;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessengerChatbotMessager implements Middleware<ChatbotContext> {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessengerChatbotMessager.class);
|
||||||
|
|
||||||
|
private final static String EVALUATION_YES_REPLY = "evaluationYesReply";
|
||||||
|
private final static String EVALUATION_NO_REPLY = "evaluationNoReply";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessengerMessageProxy messengerMessageProxy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OnlineUserRepository onlineUserRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbMessengerRepository fbMessengerRepository;
|
||||||
|
|
||||||
|
private final Map<String, String> messengerConfig = new HashMap<String, String>() {
|
||||||
|
{
|
||||||
|
put("transferManualService", "转人工");
|
||||||
|
put("suggestQuestion", "您是否想问以下问题");
|
||||||
|
put("evaluationAsk", "以上答案是否对您有帮助");
|
||||||
|
put("evaluationYes", "是");
|
||||||
|
put("evaluationNo", "否");
|
||||||
|
put(EVALUATION_YES_REPLY, "感谢您的反馈,我们会做的更好!");
|
||||||
|
put(EVALUATION_NO_REPLY, "感谢您的反馈,机器人在不断的学习!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(final ChatbotContext ctx, final Functional next) {
|
||||||
|
ChatMessage resp = ctx.getResp();
|
||||||
|
if (MainContext.ChannelType.MESSENGER.toString().equals(resp.getChannel())) {
|
||||||
|
|
||||||
|
final OnlineUser onlineUser = onlineUserRes.findOneByUseridAndOrgi(
|
||||||
|
resp.getUserid(), Constants.SYSTEM_ORGI);
|
||||||
|
|
||||||
|
Map<String, String> configMap = messengerConfig;
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOneByPageId(onlineUser.getAppid());
|
||||||
|
if (fbMessenger != null && StringUtils.isNotBlank(fbMessenger.getConfig())) {
|
||||||
|
configMap = (Map<String, String>) JSONObject.parse(fbMessenger.getConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(resp.getExpmsg())) {
|
||||||
|
String jsonStr = processGenericTemplate(resp.getExpmsg(), configMap);
|
||||||
|
messengerMessageProxy.send(onlineUser.getAppid(), onlineUser.getUserid(), JSONObject.parseObject(jsonStr), fbMessenger);
|
||||||
|
} else {
|
||||||
|
messengerMessageProxy.send(onlineUser.getAppid(), onlineUser.getUserid(), processTextTemplate(resp.getMessage(), configMap));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换文本消息
|
||||||
|
*
|
||||||
|
* @param template
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String processTextTemplate(String template, Map<String, String> params) {
|
||||||
|
if (StringUtils.equals(template, ChatbotConstants.PROVIDER_FEEDBACK_EVAL_POSITIVE_REPLY_PLACEHOLDER)) {
|
||||||
|
if (params.containsKey(EVALUATION_YES_REPLY) && StringUtils.isNotBlank(params.get(EVALUATION_YES_REPLY))) {
|
||||||
|
return params.get(EVALUATION_YES_REPLY);
|
||||||
|
}
|
||||||
|
} else if (StringUtils.equals(template, ChatbotConstants.PROVIDER_FEEDBACK_EVAL_NEGATIVE_REPLY_PLACEHOLDER)) {
|
||||||
|
if (params.containsKey(EVALUATION_NO_REPLY) && StringUtils.isNotBlank(params.get(EVALUATION_NO_REPLY))) {
|
||||||
|
return params.get(EVALUATION_NO_REPLY);
|
||||||
|
}
|
||||||
|
} else if (StringUtils.equals(template, "${leaveMeAlone}")) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换模版消息
|
||||||
|
*
|
||||||
|
* @param template
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String processGenericTemplate(String template, Map<String, String> params) {
|
||||||
|
Matcher m = Pattern.compile("\\$\\{\\w+\\}").matcher(template);
|
||||||
|
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
while (m.find()) {
|
||||||
|
String param = m.group();
|
||||||
|
Object value = params.get(param.substring(2, param.length() - 1));
|
||||||
|
m.appendReplacement(sb, value == null ? "" : value.toString());
|
||||||
|
}
|
||||||
|
m.appendTail(sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.chatopera.cc.basic.Constants;
|
||||||
|
import com.chatopera.cc.model.FbMessenger;
|
||||||
|
import com.chatopera.cc.model.FbOTN;
|
||||||
|
import com.chatopera.cc.model.FbOtnFollow;
|
||||||
|
import com.chatopera.cc.persistence.repository.FbMessengerRepository;
|
||||||
|
import com.chatopera.cc.persistence.repository.FbOTNFollowRepository;
|
||||||
|
import com.chatopera.cc.persistence.repository.FbOTNRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.jms.annotation.JmsListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessengerEventSubscription {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessengerEventSubscription.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbMessengerRepository fbMessengerRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbOTNRepository otnRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbOTNFollowRepository otnFollowRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessengerMessageProxy messengerMessageProxy;
|
||||||
|
|
||||||
|
@JmsListener(destination = Constants.INSTANT_MESSAGING_MQ_QUEUE_FACEBOOK_OTN, containerFactory = "jmsListenerContainerQueue")
|
||||||
|
public void onPublish(final String jsonStr) {
|
||||||
|
JSONObject payload = JSONObject.parseObject(jsonStr);
|
||||||
|
String otnId = payload.getString("otnId");
|
||||||
|
Date sendtime = payload.getTimestamp("sendtime");
|
||||||
|
|
||||||
|
FbOTN otn = otnRepository.getOne(otnId);
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOneByPageId(otn.getPageId());
|
||||||
|
if (fbMessenger != null && otn != null) {
|
||||||
|
if (otn.getStatus().equals("create") && otn.getSendtime() != null && otn.getSendtime().equals(sendtime)) {
|
||||||
|
otn.setStatus("sending");
|
||||||
|
otnRepository.save(otn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otn.getStatus().equals("sending")) {
|
||||||
|
List<FbOtnFollow> follows = otnFollowRepository.findByOtnId(otn.getId());
|
||||||
|
for (FbOtnFollow f : follows) {
|
||||||
|
if (f.getSendtime() == null) {
|
||||||
|
messengerMessageProxy.sendOtnText(fbMessenger.getToken(), f.getOtnToken(), otn.getOtnMessage());
|
||||||
|
f.setSendtime(new Date());
|
||||||
|
otnFollowRepository.save(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
otn.setStatus("finish");
|
||||||
|
otnRepository.save(otn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,540 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Chatopera Inc, <https://www.chatopera.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.chatopera.bot.exception.ChatbotException;
|
||||||
|
import com.chatopera.cc.acd.ACDAgentService;
|
||||||
|
import com.chatopera.cc.acd.ACDVisitorDispatcher;
|
||||||
|
import com.chatopera.cc.acd.basic.ACDComposeContext;
|
||||||
|
import com.chatopera.cc.acd.basic.ACDMessageHelper;
|
||||||
|
import com.chatopera.cc.basic.Constants;
|
||||||
|
import com.chatopera.cc.basic.MainContext;
|
||||||
|
import com.chatopera.cc.basic.MainUtils;
|
||||||
|
import com.chatopera.cc.cache.Cache;
|
||||||
|
import com.chatopera.cc.exception.CSKefuException;
|
||||||
|
import com.chatopera.cc.model.*;
|
||||||
|
import com.chatopera.cc.peer.PeerSyncIM;
|
||||||
|
import com.chatopera.cc.persistence.blob.JpaBlobHelper;
|
||||||
|
import com.chatopera.cc.persistence.repository.*;
|
||||||
|
import com.chatopera.cc.socketio.message.ChatMessage;
|
||||||
|
import com.chatopera.cc.socketio.message.Message;
|
||||||
|
import com.chatopera.cc.util.HttpClientUtil;
|
||||||
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessengerMessageProxy {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessengerMessageProxy.class);
|
||||||
|
|
||||||
|
private final String FACEBOOK_MESSAGES_API = "https://graph.facebook.com/v2.6/me/messages";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OnlineUserRepository onlineUserRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AgentUserRepository agentUserRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SNSAccountRepository snsAccountRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ACDMessageHelper acdMessageHelper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ACDVisitorDispatcher acdVisitorDispatcher;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AgentServiceRepository agentServiceRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Cache cache;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PeerSyncIM peerSyncIM;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbMessengerRepository fbMessengerRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChatbotRepository chatbotRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessengerChatbot messengerChatbot;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbOTNRepository otnRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbOTNFollowRepository otnFollowRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ACDAgentService acdAgentService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChatMessageRepository chatMessageRes;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JpaBlobHelper jpaBlobHelper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StreamingFileRepository streamingFileRes;
|
||||||
|
|
||||||
|
@Value("${uk.im.server.host}")
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
public void acceptOTNReq(String fromId, String toId, String otnToken, String ref) {
|
||||||
|
FbOTN otn = otnRepository.findOne(ref);
|
||||||
|
if (otn != null) {
|
||||||
|
FbOtnFollow follow = new FbOtnFollow();
|
||||||
|
follow.setId(MainUtils.getUUID());
|
||||||
|
follow.setOtnId(ref);
|
||||||
|
follow.setUserId(fromId);
|
||||||
|
follow.setPageId(toId);
|
||||||
|
follow.setOtnToken(otnToken);
|
||||||
|
follow.setCreatetime(new Date());
|
||||||
|
follow.setUpdatetime(new Date());
|
||||||
|
|
||||||
|
otnFollowRepository.save(follow);
|
||||||
|
|
||||||
|
otnRepository.incOneSubNumById(otn.getId());
|
||||||
|
|
||||||
|
sendOTNContent(toId, fromId, JSONObject.parseObject(otn.getSuccessMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void acceptMeLink(String fromId, String toId, String ref) {
|
||||||
|
FbOTN otn = otnRepository.findOne(ref);
|
||||||
|
if (otn != null) {
|
||||||
|
if (StringUtils.isNotBlank(otn.getPreSubMessage())) {
|
||||||
|
Object obj = JSON.parse(otn.getPreSubMessage());
|
||||||
|
if (obj instanceof JSONObject) {
|
||||||
|
JSONObject json = (JSONObject) obj;
|
||||||
|
sendOTNContent(toId, fromId, json);
|
||||||
|
} else if (obj instanceof JSONArray) {
|
||||||
|
JSONArray jsonArray = (JSONArray) obj;
|
||||||
|
sendOTNContent(toId, fromId, jsonArray.getJSONObject(0));
|
||||||
|
sendOTNContent(toId, fromId, jsonArray.getJSONObject(1));
|
||||||
|
}
|
||||||
|
otnRepository.incOneMelinkNumById(otn.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(otn.getSubMessage())) {
|
||||||
|
sendOTNReq(toId, fromId, otn.getSubMessage(), ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void accept(String fromId, String toId, MainContext.MediaType msgType, String msg) throws CSKefuException, ChatbotException, MalformedURLException {
|
||||||
|
Optional<SNSAccount> optionalSNSAccount = snsAccountRes.findBySnsid(toId);
|
||||||
|
|
||||||
|
if (!optionalSNSAccount.isPresent()) {
|
||||||
|
logger.warn("[handle] SnsAccount is null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SNSAccount snsAccount = optionalSNSAccount.get();
|
||||||
|
|
||||||
|
Date now = new Date();
|
||||||
|
|
||||||
|
OnlineUser onlineUser = onlineUserRes.findOneByUseridAndOrgi(fromId, Constants.SYSTEM_ORGI);
|
||||||
|
if (onlineUser == null) {
|
||||||
|
onlineUser = new OnlineUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(onlineUser.getUserid())) {
|
||||||
|
Map<String, String> profile = getPersonName(toId, fromId);
|
||||||
|
|
||||||
|
// 创建新的Onlineuser
|
||||||
|
onlineUser.setId(MainUtils.getUUID());
|
||||||
|
onlineUser.setUpdatetime(now);
|
||||||
|
onlineUser.setUsername(profile.get("name"));
|
||||||
|
onlineUser.setHeadimgurl(profile.get("profile_pic"));
|
||||||
|
onlineUser.setCreatetime(now);
|
||||||
|
onlineUser.setLogintime(now);
|
||||||
|
onlineUser.setChannel(MainContext.ChannelType.MESSENGER.toString());
|
||||||
|
onlineUser.setAppid(snsAccount.getSnsid());
|
||||||
|
onlineUser.setStatus(MainContext.OnlineUserStatusEnum.ONLINE.toString());
|
||||||
|
onlineUser.setOrgi(Constants.SYSTEM_ORGI);
|
||||||
|
onlineUser.setUserid(fromId);
|
||||||
|
onlineUser.setUsertype(MainContext.OnlineUserType.MESSENGER.toString());
|
||||||
|
onlineUserRes.save(onlineUser);
|
||||||
|
} else if (cache.existBlackEntityByUserIdAndOrgi(onlineUser.getUserid(), Constants.SYSTEM_ORGI)) {
|
||||||
|
// 检查该访客是否被拉黑
|
||||||
|
logger.info("[handle] online user {} is in black list.", onlineUser.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chatbot c = chatbotRes.findBySnsAccountIdentifierAndOrgi(toId, Constants.SYSTEM_ORGI);
|
||||||
|
if (c != null && c.isEnabled()) {
|
||||||
|
|
||||||
|
if (!StringUtils.equals(Constants.CHATBOT_HUMAN_FIRST, c.getWorkmode())) {
|
||||||
|
Boolean sendService = messengerChatbot.receiveMessage(c.getId(), fromId, toId, onlineUser, msgType, msg);
|
||||||
|
if (sendService) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (c != null && !c.isEnabled() && StringUtils.equals(Constants.CHATBOT_CHATBOT_ONLY, c.getWorkmode())) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
agentUserRes.findOneByUseridAndStatusNotAndChannelAndOrgi(fromId, MainContext.AgentUserStatusEnum.END.toString(), MainContext.ChannelType.MESSENGER.toString(), Constants.SYSTEM_ORGI).ifPresent(p -> {
|
||||||
|
if (p.isChatbotops()) {
|
||||||
|
messengerChatbot.switchManualCustomerService(fromId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到OnlineUser后获取AgentUser
|
||||||
|
* 因为AgentUser是和OnlineUser关联的
|
||||||
|
*/
|
||||||
|
// 一个OnlineUser可以对应多个agentUser, 此处获得
|
||||||
|
AgentUser agentUser = agentUserRes.findOneByUseridAndStatusNotAndChannelAndOrgi(fromId, MainContext.AgentUserStatusEnum.END.toString(), MainContext.ChannelType.MESSENGER.toString(), Constants.SYSTEM_ORGI)
|
||||||
|
.orElseGet(() -> new AgentUser());
|
||||||
|
|
||||||
|
AgentService agentService;
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(agentUser.getAgentserviceid())) {
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOneByPageId(toId);
|
||||||
|
if (fbMessenger != null && StringUtils.equals(fbMessenger.getStatus(), Constants.MESSENGER_CHANNEL_DISABLED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有加载到进行中的AgentUser,创建一个新的
|
||||||
|
agentService = scheduleMessengerAgentUser(
|
||||||
|
agentUser, onlineUser, snsAccount, Constants.SYSTEM_ORGI).orElseThrow(
|
||||||
|
() -> new CSKefuException("Can not resolve AgentService Object."));
|
||||||
|
} else {
|
||||||
|
agentService = agentServiceRes.findOne(agentUser.getAgentserviceid());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给客服发送消息
|
||||||
|
*/
|
||||||
|
if (agentUser != null && agentService != null) {
|
||||||
|
ChatMessage chatMessage = new ChatMessage();
|
||||||
|
Message outMessage = new Message();
|
||||||
|
|
||||||
|
chatMessage.setMessage(msg);
|
||||||
|
chatMessage.setOrgi(Constants.SYSTEM_ORGI);
|
||||||
|
chatMessage.setUsername(agentUser.getName());
|
||||||
|
chatMessage.setCalltype(MainContext.CallType.IN.toString());
|
||||||
|
if (StringUtils.isNotBlank(agentUser.getAgentno())) {
|
||||||
|
chatMessage.setTouser(agentUser.getUserid());
|
||||||
|
}
|
||||||
|
chatMessage.setChannel(MainContext.ChannelType.MESSENGER.toString());
|
||||||
|
chatMessage.setUsession(agentUser.getUserid());
|
||||||
|
chatMessage.setId(MainUtils.getUUID());
|
||||||
|
chatMessage.setContextid(agentUser.getContextid());
|
||||||
|
chatMessage.setUserid(agentUser.getUserid());
|
||||||
|
chatMessage.setUsession(agentUser.getUserid());
|
||||||
|
chatMessage.setAgentserviceid(agentUser.getAgentserviceid());
|
||||||
|
chatMessage.setUsername(agentUser.getUsername());
|
||||||
|
|
||||||
|
chatMessage.setMsgtype(msgType.toString());
|
||||||
|
|
||||||
|
outMessage.setMessageType(chatMessage.getMsgtype());
|
||||||
|
outMessage.setMessage(msg);
|
||||||
|
outMessage.setAttachmentid(chatMessage.getAttachmentid());
|
||||||
|
outMessage.setCalltype(MainContext.CallType.IN.toString());
|
||||||
|
outMessage.setContextid(agentUser.getContextid());
|
||||||
|
outMessage.setAgentUser(agentUser);
|
||||||
|
|
||||||
|
outMessage.setChannelMessage(chatMessage);
|
||||||
|
outMessage.setCreatetime(Constants.DISPLAY_DATE_FORMATTER.format(
|
||||||
|
chatMessage.getCreatetime()));
|
||||||
|
|
||||||
|
outMessage.setMessage(chatMessage.getMessage());
|
||||||
|
|
||||||
|
outMessage.setChannelMessage(chatMessage);
|
||||||
|
outMessage.setAgentUser(agentUser);
|
||||||
|
outMessage.setAgentService(agentService);
|
||||||
|
outMessage.setCalltype(
|
||||||
|
MainContext.CallType.IN.toString());
|
||||||
|
outMessage.setCreatetime(
|
||||||
|
Constants.DISPLAY_DATE_FORMATTER.format(
|
||||||
|
now));
|
||||||
|
|
||||||
|
|
||||||
|
// Notify customer service to refresh the page
|
||||||
|
if (StringUtils.isNotBlank(agentService.getAgentno())) {
|
||||||
|
peerSyncIM.send(MainContext.ReceiverType.AGENT,
|
||||||
|
MainContext.ChannelType.MESSENGER,
|
||||||
|
agentUser.getAppid(),
|
||||||
|
MainContext.MessageType.MESSAGE,
|
||||||
|
agentService.getAgentno(), outMessage, true);
|
||||||
|
} else {
|
||||||
|
chatMessageRes.save((chatMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger.info("[handle] agent user not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新的AgentUser
|
||||||
|
*
|
||||||
|
* @param onlineUser 访客
|
||||||
|
* @param snsAccount 社交信息账号
|
||||||
|
* @param orgi 租户ID
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Optional<AgentService> scheduleMessengerAgentUser(
|
||||||
|
final AgentUser agentUser,
|
||||||
|
final OnlineUser onlineUser,
|
||||||
|
final SNSAccount snsAccount,
|
||||||
|
final String orgi) throws CSKefuException {
|
||||||
|
if (agentUser == null) {
|
||||||
|
throw new CSKefuException("Invalid param for agentUser, should not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String channel = MainContext.ChannelType.MESSENGER.toString();
|
||||||
|
Date now = new Date();
|
||||||
|
agentUser.setUsername(onlineUser.getUsername());
|
||||||
|
agentUser.setSkill(snsAccount.getOrgan());
|
||||||
|
agentUser.setOrgi(orgi);
|
||||||
|
agentUser.setNickname(onlineUser.getUsername());
|
||||||
|
agentUser.setUserid(onlineUser.getUserid());
|
||||||
|
agentUser.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||||
|
agentUser.setLogindate(now);
|
||||||
|
agentUser.setServicetime(now);
|
||||||
|
agentUser.setCreatetime(now);
|
||||||
|
agentUser.setUpdatetime(now);
|
||||||
|
agentUser.setSessiontimes(System.currentTimeMillis() - now.getTime());
|
||||||
|
agentUser.setChannel(channel);
|
||||||
|
agentUser.setAppid(snsAccount.getSnsid());
|
||||||
|
agentUserRes.save(agentUser);
|
||||||
|
|
||||||
|
// 为访客安排坐席
|
||||||
|
ACDComposeContext ctx = acdMessageHelper.getComposeContextWithAgentUser(
|
||||||
|
agentUser, false, MainContext.ChatInitiatorType.USER.toString());
|
||||||
|
ctx.setOnlineUserHeadimgUrl(onlineUser.getHeadimgurl());
|
||||||
|
|
||||||
|
acdVisitorDispatcher.enqueue(ctx);
|
||||||
|
acdAgentService.notifyAgentUserProcessResult(ctx);
|
||||||
|
|
||||||
|
|
||||||
|
return Optional.ofNullable(ctx.getAgentService());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getPersonName(String pageId, String psid) {
|
||||||
|
Map<String, String> result = new HashMap<>();
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOneByPageId(pageId);
|
||||||
|
if (fbMessenger != null) {
|
||||||
|
Map<String, Object> searchParams = new HashMap<>();
|
||||||
|
searchParams.put("access_token", fbMessenger.getToken());
|
||||||
|
searchParams.put("fields", "locale,first_name,last_name,profile_pic");
|
||||||
|
try {
|
||||||
|
String res = HttpClientUtil.doGet("https://graph.facebook.com/" + psid, searchParams);
|
||||||
|
JSONObject json = JSONObject.parseObject(res);
|
||||||
|
String firstName = json.getString("first_name");
|
||||||
|
String lastName = json.getString("last_name");
|
||||||
|
|
||||||
|
result.put("profile_pic", json.getString("profile_pic"));
|
||||||
|
|
||||||
|
saveProfilePic(result, json);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(firstName) && StringUtils.isNotBlank(lastName)) {
|
||||||
|
result.put("name", firstName + " " + lastName);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("[messenger] 详情获取异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveProfilePic(Map<String, String> result, JSONObject json) {
|
||||||
|
try {
|
||||||
|
String profile_pic = json.getString("profile_pic");
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(profile_pic)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||||
|
HttpGet httpGet = new HttpGet(profile_pic);
|
||||||
|
|
||||||
|
HttpResponse response = httpClient.execute(httpGet);
|
||||||
|
//获取Http响应的码 200
|
||||||
|
int startCode = response.getStatusLine().getStatusCode();
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
if (startCode == 200 && entity != null) {
|
||||||
|
InputStream input = entity.getContent();
|
||||||
|
long size = entity.getContentLength();
|
||||||
|
String fileid = MainUtils.getUUID();
|
||||||
|
StreamingFile sf = new StreamingFile();
|
||||||
|
sf.setId(fileid);
|
||||||
|
sf.setName(fileid);
|
||||||
|
sf.setMime(entity.getContentType().getValue());
|
||||||
|
sf.setData(jpaBlobHelper.createBlob(input, size));
|
||||||
|
streamingFileRes.save(sf);
|
||||||
|
String fileURL = "/res/image.html?id=" + fileid;
|
||||||
|
result.put("profile_pic", fileURL);
|
||||||
|
}
|
||||||
|
} catch (IOException exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendOTNContent(String fromId, String toId, JSONObject json) {
|
||||||
|
if (json.getString("type").equals("image")) {
|
||||||
|
sendImage(fromId, toId, json.getString("url"));
|
||||||
|
} else if (json.getString("type").equals("text") && StringUtils.isNotBlank(json.getString("content"))) {
|
||||||
|
send(fromId, toId, json.getString("content"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendOtnText(String fbToken, String otnToken, String msg) {
|
||||||
|
logger.info("send messenger to otnToken:{} msg:{}", otnToken, msg);
|
||||||
|
|
||||||
|
JSONObject inputJson = JSONObject.parseObject(msg);
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
JSONObject recipient = new JSONObject();
|
||||||
|
recipient.put("one_time_notif_token", otnToken);
|
||||||
|
JSONObject message = new JSONObject();
|
||||||
|
if (inputJson.getString("type").equals("image")) {
|
||||||
|
JSONObject attachment = new JSONObject();
|
||||||
|
attachment.put("type", "image");
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("url", "https://" + host + inputJson.getString("url"));
|
||||||
|
attachment.put("payload", payload);
|
||||||
|
message.put("attachment", attachment);
|
||||||
|
json.put("recipient", recipient);
|
||||||
|
json.put("message", message);
|
||||||
|
} else {
|
||||||
|
message.put("text", inputJson.getString("content"));
|
||||||
|
message.put("metadata", "DEVELOPER_DEFINED_METADATA");
|
||||||
|
json.put("recipient", recipient);
|
||||||
|
json.put("message", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = HttpClientUtil.doPost(FACEBOOK_MESSAGES_API + "?access_token=" + fbToken, json.toJSONString());
|
||||||
|
logger.info(result);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("[messenger] 发送消息异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String fromId, String toId, JSONObject message) {
|
||||||
|
send(fromId, toId, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String fromId, String toId, JSONObject message, FbMessenger fbMessenger) {
|
||||||
|
if (fbMessenger == null) {
|
||||||
|
fbMessenger = fbMessengerRepository.findOneByPageId(fromId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fbMessenger != null) {
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
JSONObject recipient = new JSONObject();
|
||||||
|
recipient.put("id", toId);
|
||||||
|
json.put("recipient", recipient);
|
||||||
|
json.put("message", message);
|
||||||
|
|
||||||
|
String result = HttpClientUtil.doPost(FACEBOOK_MESSAGES_API + "?access_token=" + fbMessenger.getToken(), json.toJSONString());
|
||||||
|
logger.info(result);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("[messenger] 发送消息异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendImage(String fromId, String toId, String imageUrl) {
|
||||||
|
logger.info("send messenger fromId:{} toId:{} image:{}", fromId, toId, imageUrl);
|
||||||
|
|
||||||
|
JSONObject message = new JSONObject();
|
||||||
|
JSONObject attachment = new JSONObject();
|
||||||
|
attachment.put("type", "image");
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
if (StringUtils.indexOf(imageUrl, "http") > -1) {
|
||||||
|
payload.put("url", imageUrl);
|
||||||
|
} else {
|
||||||
|
payload.put("url", "https://" + host + imageUrl);
|
||||||
|
}
|
||||||
|
attachment.put("payload", payload);
|
||||||
|
message.put("attachment", attachment);
|
||||||
|
|
||||||
|
send(fromId, toId, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String fromId, String toId, String msg) {
|
||||||
|
logger.info("send messenger fromId:{} toId:{} msg:{}", fromId, toId, msg);
|
||||||
|
|
||||||
|
JSONObject message = new JSONObject();
|
||||||
|
message.put("text", StringEscapeUtils.unescapeHtml(msg));
|
||||||
|
|
||||||
|
send(fromId, toId, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendOTNReq(String fromId, String toId, String title, String ref) {
|
||||||
|
logger.info("send messenger fromId:{} toId:{} title:{}", fromId, toId, title);
|
||||||
|
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOneByPageId(fromId);
|
||||||
|
if (fbMessenger != null) {
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
JSONObject recipient = new JSONObject();
|
||||||
|
recipient.put("id", toId);
|
||||||
|
JSONObject message = new JSONObject();
|
||||||
|
JSONObject attachment = new JSONObject();
|
||||||
|
attachment.put("type", "template");
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("template_type", "one_time_notif_req");
|
||||||
|
payload.put("title", title);
|
||||||
|
payload.put("payload", ref);
|
||||||
|
attachment.put("payload", payload);
|
||||||
|
message.put("attachment", attachment);
|
||||||
|
json.put("recipient", recipient);
|
||||||
|
json.put("message", message);
|
||||||
|
|
||||||
|
String result = HttpClientUtil.doPost(FACEBOOK_MESSAGES_API + "?access_token=" + fbMessenger.getToken(), json.toJSONString());
|
||||||
|
logger.info(result);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("[messenger] 发送消息异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,203 @@
|
|||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.chatopera.cc.activemq.BrokerPublisher;
|
||||||
|
import com.chatopera.cc.basic.Constants;
|
||||||
|
import com.chatopera.cc.basic.MainUtils;
|
||||||
|
import com.chatopera.cc.controller.Handler;
|
||||||
|
import com.chatopera.cc.controller.api.request.RestUtils;
|
||||||
|
import com.chatopera.cc.exception.CSKefuException;
|
||||||
|
import com.chatopera.cc.model.*;
|
||||||
|
import com.chatopera.cc.persistence.repository.FbMessengerRepository;
|
||||||
|
import com.chatopera.cc.persistence.repository.FbOTNRepository;
|
||||||
|
import com.chatopera.cc.proxy.AgentProxy;
|
||||||
|
import com.chatopera.cc.proxy.OrganProxy;
|
||||||
|
import com.chatopera.cc.util.Menu;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.ModelMap;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/apps/messenger/otn")
|
||||||
|
public class MessengerOTNController extends Handler {
|
||||||
|
@Autowired
|
||||||
|
private FbMessengerRepository fbMessengerRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbOTNRepository otnRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrganProxy organProxy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AgentProxy agentProxy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BrokerPublisher brokerPublisher;
|
||||||
|
|
||||||
|
private Map<String, Organ> getOwnOrgan(HttpServletRequest request) {
|
||||||
|
return organProxy.findAllOrganByParentAndOrgi(super.getOrgan(request), super.getOrgi(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/index")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView index(ModelMap map, HttpServletRequest request, @Valid String queryPageId) {
|
||||||
|
Map<String, Organ> organs = getOwnOrgan(request);
|
||||||
|
List<FbMessenger> fbMessengers = fbMessengerRepository.findByOrganIn(organs.keySet());
|
||||||
|
List<String> pageIds = fbMessengers.stream().map(p -> p.getPageId()).collect(Collectors.toList());
|
||||||
|
|
||||||
|
map.addAttribute("fbMessengers", fbMessengers);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(queryPageId)) {
|
||||||
|
map.addAttribute("queryPageId", queryPageId);
|
||||||
|
pageIds = Arrays.asList(queryPageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Page<FbOTN> otns = otnRepository.findByPageIdIn(pageIds, new PageRequest(super.getP(request), super.getPs(request), Sort.Direction.DESC, "createtime"));
|
||||||
|
map.addAttribute("otns", otns);
|
||||||
|
return request(super.createView("/admin/channel/messenger/otn/index"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/add")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView add(ModelMap map, HttpServletRequest request) {
|
||||||
|
Map<String, Organ> organs = getOwnOrgan(request);
|
||||||
|
List<FbMessenger> fbMessengers = fbMessengerRepository.findByOrganIn(organs.keySet());
|
||||||
|
map.addAttribute("fbMessengers", fbMessengers);
|
||||||
|
return request(super.createView("/admin/channel/messenger/otn/add"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/save")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView save(ModelMap map, @Valid FbOTN otn, HttpServletRequest request) {
|
||||||
|
String msg = "save_ok";
|
||||||
|
otn.setId(MainUtils.getUUID());
|
||||||
|
otn.setCreatetime(new Date());
|
||||||
|
otn.setUpdatetime(new Date());
|
||||||
|
otn.setSubNum(0);
|
||||||
|
otn.setMelinkNum(0);
|
||||||
|
|
||||||
|
otn.setStatus("create");
|
||||||
|
otnRepository.save(otn);
|
||||||
|
|
||||||
|
if (otn.getSendtime() != null) {
|
||||||
|
delaySend(otn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request(super.createView("redirect:/apps/messenger/otn/index.html?msg=" + msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/edit")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView edit(ModelMap map, @Valid String id, HttpServletRequest request) {
|
||||||
|
map.addAttribute("otn", otnRepository.getOne(id));
|
||||||
|
return request(super.createView("/admin/channel/messenger/otn/edit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/update")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView update(ModelMap map, @Valid FbOTN otn, HttpServletRequest request) {
|
||||||
|
String msg = "update_ok";
|
||||||
|
FbOTN oldOtn = otnRepository.findOne(otn.getId());
|
||||||
|
if (oldOtn != null) {
|
||||||
|
Date oldSendtime = oldOtn.getSendtime();
|
||||||
|
|
||||||
|
oldOtn.setName(otn.getName());
|
||||||
|
oldOtn.setPreSubMessage(otn.getPreSubMessage());
|
||||||
|
oldOtn.setSubMessage(otn.getSubMessage());
|
||||||
|
oldOtn.setOtnMessage(otn.getOtnMessage());
|
||||||
|
oldOtn.setSuccessMessage(otn.getSuccessMessage());
|
||||||
|
oldOtn.setSendtime(otn.getSendtime());
|
||||||
|
oldOtn.setUpdatetime(new Date());
|
||||||
|
otnRepository.save((oldOtn));
|
||||||
|
|
||||||
|
if (otn.getSendtime() != null && !otn.getSendtime().equals(oldSendtime)) {
|
||||||
|
delaySend(otn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return request(super.createView("redirect:/apps/messenger/otn/index.html?msg=" + msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void delaySend(FbOTN otn) {
|
||||||
|
long delaySeconds = (otn.getSendtime().getTime() - new Date().getTime()) / 1000;
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("otnId", otn.getId());
|
||||||
|
payload.put("sendtime", otn.getSendtime());
|
||||||
|
brokerPublisher.send(Constants.INSTANT_MESSAGING_MQ_QUEUE_FACEBOOK_OTN, payload.toJSONString(), false, (int) delaySeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/send")
|
||||||
|
@Menu(type = "admin", subtype = "faceb ook")
|
||||||
|
public ModelAndView send(ModelMap map, @Valid String id, HttpServletRequest request) {
|
||||||
|
String msg = "send_ok";
|
||||||
|
|
||||||
|
FbOTN otn = otnRepository.getOne(id);
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOneByPageId(otn.getPageId());
|
||||||
|
if (fbMessenger != null && otn != null && otn.getStatus().equals("create")) {
|
||||||
|
otn.setStatus("sending");
|
||||||
|
otn.setSendtime(new Date());
|
||||||
|
otnRepository.save(otn);
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("otnId", otn.getId());
|
||||||
|
brokerPublisher.send(Constants.INSTANT_MESSAGING_MQ_QUEUE_FACEBOOK_OTN, payload.toJSONString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return request(super.createView("redirect:/apps/messenger/otn/index.html?msg=" + msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/image/upload")
|
||||||
|
@Menu(type = "admin", subtype = "image", access = false)
|
||||||
|
public ResponseEntity<String> upload(
|
||||||
|
ModelMap map,
|
||||||
|
HttpServletRequest request,
|
||||||
|
@RequestParam(value = "imgFile", required = false) MultipartFile multipart
|
||||||
|
) throws IOException {
|
||||||
|
final User logined = super.getUser(request);
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
HttpHeaders headers = RestUtils.header();
|
||||||
|
if (multipart != null && multipart.getOriginalFilename().lastIndexOf(".") > 0) {
|
||||||
|
try {
|
||||||
|
StreamingFile sf = agentProxy.saveFileIntoMySQLBlob(logined, multipart);
|
||||||
|
result.put("error", 0);
|
||||||
|
result.put("url", sf.getFileUrl());
|
||||||
|
} catch (CSKefuException e) {
|
||||||
|
result.put("error", 1);
|
||||||
|
result.put("message", "请选择文件");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.put("error", 1);
|
||||||
|
result.put("message", "请选择图片文件");
|
||||||
|
}
|
||||||
|
return new ResponseEntity<>(result.toString(), headers, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/delete")
|
||||||
|
@Menu(type = "admin", subtype = "messenger")
|
||||||
|
public ModelAndView delete(ModelMap map, HttpServletRequest request, @Valid String id) {
|
||||||
|
String msg = "delete_ok";
|
||||||
|
otnRepository.delete(id);
|
||||||
|
|
||||||
|
return request(super.createView("redirect:/apps/messenger/otn/index.html?msg=" + msg));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Chatopera Inc, <https://www.chatopera.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.chatopera.cc.basic.plugins.AbstractPluginConfigurer;
|
||||||
|
import com.chatopera.cc.basic.plugins.PluginRegistry;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定义Plugin存在
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class MessengerPluginConfigurer extends AbstractPluginConfigurer {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessengerPluginConfigurer.class);
|
||||||
|
private final static String pluginName = "Messenger 渠道";
|
||||||
|
private final static String pluginId = "messenger";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PluginRegistry pluginRegistry;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void setup() {
|
||||||
|
pluginRegistry.addPlugin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPluginId() {
|
||||||
|
return pluginId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取消息服务的Bean的名字
|
||||||
|
* 当该方法存在时,加载到消息处理的调用栈 PeerSyncIM 中
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getPluginName() {
|
||||||
|
return pluginName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIOEventHandler() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isModule() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Chatopera Inc, <https://www.chatopera.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chatopera.cc.plugins.messenger;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.fastjson.JSONPath;
|
||||||
|
import com.chatopera.bot.exception.ChatbotException;
|
||||||
|
import com.chatopera.cc.basic.MainContext;
|
||||||
|
import com.chatopera.cc.controller.Handler;
|
||||||
|
import com.chatopera.cc.exception.CSKefuException;
|
||||||
|
import com.chatopera.cc.model.FbMessenger;
|
||||||
|
import com.chatopera.cc.persistence.repository.FbMessengerRepository;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/messenger")
|
||||||
|
public class MessengerWebhookChannelController extends Handler {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessengerWebhookChannelController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FbMessengerRepository fbMessengerRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessengerMessageProxy messengerMessageProxy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessengerChatbot messengerChatbot;
|
||||||
|
|
||||||
|
@RequestMapping(value = "/webhook/{pageId}", method = RequestMethod.GET)
|
||||||
|
@ResponseBody
|
||||||
|
public String get(@PathVariable("pageId") String pageId, @RequestParam("hub.challenge") String challenge, @RequestParam("hub.verify_token") String verify_token) {
|
||||||
|
logger.info("[get] verify token pageid {}", pageId);
|
||||||
|
FbMessenger fbMessenger = fbMessengerRepository.findOneByPageId(pageId);
|
||||||
|
String result = "not allow!";
|
||||||
|
|
||||||
|
if (fbMessenger != null && fbMessenger.getVerifyToken().equals(verify_token)) {
|
||||||
|
result = challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/webhook/{pageId}", method = RequestMethod.POST)
|
||||||
|
@ResponseBody
|
||||||
|
public String post(@PathVariable("pageId") String pageId, @RequestBody JSONObject jsonParam) {
|
||||||
|
logger.info("[post] pageId {}, payload {}", pageId, jsonParam.toString());
|
||||||
|
// NOTE: 此处 PathVariable pageId 不安全,不建议在函数内使用,详见 #1190
|
||||||
|
// https://gitlab.chatopera.com/cskefu/cskefu.io/issues/1190
|
||||||
|
// 该值可使用 recipientId 更为准确,recipientId 就是实际的 pageId
|
||||||
|
if (jsonParam.getString("object").equals("page")) {
|
||||||
|
JSONArray entries = jsonParam.getJSONArray("entry");
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
JSONObject entry = entries.getJSONObject(i);
|
||||||
|
JSONArray messaging = entry.getJSONArray("messaging");
|
||||||
|
for (int j = 0; j < messaging.size(); j++) {
|
||||||
|
try {
|
||||||
|
messageHandler(messaging.getJSONObject(j));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("[messenger] 接收消息异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void messageHandler(JSONObject messagingEvent) throws ChatbotException, CSKefuException, MalformedURLException {
|
||||||
|
String senderId = (String) JSONPath.eval(messagingEvent, "$.sender.id");
|
||||||
|
String recipientId = (String) JSONPath.eval(messagingEvent, "$.recipient.id");
|
||||||
|
String referralType = (String) JSONPath.eval(messagingEvent, "$.referral.type");
|
||||||
|
String optinType = (String) JSONPath.eval(messagingEvent, "$.optin.type");
|
||||||
|
String postbackPayload = (String) JSONPath.eval(messagingEvent, "$.postback.payload");
|
||||||
|
JSONObject quickRepliesPayload = (JSONObject) JSONPath.eval(messagingEvent, "$.message.quick_reply");
|
||||||
|
|
||||||
|
if (StringUtils.equals(referralType, "OPEN_THREAD")) {
|
||||||
|
String ref = (String) JSONPath.eval(messagingEvent, "$.referral.ref");
|
||||||
|
messengerMessageProxy.acceptMeLink(senderId, recipientId, ref);
|
||||||
|
return;
|
||||||
|
} else if (StringUtils.equals(optinType, "one_time_notif_req")) {
|
||||||
|
String otnToken = (String) JSONPath.eval(messagingEvent, "$.optin.one_time_notif_token");
|
||||||
|
String ref = (String) JSONPath.eval(messagingEvent, "$.optin.payload");
|
||||||
|
messengerMessageProxy.acceptOTNReq(senderId, recipientId, otnToken, ref);
|
||||||
|
return;
|
||||||
|
} else if (StringUtils.isNotBlank(postbackPayload)) {
|
||||||
|
if (StringUtils.equals(postbackPayload, "TRANSFER_LABOR")) {
|
||||||
|
messengerChatbot.switchManualCustomerService(senderId);
|
||||||
|
} else if (StringUtils.equals(postbackPayload, "FAQ_LIST")) {
|
||||||
|
String msg = (String) JSONPath.eval(messagingEvent, "$.postback.title");
|
||||||
|
messengerMessageProxy.accept(senderId, recipientId, MainContext.MediaType.TEXT, msg);
|
||||||
|
} else if (StringUtils.equals(postbackPayload, "startChatopera")) {
|
||||||
|
messengerMessageProxy.accept(senderId, recipientId, MainContext.MediaType.TEXT, "__kickoff");
|
||||||
|
} else {
|
||||||
|
messengerMessageProxy.accept(senderId, recipientId, MainContext.MediaType.TEXT, postbackPayload);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (quickRepliesPayload != null) {
|
||||||
|
String msg = (String) JSONPath.eval(messagingEvent, "$.message.quick_reply.payload");
|
||||||
|
messengerMessageProxy.accept(senderId, recipientId, MainContext.MediaType.TEXT, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String msg = (String) JSONPath.eval(messagingEvent, "$.message.text");
|
||||||
|
JSONArray attachments = (JSONArray) JSONPath.eval(messagingEvent, "$.message.attachments");
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(msg)) {
|
||||||
|
messengerMessageProxy.accept(senderId, recipientId, MainContext.MediaType.TEXT, msg);
|
||||||
|
} else {
|
||||||
|
for (Object att : attachments) {
|
||||||
|
|
||||||
|
String type = (String) JSONPath.eval(att, "$.type");
|
||||||
|
String url = (String) JSONPath.eval(att, "$.payload.url");
|
||||||
|
|
||||||
|
if (StringUtils.equals(type, "image")) {
|
||||||
|
messengerMessageProxy.accept(senderId, recipientId, MainContext.MediaType.IMAGE, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
|||||||
|
.uk-layui-form(style="margin:20px auto")
|
||||||
|
form.layui-form(action="/admin/messenger/save.html", method="post")
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| 技能组
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="text", name="name", required="", lay-verify="required", autocomplete="off", value="#{organ.name}", disabled="disabled")
|
||||||
|
i.csfont(style='position: absolute;right: 3px;top: 8px;font-size: 20px;color: #e6e6e6') 
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| 该渠道的人工服务由哪个技能组接待,默认为当前技能组,在右上角切换
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| 名称
|
||||||
|
font(color='red') *
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="text", name="name", required="", lay-verify="required", autocomplete="off", placeholder="北美玩家页")
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| 使用字符串命名该渠道
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| Page ID
|
||||||
|
font(color='red') *
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="text", name="pageId", required="", lay-verify="required", autocomplete="off", placeholder="e.g. 1541840459")
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| Facebook Messenger 设置页面获得 Page Id
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| Access Token
|
||||||
|
font(color='red') *
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="password", name="token", required="", lay-verify="required", autocomplete="off", placeholder="*********")
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| Facebook Messenger 设置页面获得 Access Token
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| Verify Token
|
||||||
|
font(color='red') *
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="text", name="verifyToken", required="", lay-verify="required", autocomplete="off", placeholder="e.g. uN26itM4Ec")
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| 自定义,并且回填到 Facebook Messenger 设置页面
|
||||||
|
.layui-form-button
|
||||||
|
.layui-button-block
|
||||||
|
button.layui-btn(lay-submit="", lay-filter="formDemo") 提交
|
||||||
|
button.layui-btn.layui-btn-original(type="reset") 重置
|
||||||
|
script.
|
||||||
|
layui.use('form', function () {
|
||||||
|
var form = layui.form();
|
||||||
|
form.render(); //更新全部
|
||||||
|
});
|
||||||
|
layui.use('element', function () {
|
||||||
|
var element = layui.element();
|
||||||
|
});
|
@ -0,0 +1,61 @@
|
|||||||
|
.uk-layui-form(style="margin:20px auto")
|
||||||
|
form.layui-form(action="/admin/messenger/update.html", method="post")
|
||||||
|
input(type="hidden", name="id", value=fb.id)
|
||||||
|
input(type="hidden", name="status", value=fb.status)
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| 技能组
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="text", name="name", required="", lay-verify="required", autocomplete="off", value=organ.name, disabled="disabled")
|
||||||
|
i.csfont(style='position: absolute;right: 3px;top: 8px;font-size: 20px;color: #e6e6e6') 
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| 该渠道的人工服务由哪个技能组接待,默认为当前技能组,在右上角切换
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| 名称
|
||||||
|
font(color='red') *
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="text", name="name", required="", lay-verify="required", autocomplete="off", value=fb.name)
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| 使用字符串命名该渠道
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| Page ID
|
||||||
|
font(color='red') *
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", disabled ,type="text", name="pageId", required="", lay-verify="required", autocomplete="off", value=fb.pageId)
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| Facebook Messenger 设置页面获得 Page Id
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| Access Token
|
||||||
|
font(color='red') *
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="password", name="token", required="", lay-verify="required", autocomplete="off", value=fb.token)
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| Facebook Messenger 设置页面获得 Access Token
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style="width:150px;")
|
||||||
|
| Verify Token
|
||||||
|
font(color='red') *
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(style="width: 200px;", type="text", name="verifyToken", required="", lay-verify="required", autocomplete="off", value=fb.verifyToken)
|
||||||
|
.layui-form-mid.layui-word-aux
|
||||||
|
| 自定义,并且回填到 Facebook Messenger 设置页面
|
||||||
|
.layui-form-button
|
||||||
|
.layui-button-block
|
||||||
|
button.layui-btn(lay-submit="", lay-filter="formDemo") 提交
|
||||||
|
button.layui-btn.layui-btn-original(type="reset") 重置
|
||||||
|
script.
|
||||||
|
layui.use('form', function () {
|
||||||
|
var form = layui.form();
|
||||||
|
form.render(); //更新全部
|
||||||
|
});
|
||||||
|
layui.use('element', function () {
|
||||||
|
var element = layui.element();
|
||||||
|
});
|
@ -0,0 +1,17 @@
|
|||||||
|
.row(style="border-bottom: 10px solid #EFEFEF;padding:10px;")
|
||||||
|
.col-lg-8
|
||||||
|
.ukefu-measure
|
||||||
|
.ukefu-bt
|
||||||
|
.layui-icon.ukewo-btn.ukefu-content-ind
|
||||||
|
img(src="/images/messenger.png", style="width:60px;height:60px;")
|
||||||
|
.ukefu-bt-text
|
||||||
|
.ukefu-bt-text-title(style="font-weight:400;font-size:24px;border-bottom:1px solid #dedede;") #{fb.name}
|
||||||
|
span(style="font-size:15px;color:#AAAAAA;") 接入渠道创建时间:#{pugHelper.formatDate('yyyy-MM-dd HH:mm:ss', fb.createtime)}
|
||||||
|
|
||||||
|
.ukefu-bt-text-content(style="") Facebook Page Id: #{fb.pageId}
|
||||||
|
.ukefu-tab-title(style="margin-left: 0px;")
|
||||||
|
ul.tab-title
|
||||||
|
.ukefu-tab-title(style="margin-left: 0px;")
|
||||||
|
ul.tab-title
|
||||||
|
li(class={'layui-this': subtype == 'messenger'})
|
||||||
|
a(href="/admin/messenger/setting.html?id=" + fb.id) 基本设置
|
@ -0,0 +1,176 @@
|
|||||||
|
extends /admin/include/layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.row
|
||||||
|
input#copyInput(type="hidden")
|
||||||
|
#me-config.col-lg-12
|
||||||
|
h1.site-h1(style="background-color:#FFFFFF;")
|
||||||
|
| Messenger 列表 (#{size(fbMessengers)})
|
||||||
|
span(style="float:right;")
|
||||||
|
.layui-btn-group
|
||||||
|
if organ.skill == true
|
||||||
|
button.layui-btn.layui-btn-small.green(href="/admin/messenger/add.html", data-toggle="ajax", data-width="800", data-height="400", data-title="创建 Messenger 渠道") 创建渠道
|
||||||
|
else
|
||||||
|
button.layui-btn.layui-btn-disabled.layui-btn-small.layui-btn-warm(data-toggle="tooltip" title="当前组织机构非技能组,不支持创建 Messenger 渠道,使用右上角菜单切换") 创建渠道
|
||||||
|
button.layui-btn.layui-btn-small.layui-btn-normal(onclick='openExternalUrlWithBlankTarget("https://docs.chatopera.com/products/cskefu/channels/messenger/index.html")') 文档中心
|
||||||
|
.row(style="padding:5px;")
|
||||||
|
blockquote.layui-elem-quote.layui-quote-nm
|
||||||
|
i.layui-icon(style="color:gray") 
|
||||||
|
font(color="#999").layui-word-aux Messenger 是 Facebook 提供给企业、商铺、消费者之间相互连接的即时通信工具,与 Facebook 粉丝页、Instagram 和网页聊天等多渠道快速发起对话,春松客服支持与 Messenger 集成,如有疑问,阅读文档中心,获得详细使用指南。
|
||||||
|
.col-lg-12
|
||||||
|
table.layui-table(lay-skin="line", style="table-layout: fixed; word-break: break-all")
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th(style="width:100px") 名称
|
||||||
|
th 技能组
|
||||||
|
th Page ID
|
||||||
|
th(style="width:150px") 创建时间
|
||||||
|
th
|
||||||
|
| 状态
|
||||||
|
i.layui-icon(style="color:gray" data-toggle="tooltip" title="处于关闭状态,只对该渠道支持 Facebook OTN 功能;在开启状态,即支持客服功能也支持 Facebook OTN 功能") 
|
||||||
|
th Webhook
|
||||||
|
th(style="width:250px;white-space:nowrap;", nowrap="nowrap") 操作
|
||||||
|
tbody
|
||||||
|
for item in fbMessengers
|
||||||
|
tr
|
||||||
|
td(title="#{item.name}", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")= item.name
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")= organs[item.organ].name
|
||||||
|
td(title="#{item.pageId}", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")= item.pageId
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")= pugHelper.formatDate("yyyy-MM-dd HH:mm:ss", item.createtime)
|
||||||
|
td(style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
.fbStatus.layui-unselect.layui-form-switch.checkStatus.lay-filter(style="margin-top: 0px;width: 50px;", data-id=item.id, class={
|
||||||
|
'layui-form-onswitch': item.status != 'disabled',
|
||||||
|
'layui-form-offswitch': item.status == 'disabled'
|
||||||
|
})
|
||||||
|
i.checkStatusI
|
||||||
|
td(style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
button.layui-btn.layui-btn-small.webhook(type="button", data-pageid=item.pageId) 复制
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
a(href="/admin/messenger/setting.html?id=" + item.id, data-toggle="load", data-target="#me-config")
|
||||||
|
i.layui-icon
|
||||||
|
| 设置
|
||||||
|
a(href="/admin/messenger/edit.html?id=" + item.id, style="margin-left:10px;", data-toggle="ajax", data-width="800", data-height="400", data-title="编辑 Messenger 渠道")
|
||||||
|
i.layui-icon
|
||||||
|
| 编辑
|
||||||
|
a(href="https://www.facebook.com/" + item.pageId, style="margin-left:10px;", target="_blank")
|
||||||
|
i.layui-icon 
|
||||||
|
| 粉丝页
|
||||||
|
a(href="/admin/messenger/delete.html?id=" + item.id, style="margin-left:10px;", data-toggle="tip", title="请确认是否删除?")
|
||||||
|
i.layui-icon(style="color:red;") ဆ
|
||||||
|
| 删除
|
||||||
|
|
||||||
|
style.
|
||||||
|
.fbStatus.layui-form-onswitch .checkStatusI {
|
||||||
|
left: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fbStatus.layui-form-onswitch:before {
|
||||||
|
content: '开启';
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fbStatus.layui-form-offswitch:before {
|
||||||
|
content: '关闭';
|
||||||
|
color: #aaaaaa;
|
||||||
|
padding-left: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
script.
|
||||||
|
// 打开链接
|
||||||
|
function openExternalUrlWithBlankTarget(externalUrl) {
|
||||||
|
window.open(externalUrl, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
layui.use('layer', function () {
|
||||||
|
var layer = layui.layer;
|
||||||
|
var msg = '#{msg}'
|
||||||
|
if (msg == 'save_ok')
|
||||||
|
layer.msg('渠道添加成功', {icon: 1, time: 1000})
|
||||||
|
else if (msg == 'update_ok')
|
||||||
|
layer.msg('保存成功', {icon: 1, time: 1000})
|
||||||
|
else if (msg == 'save_no_PageId')
|
||||||
|
layer.msg('Page ID已存在', {icon: 2, time: 3000})
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.fbStatus').click(function () {
|
||||||
|
var isOpen = $(this).hasClass('layui-form-onswitch')
|
||||||
|
var id = $(this).data('id');
|
||||||
|
var status = !isOpen ? 'enabled' : 'disabled';
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
$.post('setStatus.html', {id, status}).success(function (msg) {
|
||||||
|
if (msg == 'ok') {
|
||||||
|
if (isOpen) {
|
||||||
|
$(that).removeClass('layui-form-onswitch');
|
||||||
|
$(that).addClass('layui-form-offswitch');
|
||||||
|
} else {
|
||||||
|
$(that).removeClass('layui-form-offswitch');
|
||||||
|
$(that).addClass('layui-form-onswitch');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
$('.webhook').each(function (i, n) {
|
||||||
|
var htmlOld = $(n).attr("data-pageId");
|
||||||
|
var webhook = window.location.protocol + "//" + window.location.host + "/messenger/webhook/" + htmlOld;
|
||||||
|
$(n).on('click', function () {
|
||||||
|
Clipboard.copy(webhook);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
window.Clipboard = (function (window, document, navigator) {
|
||||||
|
var textArea,
|
||||||
|
copy;
|
||||||
|
|
||||||
|
// 判断是不是ios端
|
||||||
|
function isOS() {
|
||||||
|
return navigator.userAgent.match(/ipad|iphone/i);
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建文本元素
|
||||||
|
function createTextArea(text) {
|
||||||
|
textArea = document.createElement('textArea');
|
||||||
|
textArea.value = text;
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
//选择内容
|
||||||
|
function selectText() {
|
||||||
|
var range,
|
||||||
|
selection;
|
||||||
|
if (isOS()) {
|
||||||
|
range = document.createRange();
|
||||||
|
range.selectNodeContents(textArea);
|
||||||
|
selection = window.getSelection();
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
textArea.setSelectionRange(0, 999999);
|
||||||
|
} else {
|
||||||
|
textArea.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//复制到剪贴板
|
||||||
|
function copyToClipboard() {
|
||||||
|
try {
|
||||||
|
if (document.execCommand("Copy")) {
|
||||||
|
layer.msg('webhook已复制到剪切板', {icon: 1, time: 1000, offset: 'rt'})
|
||||||
|
} else {
|
||||||
|
layer.msg('webhook复制失败请手动复制', {icon: 2, time: 1000, offset: 'rt'})
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
layer.msg('webhook复制失败请手动复制', {icon: 2, time: 1000, offset: 'rt'})
|
||||||
|
}
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
copy = function (text) {
|
||||||
|
createTextArea(text);
|
||||||
|
selectText();
|
||||||
|
copyToClipboard();
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
copy: copy
|
||||||
|
};
|
||||||
|
})(window, document, navigator);
|
@ -0,0 +1,133 @@
|
|||||||
|
.row
|
||||||
|
.col-lg-12
|
||||||
|
.ukefu-customer-div.setting-wrapper
|
||||||
|
.box.default-box
|
||||||
|
.box-header
|
||||||
|
h1.site-h1 新建OTN
|
||||||
|
.row
|
||||||
|
.col-lg-12
|
||||||
|
.uk-layui-form(style="height: calc(100% + 10px);")
|
||||||
|
form.layui-form(action="/apps/messenger/otn/save.html", method="post")
|
||||||
|
input#preSubMessage(type="hidden", name="preSubMessage", value=otn.preSubMessage)
|
||||||
|
.layui-colla-item
|
||||||
|
h2.layui-colla-title 基本信息
|
||||||
|
.layui-colla-content.layui-show
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label 名称:
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(type='text', name='name', required, lay-verify='required', autocomplete='off')
|
||||||
|
.layui-inline
|
||||||
|
font(color='red') *(必填项)
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style='width:60px;line-height: 35px;') 渠道:
|
||||||
|
.layui-input-inline(style='width:218px;margin-right:0px;padding-top:9px;')
|
||||||
|
select(name='pageId', required, lay-verify='required')
|
||||||
|
option(value="") 请选择渠道...
|
||||||
|
if fbMessengers
|
||||||
|
for m in fbMessengers
|
||||||
|
option(value=m.pageId)= m.name
|
||||||
|
.layui-inline
|
||||||
|
font(color='red') *(必填项)
|
||||||
|
.layui-colla-item
|
||||||
|
h2.layui-colla-title 订阅邀请信息(2000字符)
|
||||||
|
.layui-colla-content.layui-show
|
||||||
|
blockquote.layui-elem-quote.layui-quote-nm
|
||||||
|
i.layui-icon(style="color:gray") 
|
||||||
|
font(color="#999").layui-word-aux 访客点击链接后发送订阅邀请信息给访客
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline(style="width: 380px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px")
|
||||||
|
| 订阅邀请前消息1
|
||||||
|
//font(color='red') *(必填项)
|
||||||
|
.layui-input-inline
|
||||||
|
#preSubMessage1.messageBox(name="preSubMessage1")
|
||||||
|
.layui-inline(style="width: 380px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px")
|
||||||
|
| 订阅邀请前消息2
|
||||||
|
//font(color='red') *(必填项)
|
||||||
|
.layui-input-inline
|
||||||
|
#preSubMessage2.messageBox(name="preSubMessage2")
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline(style="width: 320px;height: 209px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px")
|
||||||
|
| 订阅邀请
|
||||||
|
font(color='red') *(必填项,60字符)
|
||||||
|
.layui-input-inline
|
||||||
|
textarea(name="subMessage", required="required", lay-verify="maxSubMessage", style="margin: 0px; width: 310px; height: 170px;resize:none;border: 1px solid #ccc")
|
||||||
|
|
||||||
|
.layui-colla-item
|
||||||
|
h2.layui-colla-title 订阅成功提醒(2000字符)
|
||||||
|
.layui-colla-content.layui-show
|
||||||
|
.layui-input-inline
|
||||||
|
.messageBox(name="successMessage")
|
||||||
|
|
||||||
|
.layui-colla-item
|
||||||
|
h2.layui-colla-title 推送信息(2000字符)
|
||||||
|
.layui-colla-content.layui-show
|
||||||
|
blockquote.layui-elem-quote.layui-quote-nm
|
||||||
|
i.layui-icon(style="color:gray") 
|
||||||
|
font(color="#999").layui-word-aux 推送时间到达时,此内容通过OTN推送给访客。推送时间可以不设置,后续手动推送。
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline(style="width: 380px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px") OTN内容
|
||||||
|
.layui-input-inline
|
||||||
|
.messageBox(name="otnMessage")
|
||||||
|
.layui-inline(style="width: 320px;height: 209px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px") 推送时间
|
||||||
|
.layui-input-inline
|
||||||
|
input#begin.layui-input.ukefu-input(name='sendtime', style="width:310px")
|
||||||
|
|
||||||
|
.layui-button-block(style="width: 700px;")
|
||||||
|
button.layui-btn(lay-submit="", lay-filter="formDemo") 提交
|
||||||
|
button.layui-btn.layui-btn-original(type="reset", onclick="location.reload()") 取消
|
||||||
|
|
||||||
|
script.
|
||||||
|
layui.use('form', function () {
|
||||||
|
var form = layui.form();
|
||||||
|
form.render(); //更新全部
|
||||||
|
|
||||||
|
form.verify({
|
||||||
|
maxSubMessage: function (value) {
|
||||||
|
if (value.length > 50) {
|
||||||
|
return '长度大于60!请重新输入';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
form.on('submit(formDemo)', function () {
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
layui.use('laydate', function () {
|
||||||
|
var laydate = layui.laydate;
|
||||||
|
document.getElementById('begin').onclick = function () {
|
||||||
|
var date = {
|
||||||
|
min: laydate.now(),
|
||||||
|
format: 'YYYY-MM-DD hh:mm:ss',
|
||||||
|
istoday: false,
|
||||||
|
istime: true
|
||||||
|
};
|
||||||
|
date.elem = this;
|
||||||
|
laydate(date);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
layui.use('element', function () {
|
||||||
|
var element = layui.element();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".messageBox").otnContent();
|
||||||
|
|
||||||
|
function preSubMessageChange() {
|
||||||
|
var preSubMessage1 = JSON.parse($('#preSubMessage1 input').val() || '{}');
|
||||||
|
var preSubMessage2 = JSON.parse($('#preSubMessage2 input').val() || '{}');
|
||||||
|
|
||||||
|
$('#preSubMessage').val(JSON.stringify([preSubMessage1, preSubMessage2]))
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#preSubMessage1').change(preSubMessageChange);
|
||||||
|
$('#preSubMessage2').change(preSubMessageChange);
|
||||||
|
|
||||||
|
|
@ -0,0 +1,131 @@
|
|||||||
|
.row
|
||||||
|
.col-lg-12
|
||||||
|
.ukefu-customer-div.setting-wrapper
|
||||||
|
.box.default-box
|
||||||
|
.box-header
|
||||||
|
h1.site-h1 编辑OTN
|
||||||
|
|
||||||
|
.row
|
||||||
|
.col-lg-12
|
||||||
|
.uk-layui-form(style="height: calc(100% + 10px);")
|
||||||
|
form.layui-form(action="/apps/messenger/otn/update.html", method="post")
|
||||||
|
input(type="hidden", name="pageId", value=pageId)
|
||||||
|
input(type="hidden", name="id", value=otn.id)
|
||||||
|
input#preSubMessage(type="hidden", name="preSubMessage", value=otn.preSubMessage)
|
||||||
|
.layui-colla-item
|
||||||
|
h2.layui-colla-title 基本信息
|
||||||
|
.layui-colla-content.layui-show
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label 名称:
|
||||||
|
.layui-input-inline
|
||||||
|
input.layui-input(type='text', name='name', required, lay-verify='required', autocomplete='off', value=otn.name)
|
||||||
|
.layui-inline
|
||||||
|
font(color='red') *(必填项)
|
||||||
|
.layui-inline
|
||||||
|
label.layui-form-label(style='width:60px;line-height: 35px;') 渠道:
|
||||||
|
.layui-input-inline(style='width:218px;margin-right:0px;padding-top:9px;')
|
||||||
|
p(style="padding: 10px 0;")= otn.fbMessenger.name
|
||||||
|
.layui-colla-item
|
||||||
|
h2.layui-colla-title 订阅邀请信息(2000字符)
|
||||||
|
.layui-colla-content.layui-show
|
||||||
|
blockquote.layui-elem-quote.layui-quote-nm
|
||||||
|
i.layui-icon(style="color:gray") 
|
||||||
|
font(color="#999").layui-word-aux 访客点击链接后发送订阅邀请信息给访客
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline(style="width: 380px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px")
|
||||||
|
| 订阅邀请前消息1
|
||||||
|
//font(color='red') *(必填项)
|
||||||
|
.layui-input-inline
|
||||||
|
#preSubMessage1.messageBox(name="preSubMessage1")
|
||||||
|
.layui-inline(style="width: 380px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px")
|
||||||
|
| 订阅邀请前消息2
|
||||||
|
//font(color='red') *(必填项)
|
||||||
|
.layui-input-inline
|
||||||
|
#preSubMessage2.messageBox(name="preSubMessage2")
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline(style="width: 320px;height: 209px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px")
|
||||||
|
| 订阅邀请
|
||||||
|
font(color='red') *(必填项,65字符)
|
||||||
|
.layui-input-inline
|
||||||
|
textarea(name="subMessage", required="required", lay-verify="maxSubMessage", style="margin: 0px; width: 310px; height: 170px;resize:none;border: 1px solid #ccc")= otn.subMessage
|
||||||
|
|
||||||
|
.layui-colla-item
|
||||||
|
h2.layui-colla-title 订阅成功提醒(2000字符)
|
||||||
|
.layui-colla-content.layui-show
|
||||||
|
.layui-input-inline
|
||||||
|
.messageBox(name="successMessage",value=otn.successMessage)
|
||||||
|
|
||||||
|
.layui-colla-item
|
||||||
|
h2.layui-colla-title 推送信息(2000字符)
|
||||||
|
.layui-colla-content.layui-show
|
||||||
|
blockquote.layui-elem-quote.layui-quote-nm
|
||||||
|
i.layui-icon(style="color:gray") 
|
||||||
|
font(color="#999").layui-word-aux 推送时间到达时,此内容通过OTN推送给访客。推送时间可以不设置,后续手动推送。
|
||||||
|
.layui-form-item
|
||||||
|
.layui-inline(style="width: 380px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px") OTN内容
|
||||||
|
.layui-input-inline
|
||||||
|
.messageBox(name="otnMessage",value=otn.otnMessage)
|
||||||
|
.layui-inline(style="width: 320px;height: 209px;")
|
||||||
|
label.layui-form-label(style="float: none;text-align: left;width:200px") 推送时间
|
||||||
|
.layui-input-inline
|
||||||
|
input#begin.layui-input.ukefu-input(name='sendtime', style="width:310px", value=pugHelper.formatDate("yyyy-MM-dd HH:mm:ss", otn.sendtime))
|
||||||
|
|
||||||
|
.layui-button-block(style="width: 700px;")
|
||||||
|
button.layui-btn(lay-submit="", lay-filter="formDemo") 提交
|
||||||
|
button.layui-btn.layui-btn-original(type="reset", onclick="location.reload()") 取消
|
||||||
|
|
||||||
|
script.
|
||||||
|
layui.use('form', function () {
|
||||||
|
var form = layui.form();
|
||||||
|
form.render(); //更新全部
|
||||||
|
|
||||||
|
form.verify({
|
||||||
|
maxSubMessage: function (value) {
|
||||||
|
if (value.length > 50) {
|
||||||
|
return '长度大于60!请重新输入';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
form.on('submit(formDemo)', function () {
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
layui.use('element', function () {
|
||||||
|
var element = layui.element();
|
||||||
|
});
|
||||||
|
|
||||||
|
layui.use('laydate', function () {
|
||||||
|
var laydate = layui.laydate;
|
||||||
|
document.getElementById('begin').onclick = function () {
|
||||||
|
var date = {
|
||||||
|
min: laydate.now(),
|
||||||
|
format: 'YYYY-MM-DD hh:mm:ss',
|
||||||
|
istoday: false,
|
||||||
|
istime: true
|
||||||
|
};
|
||||||
|
date.elem = this;
|
||||||
|
laydate(date);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var preSubMessage = JSON.parse($('#preSubMessage').val() || '[{},{}]');
|
||||||
|
$('#preSubMessage1').attr('value', JSON.stringify(preSubMessage[0]));
|
||||||
|
$('#preSubMessage2').attr('value', JSON.stringify(preSubMessage[1]));
|
||||||
|
|
||||||
|
$(".messageBox").otnContent();
|
||||||
|
|
||||||
|
function preSubMessageChange() {
|
||||||
|
var preSubMessage1 = JSON.parse($('#preSubMessage1 input').val() || '{}');
|
||||||
|
var preSubMessage2 = JSON.parse($('#preSubMessage2 input').val() || '{}');
|
||||||
|
|
||||||
|
$('#preSubMessage').val(JSON.stringify([preSubMessage1, preSubMessage2]))
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#preSubMessage1').change(preSubMessageChange);
|
||||||
|
$('#preSubMessage2').change(preSubMessageChange);
|
@ -0,0 +1,179 @@
|
|||||||
|
extends /apps/include/layout.pug
|
||||||
|
|
||||||
|
block append head
|
||||||
|
script(src="/js/otnContent.js")
|
||||||
|
|
||||||
|
block content
|
||||||
|
.layui-side.layui-bg-black
|
||||||
|
.layui-side-scroll
|
||||||
|
include /apps/marketing/left.pug
|
||||||
|
#otn-edit-content.layui-body
|
||||||
|
.layui-side-scroll
|
||||||
|
.row
|
||||||
|
input#copyInput(type="hidden")
|
||||||
|
.col-lg-12
|
||||||
|
.ukefu-customer-div.setting-wrapper
|
||||||
|
.box.default-box
|
||||||
|
.box-header
|
||||||
|
h1.site-h1
|
||||||
|
| Messenger OTN 列表
|
||||||
|
blockquote.layui-elem-quote.layui-quote-nm
|
||||||
|
i.layui-icon(style="color:gray") 
|
||||||
|
font(color="#999").layui-word-aux 请勿使用负载字段发送密码、用户凭证、可识别用户身份的信息(即,姓名或邮箱等可单独用于联系用户或识别其身份的信息)或其他敏感信息(如健康状况、财务、支付或持卡人数据,或根据适用法律定义为敏感信息的其他类别的信息
|
||||||
|
.row(style="padding:5px;")
|
||||||
|
h1(style="width: 100%;")
|
||||||
|
span(style="padding: 0 5px;") Messenger
|
||||||
|
select#queryPageId(name='queryPageId')
|
||||||
|
option(value) 请选择渠道...
|
||||||
|
for m in fbMessengers
|
||||||
|
option(value=m.pageId, selected=m.pageId == queryPageId)= m.name
|
||||||
|
span(style="float:right;")
|
||||||
|
button.layui-btn.layui-btn-small.green(href="/apps/messenger/otn/add.html", data-toggle="load", data-target="#otn-edit-content")
|
||||||
|
| 创建OTN
|
||||||
|
.row(style="padding:5px;")
|
||||||
|
.col-lg-12
|
||||||
|
table.layui-table(lay-skin="line", style="table-layout: fixed; word-break: break-all")
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th 名称
|
||||||
|
th Messenger
|
||||||
|
th(style="width:100px") 点击
|
||||||
|
th(style="width:100px") 订阅
|
||||||
|
th(style="width:80px") 创建时间
|
||||||
|
th(style="width:80px") 发送时间
|
||||||
|
th(style="width:60px") 状态
|
||||||
|
th(style="width:60px") 分享链接
|
||||||
|
th(style="width:180px;white-space:nowrap;", nowrap="nowrap") 操作
|
||||||
|
tbody
|
||||||
|
- var state = {'create' :'新建','sending':'发送中','finish':'已发送'}
|
||||||
|
for item in otns.content
|
||||||
|
tr
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
| #{item.name}
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
| #{item.fbMessenger.name}
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
| #{item.melinkNum}
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
| #{item.subNum}
|
||||||
|
td
|
||||||
|
| #{pugHelper.formatDate("yyyy-MM-dd HH:mm:ss", item.createtime)}
|
||||||
|
td
|
||||||
|
| #{item.sendtime ? pugHelper.formatDate("yyyy-MM-dd HH:mm:ss", item.sendtime) : ''}
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
| #{state[item.status]}
|
||||||
|
td(style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
button.layui-btn.layui-btn-small.webhook(type="button", data-id=item.id, data-page=item.pageId) 复制
|
||||||
|
td(title="", style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;")
|
||||||
|
if(item.status == 'create')
|
||||||
|
a(href="/apps/messenger/otn/send.html?id=" + item.id, style="margin-left:10px;", data-toggle="tip", title="请确认是否发送?")
|
||||||
|
i.layui-icon
|
||||||
|
| 发送
|
||||||
|
a(href="/apps/messenger/otn/edit.html?id=" + item.id, data-toggle="load", data-target="#otn-edit-content")
|
||||||
|
i.layui-icon
|
||||||
|
| 编辑
|
||||||
|
a(href="/apps/messenger/otn/delete.html?id=" + item.id, style="margin-left:10px;", data-toggle="tip", title="请确认是否删除?")
|
||||||
|
i.layui-icon(style="color:red;") ဆ
|
||||||
|
| 删除
|
||||||
|
.row(style='padding:5px;')
|
||||||
|
.col-lg-12#page(style='text-align:center;')
|
||||||
|
|
||||||
|
script.
|
||||||
|
layui.use(['laypage', 'layer'], function () {
|
||||||
|
var laypage = layui.laypage,
|
||||||
|
layer = layui.layer;
|
||||||
|
laypage({
|
||||||
|
cont: 'page',
|
||||||
|
pages: #{ otns.totalPages }, //总页数
|
||||||
|
curr: #{ otns.number + 1 },
|
||||||
|
groups: 5, //连续显示分页数
|
||||||
|
jump: function (data, first) {
|
||||||
|
if (!first) {
|
||||||
|
location.href = "/apps/messenger/otn/index.html?p=" + data.curr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
layui.use('layer', function () {
|
||||||
|
var layer = layui.layer;
|
||||||
|
var msg = '#{msg}'
|
||||||
|
if (msg == 'save_ok')
|
||||||
|
layer.msg('OTN创建成功', {icon: 1, time: 1000})
|
||||||
|
else if (msg == 'save_no_PageId')
|
||||||
|
layer.msg('Page ID已存在', {icon: 2, time: 3000})
|
||||||
|
else if (msg == 'send_ok')
|
||||||
|
layer.msg('发送成功', {icon: 1, time: 3000})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
$('.webhook').each(function (i, n) {
|
||||||
|
var id = $(n).data("id");
|
||||||
|
var pageId = $(n).data("page");
|
||||||
|
var webhook = "https://m.me/" + pageId + "?ref=" + id;
|
||||||
|
$(n).on('click', function () {
|
||||||
|
Clipboard.copy(webhook);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#queryPageId').change(function () {
|
||||||
|
location.search = "queryPageId=" + $(this).val();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
window.Clipboard = (function (window, document, navigator) {
|
||||||
|
var textArea,
|
||||||
|
copy;
|
||||||
|
|
||||||
|
// 判断是不是ios端
|
||||||
|
function isOS() {
|
||||||
|
return navigator.userAgent.match(/ipad|iphone/i);
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建文本元素
|
||||||
|
function createTextArea(text) {
|
||||||
|
textArea = document.createElement('textArea');
|
||||||
|
textArea.value = text;
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
//选择内容
|
||||||
|
function selectText() {
|
||||||
|
var range,
|
||||||
|
selection;
|
||||||
|
if (isOS()) {
|
||||||
|
range = document.createRange();
|
||||||
|
range.selectNodeContents(textArea);
|
||||||
|
selection = window.getSelection();
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
textArea.setSelectionRange(0, 999999);
|
||||||
|
} else {
|
||||||
|
textArea.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//复制到剪贴板
|
||||||
|
function copyToClipboard() {
|
||||||
|
try {
|
||||||
|
if (document.execCommand("Copy")) {
|
||||||
|
layer.msg('me link 已复制到剪切板', {icon: 1, time: 1000, offset: 'rt'})
|
||||||
|
} else {
|
||||||
|
layer.msg('me link 复制失败请手动复制', {icon: 2, time: 1000, offset: 'rt'})
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
layer.msg('me link 复制失败请手动复制', {icon: 2, time: 1000, offset: 'rt'})
|
||||||
|
}
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
copy = function (text) {
|
||||||
|
createTextArea(text);
|
||||||
|
selectText();
|
||||||
|
copyToClipboard();
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
copy: copy
|
||||||
|
};
|
||||||
|
})(window, document, navigator);
|
@ -0,0 +1,86 @@
|
|||||||
|
block content
|
||||||
|
include head
|
||||||
|
|
||||||
|
.layui-tab
|
||||||
|
.layui-tab-content
|
||||||
|
.layui-tab-item.layui-show
|
||||||
|
form.layui-form(method='post', action="/admin/messenger/setting/save.html")
|
||||||
|
input(type='hidden' name='id' value=fb.id)
|
||||||
|
input.layui-input(type='checkbox', name='status', lay-skin='switch', lay-text="开启|关闭")
|
||||||
|
.ukefu-customer-div.setting-wrapper
|
||||||
|
.box.default-box
|
||||||
|
.box-header
|
||||||
|
h3.box-title 机器人客服
|
||||||
|
.box-body(style="padding-top:5px;")
|
||||||
|
.row: .col-lg-8
|
||||||
|
.ukefu-webim-prop
|
||||||
|
.ukefu-webim-tl(style="clear:both;") 1、转人工按钮文本
|
||||||
|
.box-item
|
||||||
|
.row
|
||||||
|
.col-lg-8
|
||||||
|
p 默认显示信息:转人工
|
||||||
|
p(style="color:#888888;font-size:13px;margin-top:10px;") 访客在 Messenger 对话窗口,机器人客服转人工客服提示按钮文本
|
||||||
|
.col-lg-4
|
||||||
|
input.layui-input(type='text', name='transferManualService', lay-verify='required', value=(transferManualService ? transferManualService : "转人工"))
|
||||||
|
|
||||||
|
.ukefu-webim-prop
|
||||||
|
.ukefu-webim-tl(style="clear:both;") 2、推荐问题提示文本
|
||||||
|
.box-item
|
||||||
|
.row
|
||||||
|
.col-lg-8
|
||||||
|
p 默认显示信息:您是否想问以下问题
|
||||||
|
p(style="color:#888888;font-size:13px;margin-top:10px;") 机器人客服提示建议问题的文本
|
||||||
|
.col-lg-4
|
||||||
|
input.layui-input(type='text', name='suggestQuestion', lay-verify='required', value=(suggestQuestion ? suggestQuestion : "您是否想问以下问题"))
|
||||||
|
|
||||||
|
|
||||||
|
.ukefu-webim-prop
|
||||||
|
.ukefu-webim-tl(style="clear:both;") 3、询问是否有帮助文本
|
||||||
|
.box-item
|
||||||
|
.row
|
||||||
|
.col-lg-8
|
||||||
|
p 默认显示信息:以上答案是否对您有帮助
|
||||||
|
p(style="color:#888888;font-size:13px;margin-top:10px;") 机器人客服询问回答是否有帮助的文本
|
||||||
|
.col-lg-4
|
||||||
|
input.layui-input(type='text', name='evaluationAsk', lay-verify='required', value=(evaluationAsk ? evaluationAsk : "以上答案是否对您有帮助"))
|
||||||
|
.row: br
|
||||||
|
.row
|
||||||
|
.col-lg-8
|
||||||
|
p 有帮助按钮文本
|
||||||
|
p(style="color:#888888;font-size:13px;margin-top:10px;") 访客得到的上一条机器人回答有帮助
|
||||||
|
.col-lg-4
|
||||||
|
input.layui-input(type='text', name='evaluationYes', lay-verify='required', value=(evaluationYes ? evaluationYes : "是"))
|
||||||
|
.row: br
|
||||||
|
.row
|
||||||
|
.col-lg-8
|
||||||
|
p 有帮助后回复文本
|
||||||
|
p(style="color:#888888;font-size:13px;margin-top:10px;") 访客反馈反馈有帮助后机器人回复,设置为空则不回复
|
||||||
|
.col-lg-4
|
||||||
|
input.layui-input(type='text', name='evaluationYesReply', lay-verify='required', value=(evaluationYesReply ? evaluationYesReply : "感谢您的反馈,我们会做的更好!"))
|
||||||
|
|
||||||
|
.row: br
|
||||||
|
.row
|
||||||
|
.col-lg-8
|
||||||
|
p 无帮助按钮文本
|
||||||
|
p(style="color:#888888;font-size:13px;margin-top:10px;") 访客得到的上一条机器人回答没帮助
|
||||||
|
.col-lg-4
|
||||||
|
input.layui-input(type='text', name='evaluationNo', lay-verify='required', value=(evaluationNo ? evaluationNo : "否"))
|
||||||
|
.row: br
|
||||||
|
.row
|
||||||
|
.col-lg-8
|
||||||
|
p 无帮助后回复文本
|
||||||
|
p(style="color:#888888;font-size:13px;margin-top:10px;") 访客反馈反馈没帮助后机器人回复,设置为空则不回复
|
||||||
|
.col-lg-4
|
||||||
|
input.layui-input(type='text', name='evaluationNoReply', lay-verify='required', value=(evaluationNoReply ? evaluationNoReply : "感谢您的反馈,机器人在不断的学习!"))
|
||||||
|
|
||||||
|
.row
|
||||||
|
.col-lg-3
|
||||||
|
.col-lg-9
|
||||||
|
.layui-form-item
|
||||||
|
.layui-input-block
|
||||||
|
button.layui-btn(lay-submit, lay-filter='formDemo') 保存
|
||||||
|
button.layui-btn.layui-btn-original(type='reset', onclick="location.reload()") 取消
|
||||||
|
script.
|
||||||
|
layui.use('form', function () {
|
||||||
|
var form = layui.form;
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user