added trigger for chat sms
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
import useAxios from '@core/utils/useAxios'
|
||||||
|
|
||||||
|
/** اعلام تمرکز ادمین روی اتاق پشتیبانی (برای جلوگیری از SMS وقتی ادمین همان چت را میبیند) */
|
||||||
|
export const postAdminChatFocus = async (payload: {
|
||||||
|
room_id?: string | null
|
||||||
|
previous_room_id?: string | null
|
||||||
|
}) =>
|
||||||
|
await useAxios.post('/admin/chat/focus', {
|
||||||
|
room_id: payload.room_id || '',
|
||||||
|
previous_room_id: payload.previous_room_id || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
export const postAdminChatUnfocus = async (room_id: string) =>
|
||||||
|
await useAxios.post('/admin/chat/unfocus', { room_id })
|
||||||
|
|
||||||
|
export const postAdminChatEnsureWebhook = async () =>
|
||||||
|
await useAxios.post('/admin/chat/ensure-webhook')
|
||||||
@@ -317,6 +317,7 @@ const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
|
|||||||
sms_notif_fund_withdraw_customer: 'switch',
|
sms_notif_fund_withdraw_customer: 'switch',
|
||||||
sms_notif_price_trigger: 'switch',
|
sms_notif_price_trigger: 'switch',
|
||||||
sms_notif_wallet_alert: 'switch',
|
sms_notif_wallet_alert: 'switch',
|
||||||
|
sms_notif_support_admin: 'switch',
|
||||||
group_sms: 'switch',
|
group_sms: 'switch',
|
||||||
montzar_closed: 'switch',
|
montzar_closed: 'switch',
|
||||||
montzar_open_buy: 'switch',
|
montzar_open_buy: 'switch',
|
||||||
@@ -798,6 +799,8 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
|||||||
'روشن: وقتی هشدار قیمت مشتری (اقدام=پیامک) فعال شود، پیامک به مشتری میرود. خاموش: هشدار قیمت پیامک نمیفرستد.',
|
'روشن: وقتی هشدار قیمت مشتری (اقدام=پیامک) فعال شود، پیامک به مشتری میرود. خاموش: هشدار قیمت پیامک نمیفرستد.',
|
||||||
sms_notif_wallet_alert:
|
sms_notif_wallet_alert:
|
||||||
'روشن: وقتی اعتبار پنل پیامک صفر یا منفی شود، هشدار به سوپرادمین و ادمینها میرود. خاموش: این هشدار نمیرود.',
|
'روشن: وقتی اعتبار پنل پیامک صفر یا منفی شود، هشدار به سوپرادمین و ادمینها میرود. خاموش: این هشدار نمیرود.',
|
||||||
|
sms_notif_support_admin:
|
||||||
|
'روشن: وقتی مشتری اولین پیام خواندهنشده را در چت پشتیبانی میفرستد و ادمین همان گفتگو را باز و visible ندارد، پیامک به ادمینهای دارای «ارسال پیامک» میرود. خاموش: اعلان پیامک چت نمیرود.',
|
||||||
group_sms:
|
group_sms:
|
||||||
'روشن: امکان ارسال پیامک گروهی از پنل ادمین فعال است. خاموش: ارسال پیامک گروهی غیرفعال است.',
|
'روشن: امکان ارسال پیامک گروهی از پنل ادمین فعال است. خاموش: ارسال پیامک گروهی غیرفعال است.',
|
||||||
profit_gold_api_enabled:
|
profit_gold_api_enabled:
|
||||||
@@ -977,6 +980,7 @@ export const KEY_LABEL_FALLBACK: Record<string, string> = {
|
|||||||
pargar_item_id_rial: 'کد کالای ریال پرگار',
|
pargar_item_id_rial: 'کد کالای ریال پرگار',
|
||||||
pargar_item_id_gold: 'کد کالای طلا پرگار',
|
pargar_item_id_gold: 'کد کالای طلا پرگار',
|
||||||
sms_notif_wallet_alert: 'پیامک هشدار اعتبار پنل پیامک',
|
sms_notif_wallet_alert: 'پیامک هشدار اعتبار پنل پیامک',
|
||||||
|
sms_notif_support_admin: 'پیامک پیام پشتیبانی به ادمین',
|
||||||
auto_active_customer: 'فعالسازی خودکار پس از ثبتنام',
|
auto_active_customer: 'فعالسازی خودکار پس از ثبتنام',
|
||||||
balance_report_exclude_customer_id: 'حذف مشتری از گزارش تراز',
|
balance_report_exclude_customer_id: 'حذف مشتری از گزارش تراز',
|
||||||
chart_use_local_data: 'نمودار از داده داخلی',
|
chart_use_local_data: 'نمودار از داده داخلی',
|
||||||
@@ -1097,7 +1101,7 @@ export const SETTING_EDITABLE_KEYS = new Set([
|
|||||||
'sms_notif_gateway_charge_admin', 'sms_notif_register_customer', 'sms_notif_register_admin',
|
'sms_notif_gateway_charge_admin', 'sms_notif_register_customer', 'sms_notif_register_admin',
|
||||||
'sms_notif_jibit_admin', 'sms_notif_bank_card_admin', 'sms_notif_bank_card_customer',
|
'sms_notif_jibit_admin', 'sms_notif_bank_card_admin', 'sms_notif_bank_card_customer',
|
||||||
'sms_notif_fund_withdraw_admin', 'sms_notif_fund_withdraw_customer',
|
'sms_notif_fund_withdraw_admin', 'sms_notif_fund_withdraw_customer',
|
||||||
'sms_notif_price_trigger', 'sms_notif_wallet_alert',
|
'sms_notif_price_trigger', 'sms_notif_wallet_alert', 'sms_notif_support_admin',
|
||||||
'deposit_sms_support_number', 'deposit_support_message_text',
|
'deposit_sms_support_number', 'deposit_support_message_text',
|
||||||
'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
|
'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
|
||||||
'endDay',
|
'endDay',
|
||||||
|
|||||||
+83
-1
@@ -2,6 +2,7 @@ import { computed, ref, watch } from 'vue'
|
|||||||
import { useWebSocket } from '@vueuse/core'
|
import { useWebSocket } from '@vueuse/core'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
import { createUuid } from '@/utils/index'
|
import { createUuid } from '@/utils/index'
|
||||||
|
import { postAdminChatFocus, postAdminChatUnfocus, postAdminChatEnsureWebhook } from '@/api/chatFocus'
|
||||||
|
|
||||||
type RawChatRoom = Record<string, any>
|
type RawChatRoom = Record<string, any>
|
||||||
type RawChatMessage = Record<string, any>
|
type RawChatMessage = Record<string, any>
|
||||||
@@ -29,6 +30,7 @@ const MAX_RESEND_ATTEMPTS = 1
|
|||||||
const CONNECTING_TIMEOUT_MS = 12000
|
const CONNECTING_TIMEOUT_MS = 12000
|
||||||
const RECONNECT_WATCH_INTERVAL_MS = 5000
|
const RECONNECT_WATCH_INTERVAL_MS = 5000
|
||||||
const READ_SIGNAL_DEBOUNCE_MS = 1200
|
const READ_SIGNAL_DEBOUNCE_MS = 1200
|
||||||
|
const FOCUS_HEARTBEAT_MS = 20000
|
||||||
|
|
||||||
type AdminOutgoingMessageType = 'text' | 'image' | 'voice'
|
type AdminOutgoingMessageType = 'text' | 'image' | 'voice'
|
||||||
|
|
||||||
@@ -70,6 +72,8 @@ const lastReadSignalAt = ref(0)
|
|||||||
const lastReadSignalRoomId = ref('')
|
const lastReadSignalRoomId = ref('')
|
||||||
let readSignalTimer: number | null = null
|
let readSignalTimer: number | null = null
|
||||||
let pendingAdminReadSignalRoomId = ''
|
let pendingAdminReadSignalRoomId = ''
|
||||||
|
let focusHeartbeatTimer: number | null = null
|
||||||
|
let lastReportedFocusRoomId = ''
|
||||||
|
|
||||||
let bootstrapPromise: Promise<void> | null = null
|
let bootstrapPromise: Promise<void> | null = null
|
||||||
|
|
||||||
@@ -132,7 +136,12 @@ export const setAdminSupportPageOpen = (value: boolean) => {
|
|||||||
isAdminActiveRoomAtBottom.value = false
|
isAdminActiveRoomAtBottom.value = false
|
||||||
clearReadSignalTimer()
|
clearReadSignalTimer()
|
||||||
pendingAdminReadSignalRoomId = ''
|
pendingAdminReadSignalRoomId = ''
|
||||||
|
const previousRoom = activeRoomId.value || lastReportedFocusRoomId
|
||||||
activeRoomId.value = ''
|
activeRoomId.value = ''
|
||||||
|
stopFocusHeartbeat()
|
||||||
|
if (previousRoom) {
|
||||||
|
void syncAdminChatFocus(null, previousRoom)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +155,8 @@ export const setAdminSupportPageOpen = (value: boolean) => {
|
|||||||
scheduleAdminReadSignal(80)
|
scheduleAdminReadSignal(80)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshFocusHeartbeat()
|
||||||
|
|
||||||
if (value && !manuallyClosed && status.value !== 'OPEN') {
|
if (value && !manuallyClosed && status.value !== 'OPEN') {
|
||||||
safeReconnect('admin support page opened').catch((error) => {
|
safeReconnect('admin support page opened').catch((error) => {
|
||||||
console.error('admin reconnect on page open failed:', error)
|
console.error('admin reconnect on page open failed:', error)
|
||||||
@@ -164,6 +175,63 @@ const isViewingRoomNow = (roomId: string) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const syncAdminChatFocus = async (roomId: string | null, previousRoomId?: string) => {
|
||||||
|
try {
|
||||||
|
if (!roomId) {
|
||||||
|
if (previousRoomId || lastReportedFocusRoomId) {
|
||||||
|
await postAdminChatUnfocus(previousRoomId || lastReportedFocusRoomId)
|
||||||
|
}
|
||||||
|
lastReportedFocusRoomId = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await postAdminChatFocus({
|
||||||
|
room_id: roomId,
|
||||||
|
previous_room_id: previousRoomId && previousRoomId !== roomId ? previousRoomId : undefined
|
||||||
|
})
|
||||||
|
lastReportedFocusRoomId = roomId
|
||||||
|
} catch (error) {
|
||||||
|
console.error('admin chat focus sync failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopFocusHeartbeat = () => {
|
||||||
|
if (focusHeartbeatTimer !== null) {
|
||||||
|
window.clearInterval(focusHeartbeatTimer)
|
||||||
|
focusHeartbeatTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshFocusHeartbeat = () => {
|
||||||
|
stopFocusHeartbeat()
|
||||||
|
|
||||||
|
const roomId = activeRoomId.value
|
||||||
|
if (!isViewingRoomNow(roomId)) {
|
||||||
|
if (lastReportedFocusRoomId) {
|
||||||
|
void syncAdminChatFocus(null, lastReportedFocusRoomId)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
void syncAdminChatFocus(
|
||||||
|
roomId,
|
||||||
|
lastReportedFocusRoomId && lastReportedFocusRoomId !== roomId
|
||||||
|
? lastReportedFocusRoomId
|
||||||
|
: undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
focusHeartbeatTimer = window.setInterval(() => {
|
||||||
|
if (!isViewingRoomNow(activeRoomId.value)) {
|
||||||
|
stopFocusHeartbeat()
|
||||||
|
if (lastReportedFocusRoomId) {
|
||||||
|
void syncAdminChatFocus(null, lastReportedFocusRoomId)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
void syncAdminChatFocus(activeRoomId.value)
|
||||||
|
}, FOCUS_HEARTBEAT_MS)
|
||||||
|
}
|
||||||
|
|
||||||
const canTreatAdminRoomAsRead = (roomId = activeRoomId.value) => {
|
const canTreatAdminRoomAsRead = (roomId = activeRoomId.value) => {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
roomId &&
|
roomId &&
|
||||||
@@ -1032,7 +1100,13 @@ const bindNetworkListeners = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', () => {
|
document.addEventListener('visibilitychange', () => {
|
||||||
if (document.visibilityState !== 'visible') return
|
if (document.visibilityState !== 'visible') {
|
||||||
|
stopFocusHeartbeat()
|
||||||
|
if (lastReportedFocusRoomId) {
|
||||||
|
void syncAdminChatFocus(null, lastReportedFocusRoomId)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (isAdminSupportPageOpen.value && activeRoomId.value) {
|
if (isAdminSupportPageOpen.value && activeRoomId.value) {
|
||||||
if (canTreatAdminRoomAsRead(activeRoomId.value)) {
|
if (canTreatAdminRoomAsRead(activeRoomId.value)) {
|
||||||
@@ -1041,6 +1115,8 @@ const bindNetworkListeners = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshFocusHeartbeat()
|
||||||
|
|
||||||
if (!shouldKeepAdminSocketAlive()) return
|
if (!shouldKeepAdminSocketAlive()) return
|
||||||
|
|
||||||
if (status.value !== 'OPEN') {
|
if (status.value !== 'OPEN') {
|
||||||
@@ -1435,6 +1511,8 @@ export const setAdminActiveRoom = async (
|
|||||||
pendingAdminReadSignalRoomId = ''
|
pendingAdminReadSignalRoomId = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshFocusHeartbeat()
|
||||||
|
|
||||||
if (isViewingRoomNow(roomId)) {
|
if (isViewingRoomNow(roomId)) {
|
||||||
clearRoomUnread(roomId)
|
clearRoomUnread(roomId)
|
||||||
}
|
}
|
||||||
@@ -1609,6 +1687,10 @@ export const bootstrapAdminChat = async (
|
|||||||
|
|
||||||
bootstrapPromise = (async () => {
|
bootstrapPromise = (async () => {
|
||||||
try {
|
try {
|
||||||
|
void postAdminChatEnsureWebhook().catch((error) => {
|
||||||
|
console.error('admin chat ensure webhook failed:', error)
|
||||||
|
})
|
||||||
|
|
||||||
await fetchAdminRooms()
|
await fetchAdminRooms()
|
||||||
|
|
||||||
const shouldConnect = connectEvenIfNoRooms || rooms.value.length > 0
|
const shouldConnect = connectEvenIfNoRooms || rooms.value.length > 0
|
||||||
|
|||||||
Reference in New Issue
Block a user