@modochats/widget 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/models/conversation.js +1 -1
- package/dist/src/types/app.js +1 -0
- package/package.json +1 -1
- package/.vscode/settings.json +0 -3
- package/.yarn/install-state.gz +0 -0
- package/cdn-dist/README.md +0 -42
- package/cdn-dist/modo-web-component.js +0 -1344
- package/cdn-dist/modo-web-component.min.js +0 -1
- package/cdn-dist/package.json +0 -27
- package/dist/types/src/app.d.ts +0 -30
- package/dist/types/src/app.d.ts.map +0 -1
- package/dist/types/src/constants/index.d.ts +0 -10
- package/dist/types/src/constants/index.d.ts.map +0 -1
- package/dist/types/src/constants/regex.d.ts +0 -3
- package/dist/types/src/constants/regex.d.ts.map +0 -1
- package/dist/types/src/index.d.ts +0 -10
- package/dist/types/src/index.d.ts.map +0 -1
- package/dist/types/src/models/chatbot.d.ts +0 -24
- package/dist/types/src/models/chatbot.d.ts.map +0 -1
- package/dist/types/src/models/conversation.d.ts +0 -23
- package/dist/types/src/models/conversation.d.ts.map +0 -1
- package/dist/types/src/models/customer-data.d.ts +0 -32
- package/dist/types/src/models/customer-data.d.ts.map +0 -1
- package/dist/types/src/models/message-utils.d.ts +0 -13
- package/dist/types/src/models/message-utils.d.ts.map +0 -1
- package/dist/types/src/services/chat/conversation.d.ts +0 -23
- package/dist/types/src/services/chat/conversation.d.ts.map +0 -1
- package/dist/types/src/services/chat/message-utils.d.ts +0 -13
- package/dist/types/src/services/chat/message-utils.d.ts.map +0 -1
- package/dist/types/src/services/chat/model.d.ts +0 -28
- package/dist/types/src/services/chat/model.d.ts.map +0 -1
- package/dist/types/src/services/chatbot/chatbot.d.ts +0 -24
- package/dist/types/src/services/chatbot/chatbot.d.ts.map +0 -1
- package/dist/types/src/services/checker.d.ts +0 -4
- package/dist/types/src/services/checker.d.ts.map +0 -1
- package/dist/types/src/services/listeners/adders.d.ts +0 -4
- package/dist/types/src/services/listeners/adders.d.ts.map +0 -1
- package/dist/types/src/services/listeners/fn.d.ts +0 -4
- package/dist/types/src/services/listeners/fn.d.ts.map +0 -1
- package/dist/types/src/services/socket/utils.d.ts +0 -3
- package/dist/types/src/services/socket/utils.d.ts.map +0 -1
- package/dist/types/src/services/ui/fn.d.ts +0 -14
- package/dist/types/src/services/ui/fn.d.ts.map +0 -1
- package/dist/types/src/services/ui/html.d.ts +0 -4
- package/dist/types/src/services/ui/html.d.ts.map +0 -1
- package/dist/types/src/services/user/customer-data.d.ts +0 -32
- package/dist/types/src/services/user/customer-data.d.ts.map +0 -1
- package/dist/types/src/services/voice-chat/model.d.ts +0 -13
- package/dist/types/src/services/voice-chat/model.d.ts.map +0 -1
- package/dist/types/src/services/voice-chat/utils.d.ts +0 -10
- package/dist/types/src/services/voice-chat/utils.d.ts.map +0 -1
- package/dist/types/src/tools/fetch.d.ts +0 -3
- package/dist/types/src/tools/fetch.d.ts.map +0 -1
- package/dist/types/src/types/app.d.ts +0 -18
- package/dist/types/src/types/app.d.ts.map +0 -1
- package/dist/types/src/types/conversation.d.ts +0 -15
- package/dist/types/src/types/conversation.d.ts.map +0 -1
- package/dist/types/src/types/socket.d.ts +0 -7
- package/dist/types/src/types/socket.d.ts.map +0 -1
- package/dist/types/src/types/window.d.ts +0 -10
- package/dist/types/src/types/window.d.ts.map +0 -1
- package/dist/types/src/utils/audio.d.ts +0 -4
- package/dist/types/src/utils/audio.d.ts.map +0 -1
- package/dist/types/src/utils/browser.d.ts +0 -3
- package/dist/types/src/utils/browser.d.ts.map +0 -1
- package/dist/types/src/utils/fetch.d.ts +0 -19
- package/dist/types/src/utils/fetch.d.ts.map +0 -1
- package/dist/types/src/utils/uuid.d.ts +0 -7
- package/dist/types/src/utils/uuid.d.ts.map +0 -1
- package/rollup.config.js +0 -18
- package/rollup.dev.config.js +0 -22
- package/scripts/create-umd-bundle.js +0 -213
- package/scripts/terser-minify.js +0 -112
- package/src/app.ts +0 -117
- package/src/constants/index.ts +0 -21
- package/src/constants/regex.ts +0 -2
- package/src/index.ts +0 -16
- package/src/services/chat/conversation.ts +0 -135
- package/src/services/chat/message-utils.ts +0 -221
- package/src/services/chat/model.ts +0 -139
- package/src/services/chatbot/chatbot.ts +0 -66
- package/src/services/checker.ts +0 -10
- package/src/services/listeners/adders.ts +0 -178
- package/src/services/listeners/fn.ts +0 -77
- package/src/services/socket/utils.ts +0 -9
- package/src/services/ui/fn.ts +0 -254
- package/src/services/ui/html.ts +0 -192
- package/src/services/user/customer-data.ts +0 -78
- package/src/services/voice-chat/model.ts +0 -79
- package/src/services/voice-chat/utils.ts +0 -137
- package/src/tools/fetch.ts +0 -7
- package/src/types/app.ts +0 -17
- package/src/types/conversation.ts +0 -14
- package/src/types/socket.ts +0 -7
- package/src/types/window.ts +0 -12
- package/src/utils/audio.ts +0 -67
- package/src/utils/browser.ts +0 -4
- package/src/utils/fetch.ts +0 -98
- package/src/utils/uuid.ts +0 -13
- package/temp/audio/new-message.mp3 +0 -0
- package/temp/audio/on-hold.mp3 +0 -0
- package/temp/audio-processor.js +0 -261
- package/temp/css/index.css +0 -2283
- package/temp/dev.html +0 -87
- package/temp/index.html +0 -16
- package/tsconfig.json +0 -119
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import {ConversationMessage} from "@modochats/chat-client";
|
|
2
|
-
import {marked} from "marked";
|
|
3
|
-
|
|
4
|
-
type MessageRelatedUtil<T extends Array<any> = [], R = void> = (message: ConversationMessage, ...args: T) => R;
|
|
5
|
-
|
|
6
|
-
const initMessageElement: MessageRelatedUtil = message => {
|
|
7
|
-
const element = document.createElement("div");
|
|
8
|
-
const widget = window.getMWidget?.();
|
|
9
|
-
element.id = `message-${message.id}`;
|
|
10
|
-
// Format time from createdAt
|
|
11
|
-
const messageTime = new Date(message.createdAt);
|
|
12
|
-
const timeString = messageTime.toLocaleTimeString("fa-IR", {
|
|
13
|
-
hour: "2-digit",
|
|
14
|
-
minute: "2-digit",
|
|
15
|
-
hour12: false
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
// Build the HTML with replied-to message preview if exists
|
|
19
|
-
let repliedToHtml = "";
|
|
20
|
-
if (message.repliedTo) {
|
|
21
|
-
const repliedContent = message.repliedTo.content.length > 40 ? message.repliedTo.content.substring(0, 40) + "..." : message.repliedTo.content;
|
|
22
|
-
repliedToHtml = `
|
|
23
|
-
<div class="mw-replied-to-preview" data-reply-message-id="${message.repliedTo.id}">
|
|
24
|
-
<div class="mw-replied-to-header">
|
|
25
|
-
<span class="mw-replied-to-sender">${message.repliedTo.type === "USER" ? "شما" : "پشتیبان"}</span>
|
|
26
|
-
</div>
|
|
27
|
-
<div class="mw-replied-to-content">${repliedContent}</div>
|
|
28
|
-
</div>
|
|
29
|
-
`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Build file preview HTML if file exists
|
|
33
|
-
let filePreviewHtml = "";
|
|
34
|
-
if (message.fileSrc) {
|
|
35
|
-
const displayFileName = message.fileSrc.length > 20 ? message.fileSrc.substring(0, 17) + "..." : message.fileSrc;
|
|
36
|
-
filePreviewHtml = `
|
|
37
|
-
<a href="${message.fileSrc}" target="_blank" rel="noopener noreferrer" class="mw-file-preview" title="دانلود فایل">
|
|
38
|
-
<div class="mw-file-preview-icon">
|
|
39
|
-
${'<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path fill="currentColor" d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-8-6m4 18H6V4h7v5h5v11z"/></svg>'}
|
|
40
|
-
</div>
|
|
41
|
-
<div class="mw-file-preview-info">
|
|
42
|
-
<div class="mw-file-preview-name">${displayFileName}</div>
|
|
43
|
-
<div class="mw-file-preview-type">${"file"}</div>
|
|
44
|
-
</div>
|
|
45
|
-
</a>
|
|
46
|
-
`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
element.innerHTML = `
|
|
50
|
-
<div class="mw-chat-message ${message.type === "USER" ? "mw-chat-message-user" : "mw-chat-message-supporter"}">
|
|
51
|
-
${repliedToHtml}
|
|
52
|
-
${filePreviewHtml}
|
|
53
|
-
<div class="mw-message-content">${marked.parse(message.content) as string}</div>
|
|
54
|
-
</div>
|
|
55
|
-
<div class="mw-message-footer">
|
|
56
|
-
${
|
|
57
|
-
message.type !== "USER"
|
|
58
|
-
? `
|
|
59
|
-
<div class="mw-message-feedback">
|
|
60
|
-
<button class="mw-feedback-btn mw-feedback-dislike" data-message-id="${message.id}" title="مفید نبود">
|
|
61
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none"><path fill="currentColor" d="m15 14l-.986.164A1 1 0 0 1 15 13zM4 14v1a1 1 0 0 1-1-1zm16.522-2.392l.98-.196zM6 3h11.36v2H6zm12.56 12H15v-2h3.56zm-2.573-1.164l.805 4.835L14.82 19l-.806-4.836zM14.82 21h-.214v-2h.214zm-3.543-1.781l-2.515-3.774l1.664-1.11l2.516 3.774zM7.93 15H4v-2h3.93zM3 14V6h2v8zm17.302-8.588l1.2 6l-1.96.392l-1.2-6zM8.762 15.445A1 1 0 0 0 7.93 15v-2a3 3 0 0 1 2.496 1.336zm8.03 3.226A2 2 0 0 1 14.82 21v-2zM18.56 13a1 1 0 0 0 .981-1.196l1.961-.392A3 3 0 0 1 18.561 15zm-1.2-10a3 3 0 0 1 2.942 2.412l-1.96.392A1 1 0 0 0 17.36 5zm-2.754 18a4 4 0 0 1-3.328-1.781l1.664-1.11a2 2 0 0 0 1.664.891zM6 5a1 1 0 0 0-1 1H3a3 3 0 0 1 3-3z"/><path stroke="currentColor" stroke-width="2" d="M8 14V4"/></g></svg>
|
|
62
|
-
</button>
|
|
63
|
-
<button class="mw-feedback-btn mw-feedback-like" data-message-id="${message.id}" title="مفید بود">
|
|
64
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none"><path fill="currentColor" d="m15 10l-.74-.123a.75.75 0 0 0 .74.873zM4 10v-.75a.75.75 0 0 0-.75.75zm16.522 2.392l.735.147zM6 20.75h11.36v-1.5H6zm12.56-11.5H15v1.5h3.56zm-2.82.873l.806-4.835l-1.48-.247l-.806 4.836zm-.92-6.873h-.214v1.5h.213zm-3.335 1.67L8.97 8.693l1.248.832l2.515-3.773zM7.93 9.25H4v1.5h3.93zM3.25 10v8h1.5v-8zm16.807 8.54l1.2-6l-1.47-.295l-1.2 6zM8.97 8.692a1.25 1.25 0 0 1-1.04.557v1.5c.92 0 1.778-.46 2.288-1.225zm7.576-3.405A1.75 1.75 0 0 0 14.82 3.25v1.5a.25.25 0 0 1 .246.291zm2.014 5.462c.79 0 1.38.722 1.226 1.495l1.471.294A2.75 2.75 0 0 0 18.56 9.25zm-1.2 10a2.75 2.75 0 0 0 2.697-2.21l-1.47-.295a1.25 1.25 0 0 1-1.227 1.005zm-2.754-17.5a3.75 3.75 0 0 0-3.12 1.67l1.247.832a2.25 2.25 0 0 1 1.873-1.002zM6 19.25c-.69 0-1.25-.56-1.25-1.25h-1.5A2.75 2.75 0 0 0 6 20.75z"/><path stroke="currentColor" stroke-width="1.5" d="M8 10v10"/></g></svg>
|
|
65
|
-
</button>
|
|
66
|
-
</div>
|
|
67
|
-
`
|
|
68
|
-
: ""
|
|
69
|
-
}
|
|
70
|
-
<div class="mw-message-time">${timeString}</div>
|
|
71
|
-
</div>
|
|
72
|
-
`;
|
|
73
|
-
|
|
74
|
-
element.className = `mw-message-wrapper ${message.type === "USER" ? "mw-message-wrapper-user" : "mw-message-wrapper-supporter"}`;
|
|
75
|
-
widget?.chat?.conversation.containerElement?.appendChild(element);
|
|
76
|
-
|
|
77
|
-
addMessageElementListeners(message);
|
|
78
|
-
|
|
79
|
-
// Add feedback event listeners for non-user messages
|
|
80
|
-
if (message.type !== "USER") {
|
|
81
|
-
addMessageFeedbackListeners(message);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Add clicked listener for replied-to preview
|
|
85
|
-
if (message.repliedTo) {
|
|
86
|
-
addMessageRepliedToListener(message);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const addMessageElementListeners: MessageRelatedUtil = message => {
|
|
91
|
-
const widget = window.getMWidget?.();
|
|
92
|
-
const element = getMessageElement(message);
|
|
93
|
-
|
|
94
|
-
element?.addEventListener("dblclick", () => {
|
|
95
|
-
widget?.chat.replyMaster.setReply(message);
|
|
96
|
-
});
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const addMessageFeedbackListeners: MessageRelatedUtil = message => {
|
|
100
|
-
const element = getMessageElement(message);
|
|
101
|
-
const likeBtn = element?.querySelector(".mw-feedback-like") as HTMLButtonElement;
|
|
102
|
-
const dislikeBtn = element?.querySelector(".mw-feedback-dislike") as HTMLButtonElement;
|
|
103
|
-
|
|
104
|
-
if (likeBtn) {
|
|
105
|
-
likeBtn.addEventListener("click", () => {
|
|
106
|
-
sendMessageFeedBack(message, true);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (dislikeBtn) {
|
|
111
|
-
dislikeBtn.addEventListener("click", () => {
|
|
112
|
-
sendMessageFeedBack(message, false);
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const sendMessageFeedBack: MessageRelatedUtil<[liked: boolean]> = async (message, liked) => {
|
|
118
|
-
const element = getMessageElement(message);
|
|
119
|
-
if (message.hasFeedback) return; // Prevent multiple feedback submissions
|
|
120
|
-
disableMessageFeedbackButtons(message);
|
|
121
|
-
try {
|
|
122
|
-
await message.sendFeedBack(liked);
|
|
123
|
-
const likeBtn = element?.querySelector(".mw-feedback-like") as HTMLButtonElement;
|
|
124
|
-
const dislikeBtn = element?.querySelector(".mw-feedback-dislike") as HTMLButtonElement;
|
|
125
|
-
|
|
126
|
-
if (liked && likeBtn) {
|
|
127
|
-
likeBtn.classList.add("mw-feedback-active");
|
|
128
|
-
} else if (!liked && dislikeBtn) {
|
|
129
|
-
dislikeBtn.classList.add("mw-feedback-active");
|
|
130
|
-
}
|
|
131
|
-
} catch {
|
|
132
|
-
enableMessageFeedbackButtons(message);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const disableMessageFeedbackButtons: MessageRelatedUtil = message => {
|
|
137
|
-
const element = getMessageElement(message);
|
|
138
|
-
const likeBtn = element?.querySelector(".mw-feedback-like") as HTMLButtonElement;
|
|
139
|
-
const dislikeBtn = element?.querySelector(".mw-feedback-dislike") as HTMLButtonElement;
|
|
140
|
-
|
|
141
|
-
if (likeBtn) {
|
|
142
|
-
likeBtn.disabled = true;
|
|
143
|
-
likeBtn.classList.add("mw-feedback-disabled");
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (dislikeBtn) {
|
|
147
|
-
dislikeBtn.disabled = true;
|
|
148
|
-
dislikeBtn.classList.add("mw-feedback-disabled");
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const addMessageRepliedToListener: MessageRelatedUtil = message => {
|
|
153
|
-
const element = getMessageElement(message);
|
|
154
|
-
|
|
155
|
-
const repliedToEl = getMessageElement(message.repliedTo! || {});
|
|
156
|
-
const repliedToPreview = element?.querySelector(".mw-replied-to-preview") as HTMLDivElement;
|
|
157
|
-
if (repliedToPreview && repliedToEl) {
|
|
158
|
-
repliedToPreview.addEventListener("click", () => {
|
|
159
|
-
// Scroll to the replied message
|
|
160
|
-
repliedToEl?.scrollIntoView({behavior: "smooth", block: "center"});
|
|
161
|
-
|
|
162
|
-
// Add highlight effect
|
|
163
|
-
repliedToEl?.classList.add("mw-message-highlight");
|
|
164
|
-
setTimeout(() => {
|
|
165
|
-
repliedToEl?.classList.remove("mw-message-highlight");
|
|
166
|
-
}, 2000);
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const getMessageElement: MessageRelatedUtil<[], HTMLDivElement | undefined | null> = message => {
|
|
172
|
-
const widget = window.getMWidget?.();
|
|
173
|
-
return widget?.chat.conversation.containerElement?.querySelector(`#message-${message.id}`);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const showMessageTooltip: MessageRelatedUtil = message => {
|
|
177
|
-
const widget = window.getMWidget?.();
|
|
178
|
-
const tooltip = widget?.container?.querySelector(".mw-toggle-tooltip");
|
|
179
|
-
const tooltipText = widget?.container?.querySelector(".mw-toggle-tooltip-text");
|
|
180
|
-
if (tooltip && tooltipText) {
|
|
181
|
-
// Show the tooltip
|
|
182
|
-
tooltip.classList.remove("mw-hidden");
|
|
183
|
-
|
|
184
|
-
// Update tooltip text with message preview
|
|
185
|
-
const preview = message.content.length > 50 ? message.content.substring(0, 50) + "..." : message.content;
|
|
186
|
-
tooltipText.textContent = preview;
|
|
187
|
-
|
|
188
|
-
// Auto-hide after 3 seconds
|
|
189
|
-
setTimeout(() => {
|
|
190
|
-
tooltip.classList.add("mw-hidden");
|
|
191
|
-
}, 3000);
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const enableMessageFeedbackButtons: MessageRelatedUtil = message => {
|
|
196
|
-
const element = getMessageElement(message);
|
|
197
|
-
const likeBtn = element?.querySelector(".mw-feedback-like") as HTMLButtonElement;
|
|
198
|
-
const dislikeBtn = element?.querySelector(".mw-feedback-dislike") as HTMLButtonElement;
|
|
199
|
-
|
|
200
|
-
if (likeBtn) {
|
|
201
|
-
likeBtn.disabled = false;
|
|
202
|
-
likeBtn.classList.remove("mw-feedback-disabled");
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (dislikeBtn) {
|
|
206
|
-
dislikeBtn.disabled = false;
|
|
207
|
-
dislikeBtn.classList.remove("mw-feedback-disabled");
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
export {
|
|
212
|
-
initMessageElement,
|
|
213
|
-
addMessageElementListeners,
|
|
214
|
-
addMessageFeedbackListeners,
|
|
215
|
-
sendMessageFeedBack,
|
|
216
|
-
disableMessageFeedbackButtons,
|
|
217
|
-
addMessageRepliedToListener,
|
|
218
|
-
getMessageElement,
|
|
219
|
-
showMessageTooltip,
|
|
220
|
-
enableMessageFeedbackButtons
|
|
221
|
-
};
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import {Conversation} from "#src/models/conversation.js";
|
|
2
|
-
import {ChatClient, ConversationMessage, EventType} from "@modochats/chat-client";
|
|
3
|
-
import {onSocketConnectionUpdate} from "../socket/utils.js";
|
|
4
|
-
|
|
5
|
-
class Chat {
|
|
6
|
-
private instance?: ChatClient;
|
|
7
|
-
fileMaster: CFileMaster;
|
|
8
|
-
replyMaster: CReplyMaster;
|
|
9
|
-
conversation: Conversation;
|
|
10
|
-
constructor() {
|
|
11
|
-
this.fileMaster = new CFileMaster();
|
|
12
|
-
this.replyMaster = new CReplyMaster();
|
|
13
|
-
this.conversation = new Conversation();
|
|
14
|
-
}
|
|
15
|
-
initInstance() {
|
|
16
|
-
const widget = window.getMWidget?.();
|
|
17
|
-
const savedUUid = localStorage.getItem(`modo-chat:${widget?.chatbot?.uuid}-conversation-uuid`);
|
|
18
|
-
this.instance = new ChatClient({
|
|
19
|
-
chatbotUuid: widget?.chatbot?.uuid as string,
|
|
20
|
-
userData: {uuid: widget?.customerData.uniqueId as string, phoneNumber: widget?.customerData.phoneNumber},
|
|
21
|
-
conversationUUid: savedUUid || undefined
|
|
22
|
-
});
|
|
23
|
-
this.instance.on(EventType.CONVERSATION_SYSTEM_MESSAGE, ev => {
|
|
24
|
-
this.conversation.addSystemMessage(ev.message);
|
|
25
|
-
});
|
|
26
|
-
this.instance.on(EventType.CONVERSATION_MESSAGE, ev => {
|
|
27
|
-
this.conversation.addMessage(ev.message, {incoming: ev.incoming});
|
|
28
|
-
});
|
|
29
|
-
this.instance.on(EventType.SOCKET_CONNECTED, ev => {
|
|
30
|
-
onSocketConnectionUpdate(true);
|
|
31
|
-
});
|
|
32
|
-
this.instance.on(EventType.SOCKET_DISCONNECTED, ev => {
|
|
33
|
-
onSocketConnectionUpdate(false);
|
|
34
|
-
});
|
|
35
|
-
this.instance.on(EventType.CONVERSATION_LOAD, ev => {
|
|
36
|
-
this.conversation.clearContainerEl();
|
|
37
|
-
this.conversation.setStatus();
|
|
38
|
-
this.conversation.onInit();
|
|
39
|
-
|
|
40
|
-
localStorage.setItem(`modo-chat:${widget?.chatbot?.uuid}-conversation-uuid`, this.instance?.conversation?.uuid as string);
|
|
41
|
-
});
|
|
42
|
-
this.instance.on(EventType.CONVERSATION_MESSAGES_CLEAR, () => {
|
|
43
|
-
this.conversation.clearContainerEl();
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
get socket() {
|
|
47
|
-
return this.instance?.socket;
|
|
48
|
-
}
|
|
49
|
-
// conversation data , since the instance only stores and handles data ^^
|
|
50
|
-
get conversationD() {
|
|
51
|
-
return this.instance?.conversation;
|
|
52
|
-
}
|
|
53
|
-
clear() {
|
|
54
|
-
this.conversation.clear();
|
|
55
|
-
this.instance?.clearConversation();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
sendMessage(...args: Parameters<ChatClient["sendMessage"]>) {
|
|
59
|
-
return this.instance?.sendMessage(...args);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
class CFileMaster {
|
|
63
|
-
file?: File;
|
|
64
|
-
|
|
65
|
-
clearFile() {
|
|
66
|
-
this.file = undefined;
|
|
67
|
-
this.toggleUiState();
|
|
68
|
-
}
|
|
69
|
-
setFile(file: File) {
|
|
70
|
-
this.file = file;
|
|
71
|
-
this.toggleUiState();
|
|
72
|
-
}
|
|
73
|
-
toggleUiState() {
|
|
74
|
-
const container = window.getMWidget?.().container;
|
|
75
|
-
const fileUploadBtn = container?.querySelector(".mw-file-upload-btn") as HTMLButtonElement;
|
|
76
|
-
const fileInput = container?.querySelector(".mw-file-input") as HTMLInputElement;
|
|
77
|
-
const uploadIcon = container?.querySelector(".mw-file-upload-icon") as SVGElement;
|
|
78
|
-
const removeIcon = container?.querySelector(".mw-file-remove-icon") as SVGElement;
|
|
79
|
-
|
|
80
|
-
if (this.file) {
|
|
81
|
-
uploadIcon.classList.add("mw-hidden");
|
|
82
|
-
removeIcon.classList.remove("mw-hidden");
|
|
83
|
-
fileUploadBtn.classList.add("mw-file-uploaded");
|
|
84
|
-
} else {
|
|
85
|
-
fileInput.value = "";
|
|
86
|
-
uploadIcon.classList.remove("mw-hidden");
|
|
87
|
-
removeIcon.classList.add("mw-hidden");
|
|
88
|
-
fileUploadBtn.classList.remove("mw-file-uploaded");
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
class CReplyMaster {
|
|
94
|
-
replyingTo?: ConversationMessage;
|
|
95
|
-
|
|
96
|
-
setReply(message: ConversationMessage) {
|
|
97
|
-
this.replyingTo = message;
|
|
98
|
-
this.updateReplyUI();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
clearReply() {
|
|
102
|
-
this.replyingTo = undefined;
|
|
103
|
-
|
|
104
|
-
this.updateReplyUI();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private updateReplyUI() {
|
|
108
|
-
const container = window.getMWidget?.().container;
|
|
109
|
-
const replyPreview = container?.querySelector(".mw-reply-preview") as HTMLDivElement;
|
|
110
|
-
const replyText = container?.querySelector(".mw-reply-preview-text") as HTMLElement;
|
|
111
|
-
const chatMessagesContainer = container?.querySelector(".mw-chat-messages-con") as HTMLDivElement;
|
|
112
|
-
|
|
113
|
-
if (this.replyingTo) {
|
|
114
|
-
// Show reply preview
|
|
115
|
-
if (replyPreview && replyText) {
|
|
116
|
-
// Truncate content to 50 chars
|
|
117
|
-
const content = this.replyingTo.content.length > 50 ? this.replyingTo.content.substring(0, 50) + "..." : this.replyingTo.content;
|
|
118
|
-
replyText.textContent = content;
|
|
119
|
-
replyPreview.classList.remove("mw-hidden");
|
|
120
|
-
|
|
121
|
-
// Add reply active class to messages container
|
|
122
|
-
if (chatMessagesContainer) {
|
|
123
|
-
chatMessagesContainer.classList.add("mw-reply-active");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
// Hide reply preview
|
|
128
|
-
if (replyPreview) {
|
|
129
|
-
replyPreview.classList.add("mw-hidden");
|
|
130
|
-
|
|
131
|
-
// Remove reply active class from messages container
|
|
132
|
-
if (chatMessagesContainer) {
|
|
133
|
-
chatMessagesContainer.classList.remove("mw-reply-active");
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
export {Chat};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
class Chatbot {
|
|
2
|
-
name: string;
|
|
3
|
-
image: string;
|
|
4
|
-
shortDescription: string;
|
|
5
|
-
starters: string[] = [];
|
|
6
|
-
voiceChat: boolean;
|
|
7
|
-
createdAt: string;
|
|
8
|
-
updatedAt: string;
|
|
9
|
-
deletedAt: string | null;
|
|
10
|
-
uuid: string;
|
|
11
|
-
allowedHosts: string[] = [];
|
|
12
|
-
id: number;
|
|
13
|
-
greetingMessage?: string;
|
|
14
|
-
uiConfig: {
|
|
15
|
-
primaryColor: string;
|
|
16
|
-
foregroundColor: string;
|
|
17
|
-
theme: "dark" | "light";
|
|
18
|
-
};
|
|
19
|
-
constructor(data: Record<string, any>) {
|
|
20
|
-
this.name = data.name;
|
|
21
|
-
this.image = data.image;
|
|
22
|
-
this.shortDescription = data.short_description;
|
|
23
|
-
this.starters = data.starters;
|
|
24
|
-
this.voiceChat = data.voice_agent;
|
|
25
|
-
this.createdAt = data.setting.created_at;
|
|
26
|
-
this.updatedAt = data.setting.updated_at;
|
|
27
|
-
this.deletedAt = data.setting.deleted_at;
|
|
28
|
-
this.uuid = data.setting.unique_id;
|
|
29
|
-
this.allowedHosts = data.setting.allow_hosts?.split(",") ?? [];
|
|
30
|
-
this.id = data.setting.chatbot;
|
|
31
|
-
this.allowedHosts.push("modochats.com");
|
|
32
|
-
this.uiConfig = {
|
|
33
|
-
primaryColor: data.primary_color,
|
|
34
|
-
foregroundColor: data.foreground_color,
|
|
35
|
-
theme: data.theme
|
|
36
|
-
};
|
|
37
|
-
this.greetingMessage = data.greeting_message;
|
|
38
|
-
}
|
|
39
|
-
showTooltip() {
|
|
40
|
-
const widget = window.getMWidget?.();
|
|
41
|
-
const tooltip = widget?.container?.querySelector(".mw-toggle-tooltip");
|
|
42
|
-
const tooltipText = widget?.container?.querySelector(".mw-toggle-tooltip-text");
|
|
43
|
-
const hasSeen = localStorage.getItem(`modochats:${widget?.publicKey}-has-seen-greeting-message`) === "true";
|
|
44
|
-
|
|
45
|
-
if (tooltip && tooltipText && this.greetingMessage && !hasSeen) {
|
|
46
|
-
// Show the tooltip
|
|
47
|
-
tooltip.classList.remove("mw-hidden");
|
|
48
|
-
|
|
49
|
-
// Update tooltip text with greeting message
|
|
50
|
-
tooltipText.textContent = this.greetingMessage;
|
|
51
|
-
|
|
52
|
-
// Auto-hide after 5 seconds
|
|
53
|
-
// setTimeout(() => {
|
|
54
|
-
// tooltip.classList.add("mw-hidden");
|
|
55
|
-
// }, 5000);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
hideTooltip() {
|
|
59
|
-
const widget = window.getMWidget?.();
|
|
60
|
-
const tooltip = widget?.container?.querySelector(".mw-toggle-tooltip");
|
|
61
|
-
if (tooltip) {
|
|
62
|
-
tooltip.classList.add("mw-hidden");
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
export {Chatbot};
|
package/src/services/checker.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import {Widget} from "#src/app.js";
|
|
2
|
-
import {parse} from "tldts";
|
|
3
|
-
|
|
4
|
-
const checkIfHostIsAllowed = (widget: Widget) => {
|
|
5
|
-
const currentHost = parse(window.location.origin).hostname;
|
|
6
|
-
const allowedHosts = widget.chatbot?.allowedHosts || [];
|
|
7
|
-
if (currentHost) return allowedHosts.includes(currentHost);
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export {checkIfHostIsAllowed};
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import {getMessageElement} from "#src/services/chat/message-utils.js";
|
|
2
|
-
import {sendMessage, submitPhoneNumberForm} from "./fn.js";
|
|
3
|
-
|
|
4
|
-
const registerListeners = (widgetContainer: HTMLDivElement) => {
|
|
5
|
-
let chatBody = widgetContainer.querySelector(".mw-chat-body");
|
|
6
|
-
const toggleChatBtn = widgetContainer.querySelector(".mw-toggle-chat-btn") as HTMLButtonElement;
|
|
7
|
-
let isBodyOpen = false;
|
|
8
|
-
|
|
9
|
-
// Set footer link URL with origin parameter and version title
|
|
10
|
-
const footerLink = widgetContainer.querySelector(".mw-footer-link") as HTMLAnchorElement;
|
|
11
|
-
if (footerLink) {
|
|
12
|
-
const widget = window.getMWidget?.();
|
|
13
|
-
footerLink.href = `https://modochats.com?utm_source=${encodeURIComponent(window.location.origin)}`;
|
|
14
|
-
footerLink.title = `مودوچت v${widget?.version || "0.1"}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// toggle chat body visibility (only if not in fullscreen mode)
|
|
18
|
-
if (toggleChatBtn) {
|
|
19
|
-
toggleChatBtn.addEventListener(
|
|
20
|
-
"click",
|
|
21
|
-
() => {
|
|
22
|
-
const widget = window.getMWidget?.();
|
|
23
|
-
isBodyOpen = !isBodyOpen;
|
|
24
|
-
if (isBodyOpen) widget?.onOpen();
|
|
25
|
-
else widget?.onClose();
|
|
26
|
-
chatBody?.classList.toggle("mw-hidden");
|
|
27
|
-
toggleChatBtn.classList.toggle("mw-chat-open", isBodyOpen);
|
|
28
|
-
},
|
|
29
|
-
{capture: false}
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
registerSendMessageListener(widgetContainer);
|
|
34
|
-
registerPhoneNumberFormListeners(widgetContainer);
|
|
35
|
-
registerNewConversationListener(widgetContainer);
|
|
36
|
-
registerFileUploadListener(widgetContainer);
|
|
37
|
-
registerReplyPreviewListener(widgetContainer);
|
|
38
|
-
registerTooltipCloseListener(widgetContainer);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const registerSendMessageListener = (widgetContainer: HTMLDivElement) => {
|
|
42
|
-
const chatInput = widgetContainer.querySelector(".mw-chat-input") as HTMLInputElement;
|
|
43
|
-
const sendMessageBtn = widgetContainer.querySelector(".mw-send-message-btn") as HTMLButtonElement;
|
|
44
|
-
|
|
45
|
-
let isDisabled = false;
|
|
46
|
-
function toggleLoading() {
|
|
47
|
-
isDisabled = !isDisabled;
|
|
48
|
-
chatInput.disabled = isDisabled;
|
|
49
|
-
sendMessageBtn.disabled = isDisabled;
|
|
50
|
-
sendMessageBtn.setAttribute("data-is-loading", String(isDisabled));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
chatInput.addEventListener("keydown", e => {
|
|
54
|
-
if (e.key === "Enter") {
|
|
55
|
-
e.preventDefault();
|
|
56
|
-
const message = chatInput.value;
|
|
57
|
-
toggleLoading();
|
|
58
|
-
sendMessage(message)
|
|
59
|
-
.then(() => {
|
|
60
|
-
chatInput.value = "";
|
|
61
|
-
})
|
|
62
|
-
.finally(toggleLoading);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
sendMessageBtn.addEventListener("click", e => {
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
const message = chatInput.value;
|
|
68
|
-
toggleLoading();
|
|
69
|
-
sendMessage(message)
|
|
70
|
-
.then(() => {
|
|
71
|
-
chatInput.value = "";
|
|
72
|
-
})
|
|
73
|
-
.finally(toggleLoading);
|
|
74
|
-
});
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const registerPhoneNumberFormListeners = (widgetContainer: HTMLDivElement) => {
|
|
78
|
-
const formOverlay = widgetContainer.querySelector(".mw-form-overlay") as HTMLDivElement;
|
|
79
|
-
const phoneInput = widgetContainer.querySelector(".mw-phone-input") as HTMLInputElement;
|
|
80
|
-
const formSubmitBtn = widgetContainer.querySelector(".mw-form-submit-btn") as HTMLButtonElement;
|
|
81
|
-
const formCancelBtn = widgetContainer.querySelector(".mw-form-cancel-btn") as HTMLButtonElement;
|
|
82
|
-
formSubmitBtn.addEventListener("click", () => {
|
|
83
|
-
const phoneNumber = phoneInput.value;
|
|
84
|
-
submitPhoneNumberForm(phoneNumber);
|
|
85
|
-
});
|
|
86
|
-
formCancelBtn.addEventListener("click", () => {
|
|
87
|
-
formOverlay.classList.add("mw-hidden");
|
|
88
|
-
});
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const registerNewConversationListener = (widgetContainer: HTMLDivElement) => {
|
|
92
|
-
const newBtn = widgetContainer.querySelector(".mw-new-conversation-btn") as HTMLButtonElement;
|
|
93
|
-
|
|
94
|
-
newBtn.addEventListener("click", () => {
|
|
95
|
-
const widget = window.getMWidget?.();
|
|
96
|
-
if (widget) {
|
|
97
|
-
widget.chat!.clear();
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const registerFileUploadListener = (widgetContainer: HTMLDivElement) => {
|
|
103
|
-
const fileUploadBtn = widgetContainer.querySelector(".mw-file-upload-btn") as HTMLButtonElement;
|
|
104
|
-
const fileInput = widgetContainer.querySelector(".mw-file-input") as HTMLInputElement;
|
|
105
|
-
const widget = window?.getMWidget?.();
|
|
106
|
-
|
|
107
|
-
// Trigger file input when button is clicked
|
|
108
|
-
fileUploadBtn.addEventListener("click", () => {
|
|
109
|
-
if (widget?.chat.fileMaster.file) {
|
|
110
|
-
// If a file is selected, remove it
|
|
111
|
-
widget?.chat.fileMaster.clearFile();
|
|
112
|
-
} else {
|
|
113
|
-
// Otherwise, open file picker
|
|
114
|
-
fileInput.click();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Handle file selection
|
|
119
|
-
fileInput.addEventListener("change", () => {
|
|
120
|
-
if (fileInput.files && fileInput.files.length > 0) {
|
|
121
|
-
widget?.chat.fileMaster.setFile(fileInput.files[0]);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const registerReplyPreviewListener = (widgetContainer: HTMLDivElement) => {
|
|
127
|
-
const replyPreview = widgetContainer.querySelector(".mw-reply-preview") as HTMLDivElement;
|
|
128
|
-
const replyPreviewClose = widgetContainer.querySelector(".mw-reply-preview-close") as HTMLButtonElement;
|
|
129
|
-
const replyPreviewInfo = widgetContainer.querySelector(".mw-reply-preview-info") as HTMLDivElement;
|
|
130
|
-
|
|
131
|
-
if (!replyPreview || !replyPreviewClose || !replyPreviewInfo) return;
|
|
132
|
-
|
|
133
|
-
// Close button - clear reply
|
|
134
|
-
replyPreviewClose.addEventListener("click", e => {
|
|
135
|
-
e.stopPropagation();
|
|
136
|
-
const widget = window.getMWidget?.();
|
|
137
|
-
if (widget?.chat) {
|
|
138
|
-
widget.chat.replyMaster.clearReply();
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Click on preview info - scroll to message
|
|
143
|
-
replyPreviewInfo.addEventListener("click", () => {
|
|
144
|
-
const widget = window.getMWidget?.();
|
|
145
|
-
const replyingToEl = getMessageElement(widget?.chat.replyMaster.replyingTo! || {});
|
|
146
|
-
|
|
147
|
-
if (replyingToEl) {
|
|
148
|
-
// Scroll to the message
|
|
149
|
-
const messagesContainer = widgetContainer.querySelector(".mw-chat-messages-con") as HTMLDivElement;
|
|
150
|
-
if (messagesContainer) {
|
|
151
|
-
replyingToEl.scrollIntoView({behavior: "smooth", block: "center"});
|
|
152
|
-
|
|
153
|
-
// Add a highlight effect
|
|
154
|
-
replyingToEl.classList.add("mw-message-highlight");
|
|
155
|
-
setTimeout(() => {
|
|
156
|
-
replyingToEl?.classList.remove("mw-message-highlight");
|
|
157
|
-
}, 2000);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const registerTooltipCloseListener = (widgetContainer: HTMLDivElement) => {
|
|
164
|
-
const widget = window.getMWidget?.();
|
|
165
|
-
const tooltipCloseBtn = widgetContainer.querySelector(".mw-toggle-tooltip-close") as HTMLButtonElement;
|
|
166
|
-
const tooltip = widgetContainer.querySelector(".mw-toggle-tooltip") as HTMLDivElement;
|
|
167
|
-
if (tooltipCloseBtn) {
|
|
168
|
-
tooltipCloseBtn.addEventListener("click", e => {
|
|
169
|
-
localStorage.setItem(`modochats:${widget?.publicKey}-has-seen-greeting-message`, "true");
|
|
170
|
-
e.stopPropagation();
|
|
171
|
-
if (tooltip) {
|
|
172
|
-
tooltip.classList.add("mw-hidden");
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
export {registerListeners, registerNewConversationListener};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import {PhoneNumberRegex} from "#src/constants/regex.js";
|
|
2
|
-
|
|
3
|
-
const sendMessage = async (message: string) => {
|
|
4
|
-
if (message.trim().length) {
|
|
5
|
-
if (checkIfUserHasPhoneNumber()) {
|
|
6
|
-
const widget = window.getMWidget?.();
|
|
7
|
-
|
|
8
|
-
if (widget) {
|
|
9
|
-
const savedFile = widget.chat.fileMaster.file;
|
|
10
|
-
const savedReply = widget.chat.replyMaster.replyingTo?.id;
|
|
11
|
-
if (widget?.conversation?.d?.uuid) {
|
|
12
|
-
const chatInput = widget.container?.querySelector(".mw-chat-input") as HTMLInputElement;
|
|
13
|
-
if (chatInput) chatInput.value = "";
|
|
14
|
-
}
|
|
15
|
-
widget.chat.fileMaster.clearFile();
|
|
16
|
-
widget.chat.replyMaster.clearReply();
|
|
17
|
-
await widget?.chat.sendMessage(message, {file: savedFile, replyTo: savedReply});
|
|
18
|
-
} else {
|
|
19
|
-
console.error("Widget instance not found");
|
|
20
|
-
}
|
|
21
|
-
} else {
|
|
22
|
-
throw new Error("User has not submitted the phone number form");
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const checkIfUserHasPhoneNumber = () => {
|
|
28
|
-
const widget = window.getMWidget?.();
|
|
29
|
-
if (widget?.customerData?.hasSubmittedPhoneForm()) {
|
|
30
|
-
// User has already submitted the phone number form (whether empty or with phone number)
|
|
31
|
-
return true;
|
|
32
|
-
} else {
|
|
33
|
-
// Show phone number form
|
|
34
|
-
switchToPhoneNumberFormView();
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const switchToPhoneNumberFormView = () => {
|
|
40
|
-
const formOverlay = window.getMWidget?.().container?.querySelector(".mw-form-overlay");
|
|
41
|
-
if (formOverlay) {
|
|
42
|
-
formOverlay.classList.remove("mw-hidden");
|
|
43
|
-
formOverlay.classList.add("mw-active");
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
const submitPhoneNumberForm = (phoneNumber: string) => {
|
|
47
|
-
// Allow empty phone number or valid phone number
|
|
48
|
-
const parsedPhoneNumber = phoneNumber.replace(/[۰-۹٠-٩]/g, ch => {
|
|
49
|
-
const fa = "۰۱۲۳۴۵۶۷۸۹".indexOf(ch);
|
|
50
|
-
if (fa > -1) return String(fa);
|
|
51
|
-
const ar = "٠١٢٣٤٥٦٧٨٩".indexOf(ch);
|
|
52
|
-
if (ar > -1) return String(ar);
|
|
53
|
-
return ch;
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (parsedPhoneNumber.trim() === "" || PhoneNumberRegex.test(parsedPhoneNumber)) {
|
|
57
|
-
const widget = window.getMWidget?.();
|
|
58
|
-
if (widget) {
|
|
59
|
-
// Update the phone number
|
|
60
|
-
widget.customerData.savePhoneNumber(phoneNumber.trim() || undefined);
|
|
61
|
-
|
|
62
|
-
const formOverlay = widget.container?.querySelector(".mw-form-overlay");
|
|
63
|
-
if (formOverlay) {
|
|
64
|
-
formOverlay.classList.remove("mw-active");
|
|
65
|
-
formOverlay.classList.add("mw-hidden");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
(widget.container?.querySelector(".mw-send-message-btn") as HTMLButtonElement)?.click();
|
|
69
|
-
} else {
|
|
70
|
-
console.error("Widget instance not found");
|
|
71
|
-
}
|
|
72
|
-
} else {
|
|
73
|
-
alert("لطفا شماره تلفن معتبر وارد کنید یا فیلد را خالی بگذارید.");
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export {sendMessage, submitPhoneNumberForm};
|