最初版本
This commit is contained in:
@@ -0,0 +1,456 @@
|
||||
{% if chatwoot_config.enabled %}
|
||||
<script>
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* 會員身份由 Flask Session 注入,identifier 直接使用 username_clean。
|
||||
* 不再接受 ?identifier=... 等網址參數,避免使用者冒充其他會員。
|
||||
*/
|
||||
const CHATWOOT_CONFIG = {{ chatwoot_config | tojson }};
|
||||
const CHATWOOT_USER = {{ chatwoot_user | tojson }};
|
||||
|
||||
window.chatwootSettings = {
|
||||
hideMessageBubble: false,
|
||||
showUnreadMessagesDialog: false,
|
||||
position: "right",
|
||||
locale: "zh_TW",
|
||||
useBrowserLanguage: false,
|
||||
type: "standard",
|
||||
darkMode: "dark",
|
||||
launcherTitle: ""
|
||||
};
|
||||
|
||||
function debugLog() {
|
||||
if (!CHATWOOT_CONFIG.debug) {
|
||||
return;
|
||||
}
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
|
||||
function configureChatwootIdentity() {
|
||||
if (!window.$chatwoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CHATWOOT_USER.is_authenticated) {
|
||||
if (typeof window.$chatwoot.setCustomAttributes === "function") {
|
||||
window.$chatwoot.setCustomAttributes({
|
||||
visitor_type: "guest"
|
||||
});
|
||||
}
|
||||
debugLog("Chatwoot anonymous guest ready");
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof window.$chatwoot.setUser !== "function" ||
|
||||
!CHATWOOT_USER.identifier
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userPayload = {
|
||||
name: CHATWOOT_USER.name || CHATWOOT_USER.username
|
||||
};
|
||||
|
||||
if (CHATWOOT_USER.identifier_hash) {
|
||||
userPayload.identifier_hash = CHATWOOT_USER.identifier_hash;
|
||||
}
|
||||
|
||||
window.$chatwoot.setUser(
|
||||
CHATWOOT_USER.identifier,
|
||||
userPayload
|
||||
);
|
||||
|
||||
if (typeof window.$chatwoot.setCustomAttributes === "function") {
|
||||
window.$chatwoot.setCustomAttributes({
|
||||
visitor_type: "member",
|
||||
username: CHATWOOT_USER.username || "",
|
||||
member_uid: CHATWOOT_USER.uid == null
|
||||
? ""
|
||||
: String(CHATWOOT_USER.uid),
|
||||
account_status: CHATWOOT_USER.account_status || "",
|
||||
locked_reason: CHATWOOT_USER.locked_reason || "",
|
||||
member_type: CHATWOOT_USER.member_type || "",
|
||||
vip_level: CHATWOOT_USER.vip_level || "",
|
||||
tier: CHATWOOT_USER.tier || ""
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Flask 登出前清除 Chatwoot Session,避免下一個登入會員
|
||||
* 沿用上一位會員的 Chatwoot Cookie 與對話。
|
||||
*/
|
||||
window.resetChatwootBeforeLogout = function (form) {
|
||||
if (!form) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 防止使用者重複點擊登出,造成多次提交。
|
||||
*/
|
||||
if (form.dataset.submitting === "true") {
|
||||
return false;
|
||||
}
|
||||
|
||||
form.dataset.submitting = "true";
|
||||
|
||||
const submitLogout = function () {
|
||||
form.submit();
|
||||
};
|
||||
|
||||
if (
|
||||
window.$chatwoot &&
|
||||
typeof window.$chatwoot.reset === "function"
|
||||
) {
|
||||
try {
|
||||
window.$chatwoot.reset();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Chatwoot reset before logout failed:",
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Chatwoot reset 沒有 Promise 回傳,因此提供短暫緩衝時間。
|
||||
* 即使前端清理未完成,後端的一次性 guest reset
|
||||
* 仍會在登出後首次載入匿名頁面時再次執行。
|
||||
*/
|
||||
window.setTimeout(
|
||||
submitLogout,
|
||||
300
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
submitLogout();
|
||||
return false;
|
||||
};
|
||||
|
||||
window.openGuestChatNewConversation = function () {
|
||||
createGuestConversationAndShow({ openAfter: true });
|
||||
};
|
||||
|
||||
window.exitGuestChatToStartSession = function () {
|
||||
// 清掉對話,回到 Chatwoot「开始会话」首頁(不預建新對話)
|
||||
window.__guestWidgetOpen = true;
|
||||
try {
|
||||
if (window.$chatwoot && typeof window.$chatwoot.reset === "function") {
|
||||
window.$chatwoot.reset();
|
||||
}
|
||||
} catch (error) {}
|
||||
document.querySelectorAll("iframe[src*='widget']").forEach(function (iframe) {
|
||||
var url = new URL(iframe.src, window.location.origin);
|
||||
url.searchParams.delete("cw_conversation");
|
||||
url.hash = "/";
|
||||
iframe.src = url.toString();
|
||||
});
|
||||
window.setTimeout(function () {
|
||||
if (window.$chatwoot && typeof window.$chatwoot.toggle === "function") {
|
||||
try {
|
||||
window.$chatwoot.toggle("open");
|
||||
} catch (error) {}
|
||||
}
|
||||
}, 400);
|
||||
};
|
||||
|
||||
// 相容舊名稱:退出圖示改為回「开始会话」,不再強制開新單
|
||||
window.exitGuestChatAndStartNew = window.exitGuestChatToStartSession;
|
||||
|
||||
function createGuestConversationAndShow(options) {
|
||||
var opts = options || {};
|
||||
var openAfter = opts.openAfter !== false;
|
||||
var delayMs = typeof opts.delayMs === "number" ? opts.delayMs : 700;
|
||||
|
||||
if (!CHATWOOT_CONFIG.guest_new_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(CHATWOOT_CONFIG.guest_new_path, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"X-CSRF-Token": CHATWOOT_CONFIG.csrf_token || ""
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
return response.json().then(function (data) {
|
||||
if (!response.ok) {
|
||||
throw new Error((data && data.message) || "建立游客对话失败");
|
||||
}
|
||||
return data;
|
||||
});
|
||||
})
|
||||
.then(function (data) {
|
||||
if (!data || !data.widget_token) {
|
||||
throw new Error("Chatwoot 未回傳 widget_token");
|
||||
}
|
||||
|
||||
document.querySelectorAll("iframe[src*='widget']").forEach(
|
||||
function (iframe) {
|
||||
const url = new URL(iframe.src, window.location.origin);
|
||||
url.searchParams.set("cw_conversation", data.widget_token);
|
||||
url.hash = "/messages";
|
||||
iframe.src = url.toString();
|
||||
}
|
||||
);
|
||||
|
||||
if (!openAfter) {
|
||||
window.__guestWidgetOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
window.__guestWidgetOpen = true;
|
||||
window.setTimeout(function () {
|
||||
if (
|
||||
window.$chatwoot &&
|
||||
typeof window.$chatwoot.toggle === "function"
|
||||
) {
|
||||
window.$chatwoot.toggle("open");
|
||||
}
|
||||
}, delayMs);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("Chatwoot guest open failed:", error);
|
||||
window.alert("客服暂时无法开启,请稍后再试。");
|
||||
});
|
||||
};
|
||||
|
||||
function isGuestChatOpen() {
|
||||
var holder = document.querySelector(".woot-widget-holder");
|
||||
if (holder && !holder.classList.contains("woot--hide")) {
|
||||
var style = window.getComputedStyle(holder);
|
||||
var rect = holder.getBoundingClientRect();
|
||||
if (
|
||||
style.display !== "none" &&
|
||||
style.visibility !== "hidden" &&
|
||||
Number(style.opacity || "1") > 0 &&
|
||||
rect.width > 0 &&
|
||||
rect.height > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return !!window.__guestWidgetOpen;
|
||||
}
|
||||
|
||||
function openGuestChatResume() {
|
||||
window.__guestWidgetOpen = true;
|
||||
if (window.$chatwoot && typeof window.$chatwoot.toggle === "function") {
|
||||
try {
|
||||
window.$chatwoot.toggle("open");
|
||||
return;
|
||||
} catch (error) {}
|
||||
}
|
||||
var holder = document.querySelector(".woot-widget-holder");
|
||||
if (holder) {
|
||||
holder.classList.remove("woot--hide");
|
||||
}
|
||||
}
|
||||
|
||||
function closeGuestChat() {
|
||||
window.__guestWidgetOpen = false;
|
||||
if (window.$chatwoot && typeof window.$chatwoot.toggle === "function") {
|
||||
try {
|
||||
window.$chatwoot.toggle("close");
|
||||
return;
|
||||
} catch (error) {}
|
||||
}
|
||||
var holder = document.querySelector(".woot-widget-holder");
|
||||
if (holder) {
|
||||
holder.classList.add("woot--hide");
|
||||
}
|
||||
}
|
||||
|
||||
function bindGuestBubbleToggle() {
|
||||
if (!CHATWOOT_CONFIG.guest_new_path || window.__guestBubbleBound) {
|
||||
return;
|
||||
}
|
||||
window.__guestBubbleBound = true;
|
||||
window.addEventListener("chatwoot:opened", function () {
|
||||
window.__guestWidgetOpen = true;
|
||||
});
|
||||
window.addEventListener("chatwoot:closed", function () {
|
||||
window.__guestWidgetOpen = false;
|
||||
});
|
||||
document.addEventListener(
|
||||
"click",
|
||||
function (event) {
|
||||
var target = event.target;
|
||||
if (!target || typeof target.closest !== "function") {
|
||||
return;
|
||||
}
|
||||
var bubble = target.closest(
|
||||
".woot-widget-bubble, .woot--bubble-holder, button.woot-widget-bubble"
|
||||
);
|
||||
if (!bubble) {
|
||||
return;
|
||||
}
|
||||
// 右下角氣泡:只收起/重開同一個對話,绝不開新單。
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (isGuestChatOpen()) {
|
||||
closeGuestChat();
|
||||
} else {
|
||||
openGuestChatResume();
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
window.addEventListener("message", function (event) {
|
||||
var raw = event && event.data;
|
||||
if (typeof raw !== "string" || raw.indexOf("chatwoot-widget:") !== 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var payload = JSON.parse(raw.replace("chatwoot-widget:", ""));
|
||||
if (
|
||||
payload &&
|
||||
(payload.event === "guestExitToStartSession" ||
|
||||
payload.event === "guestExitNewChat")
|
||||
) {
|
||||
if (typeof window.exitGuestChatToStartSession === "function") {
|
||||
window.exitGuestChatToStartSession();
|
||||
} else if (typeof window.exitGuestChatAndStartNew === "function") {
|
||||
window.exitGuestChatAndStartNew();
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
});
|
||||
|
||||
|
||||
window.addEventListener(
|
||||
"chatwoot:ready",
|
||||
function () {
|
||||
debugLog("chatwoot:ready", window.$chatwoot);
|
||||
|
||||
const guestResetRequired =
|
||||
{{ chatwoot_guest_reset_required | tojson }};
|
||||
|
||||
/*
|
||||
* 只有「剛完成會員登出」且目前未登入時,才清除舊會員身份。
|
||||
* 一般遊客頁面不會每次都 reset,因此不會破壞既有匿名對話。
|
||||
*/
|
||||
if (
|
||||
!CHATWOOT_USER.is_authenticated &&
|
||||
guestResetRequired &&
|
||||
window.$chatwoot &&
|
||||
typeof window.$chatwoot.reset === "function"
|
||||
) {
|
||||
try {
|
||||
window.$chatwoot.reset();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Chatwoot guest reset failed:",
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* reset 後稍候再設定 visitor_type=guest。
|
||||
* 不執行 setUser(),因此不會向 Chatwoot 傳遞用戶名。
|
||||
*/
|
||||
window.setTimeout(
|
||||
configureChatwootIdentity,
|
||||
300
|
||||
);
|
||||
bindGuestBubbleToggle();
|
||||
return;
|
||||
}
|
||||
|
||||
configureChatwootIdentity();
|
||||
bindGuestBubbleToggle();
|
||||
}
|
||||
);
|
||||
|
||||
window.addEventListener(
|
||||
"chatwoot:error",
|
||||
function (event) {
|
||||
console.error(
|
||||
"chatwoot:error",
|
||||
event && event.detail ? event.detail : event
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if (CHATWOOT_CONFIG.debug) {
|
||||
window.addEventListener(
|
||||
"chatwoot:on-message",
|
||||
function (event) {
|
||||
debugLog("chatwoot:on-message", event.detail);
|
||||
}
|
||||
);
|
||||
|
||||
window.addEventListener(
|
||||
"chatwoot:postback",
|
||||
function (event) {
|
||||
debugLog("chatwoot:postback", event.detail);
|
||||
}
|
||||
);
|
||||
|
||||
window.addEventListener(
|
||||
"chatwoot:opened",
|
||||
function () {
|
||||
debugLog("chatwoot:opened");
|
||||
}
|
||||
);
|
||||
|
||||
window.addEventListener(
|
||||
"chatwoot:closed",
|
||||
function () {
|
||||
debugLog("chatwoot:closed");
|
||||
}
|
||||
);
|
||||
|
||||
window.addEventListener(
|
||||
"chatwoot:on-start-conversation",
|
||||
function (event) {
|
||||
debugLog(
|
||||
"chatwoot:on-start-conversation",
|
||||
event.detail
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const script = document.createElement("script");
|
||||
const firstScript = document.getElementsByTagName("script")[0];
|
||||
|
||||
script.src = (
|
||||
CHATWOOT_CONFIG.base_url +
|
||||
"/packs/js/sdk.js"
|
||||
);
|
||||
script.async = true;
|
||||
|
||||
script.onload = function () {
|
||||
if (!window.chatwootSDK) {
|
||||
console.error("Chatwoot SDK object not found");
|
||||
return;
|
||||
}
|
||||
|
||||
window.chatwootSDK.run({
|
||||
websiteToken: CHATWOOT_CONFIG.website_token,
|
||||
baseUrl: CHATWOOT_CONFIG.base_url
|
||||
});
|
||||
};
|
||||
|
||||
script.onerror = function () {
|
||||
console.error(
|
||||
"Chatwoot SDK load failed:",
|
||||
script.src
|
||||
);
|
||||
};
|
||||
|
||||
firstScript.parentNode.insertBefore(
|
||||
script,
|
||||
firstScript
|
||||
);
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
Reference in New Issue
Block a user