fix: improve ghl conversation sync mapping
This commit is contained in:
parent
013a908d92
commit
c5e40c5caf
3 changed files with 223 additions and 118 deletions
251
convex/crm.ts
251
convex/crm.ts
|
|
@ -15,6 +15,7 @@ import {
|
||||||
} from "./crmModel"
|
} from "./crmModel"
|
||||||
import {
|
import {
|
||||||
fetchGhlCallLogsPage,
|
fetchGhlCallLogsPage,
|
||||||
|
fetchGhlConversationsPage,
|
||||||
fetchGhlConversationMessages,
|
fetchGhlConversationMessages,
|
||||||
fetchGhlContactsPage,
|
fetchGhlContactsPage,
|
||||||
fetchGhlMessagesPage,
|
fetchGhlMessagesPage,
|
||||||
|
|
@ -142,6 +143,12 @@ function extractGhlMessages(payload: any) {
|
||||||
if (Array.isArray(payload?.items)) {
|
if (Array.isArray(payload?.items)) {
|
||||||
return payload.items
|
return payload.items
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(payload?.messages?.messages)) {
|
||||||
|
return payload.messages.messages
|
||||||
|
}
|
||||||
|
if (Array.isArray(payload?.data?.messages?.messages)) {
|
||||||
|
return payload.data.messages.messages
|
||||||
|
}
|
||||||
return Array.isArray(payload?.messages)
|
return Array.isArray(payload?.messages)
|
||||||
? payload.messages
|
? payload.messages
|
||||||
: Array.isArray(payload?.data?.messages)
|
: Array.isArray(payload?.data?.messages)
|
||||||
|
|
@ -151,6 +158,78 @@ function extractGhlMessages(payload: any) {
|
||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseGhlTimestamp(value: unknown) {
|
||||||
|
if (typeof value === "number" && Number.isFinite(value)) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === "string" && value.trim()) {
|
||||||
|
const numeric = Number(value)
|
||||||
|
if (Number.isFinite(numeric) && numeric > 0) {
|
||||||
|
return numeric
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = new Date(value).getTime()
|
||||||
|
if (Number.isFinite(parsed) && parsed > 0) {
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeGhlChannel(value?: unknown) {
|
||||||
|
const normalized = String(value || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
|
||||||
|
if (
|
||||||
|
normalized.includes("sms") ||
|
||||||
|
normalized.includes("whatsapp") ||
|
||||||
|
normalized.includes("message")
|
||||||
|
) {
|
||||||
|
return "sms"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
normalized.includes("call") ||
|
||||||
|
normalized.includes("phone") ||
|
||||||
|
normalized.includes("voicemail")
|
||||||
|
) {
|
||||||
|
return "call"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized.includes("chat")) {
|
||||||
|
return "chat"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveGhlConversationChannel(payload: any) {
|
||||||
|
return (
|
||||||
|
(payload.channel ? normalizeGhlChannel(payload.channel) : undefined) ||
|
||||||
|
(payload.messageType ? normalizeGhlChannel(payload.messageType) : undefined) ||
|
||||||
|
(payload.lastMessageType
|
||||||
|
? normalizeGhlChannel(payload.lastMessageType)
|
||||||
|
: undefined) ||
|
||||||
|
(payload.type ? normalizeGhlChannel(payload.type) : undefined) ||
|
||||||
|
"unknown"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveConversationStatus(payload: any) {
|
||||||
|
if (payload.status) {
|
||||||
|
return normalizeConversationStatus(payload.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof payload.inbox === "boolean") {
|
||||||
|
return payload.inbox ? "open" : "closed"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "open"
|
||||||
|
}
|
||||||
|
|
||||||
function matchesSearch(values: Array<string | undefined>, search: string) {
|
function matchesSearch(values: Array<string | undefined>, search: string) {
|
||||||
if (!search) {
|
if (!search) {
|
||||||
return true
|
return true
|
||||||
|
|
@ -546,9 +625,9 @@ export const importContact = mutation({
|
||||||
source: `${args.provider}:mirror`,
|
source: `${args.provider}:mirror`,
|
||||||
ghlContactId: payload.id || args.entityId,
|
ghlContactId: payload.id || args.entityId,
|
||||||
lastActivityAt:
|
lastActivityAt:
|
||||||
typeof payload.dateUpdated === "string"
|
parseGhlTimestamp(payload.dateUpdated) ??
|
||||||
? new Date(payload.dateUpdated).getTime()
|
parseGhlTimestamp(payload.lastMessageDate) ??
|
||||||
: Date.now(),
|
Date.now(),
|
||||||
})
|
})
|
||||||
|
|
||||||
await upsertExternalSyncState(ctx, {
|
await upsertExternalSyncState(ctx, {
|
||||||
|
|
@ -590,34 +669,40 @@ export const importConversation = mutation({
|
||||||
ghlContactId: payload.contactId,
|
ghlContactId: payload.contactId,
|
||||||
status: "lead",
|
status: "lead",
|
||||||
lastActivityAt:
|
lastActivityAt:
|
||||||
typeof payload.dateUpdated === "string"
|
parseGhlTimestamp(payload.lastMessageDate) ??
|
||||||
? new Date(payload.dateUpdated).getTime()
|
parseGhlTimestamp(payload.dateUpdated) ??
|
||||||
: Date.now(),
|
Date.now(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const conversation = await upsertConversationRecord(ctx, {
|
const conversation = await upsertConversationRecord(ctx, {
|
||||||
contactId: contact?._id,
|
contactId: contact?._id,
|
||||||
title: payload.fullName || payload.title || payload.contactName,
|
title:
|
||||||
channel:
|
payload.fullName ||
|
||||||
payload.channel === "SMS" || payload.type === "sms" ? "sms" : "call",
|
payload.title ||
|
||||||
|
payload.contactName ||
|
||||||
|
payload.phone ||
|
||||||
|
payload.email,
|
||||||
|
channel: deriveGhlConversationChannel(payload),
|
||||||
source: `${args.provider}:mirror`,
|
source: `${args.provider}:mirror`,
|
||||||
status: normalizeConversationStatus(payload.status),
|
status: deriveConversationStatus(payload),
|
||||||
direction: normalizeConversationDirection(payload.direction),
|
direction: normalizeConversationDirection(
|
||||||
|
payload.lastMessageDirection || payload.direction
|
||||||
|
),
|
||||||
startedAt:
|
startedAt:
|
||||||
typeof payload.dateAdded === "string"
|
parseGhlTimestamp(payload.dateAdded) ??
|
||||||
? new Date(payload.dateAdded).getTime()
|
parseGhlTimestamp(payload.lastMessageDate) ??
|
||||||
: Date.now(),
|
Date.now(),
|
||||||
endedAt:
|
endedAt: parseGhlTimestamp(payload.dateEnded),
|
||||||
typeof payload.dateEnded === "string"
|
|
||||||
? new Date(payload.dateEnded).getTime()
|
|
||||||
: undefined,
|
|
||||||
lastMessageAt:
|
lastMessageAt:
|
||||||
typeof payload.lastMessageAt === "string"
|
parseGhlTimestamp(payload.lastMessageDate) ??
|
||||||
? new Date(payload.lastMessageAt).getTime()
|
parseGhlTimestamp(payload.lastMessageAt) ??
|
||||||
: undefined,
|
parseGhlTimestamp(payload.dateUpdated),
|
||||||
lastMessagePreview: payload.lastMessageBody || payload.snippet,
|
lastMessagePreview: payload.lastMessageBody || payload.snippet,
|
||||||
|
unreadCount:
|
||||||
|
typeof payload.unreadCount === "number" ? payload.unreadCount : undefined,
|
||||||
summaryText: payload.summary,
|
summaryText: payload.summary,
|
||||||
ghlConversationId: payload.id || args.entityId,
|
ghlConversationId:
|
||||||
|
payload.conversationId || args.entityId || payload.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
await ensureConversationParticipant(ctx, {
|
await ensureConversationParticipant(ctx, {
|
||||||
|
|
@ -675,24 +760,20 @@ export const importMessage = mutation({
|
||||||
|
|
||||||
const conversation = await upsertConversationRecord(ctx, {
|
const conversation = await upsertConversationRecord(ctx, {
|
||||||
contactId: contact?._id,
|
contactId: contact?._id,
|
||||||
title: payload.contactName,
|
title:
|
||||||
channel:
|
payload.contactName || payload.fullName || payload.name || payload.phone,
|
||||||
payload.channel === "SMS" || payload.messageType === "SMS"
|
channel: deriveGhlConversationChannel(payload),
|
||||||
? "sms"
|
|
||||||
: payload.channel === "Call"
|
|
||||||
? "call"
|
|
||||||
: "unknown",
|
|
||||||
source: `${args.provider}:mirror`,
|
source: `${args.provider}:mirror`,
|
||||||
status: normalizeConversationStatus(payload.conversationStatus),
|
status: normalizeConversationStatus(payload.conversationStatus),
|
||||||
direction: normalizeConversationDirection(payload.direction),
|
direction: normalizeConversationDirection(payload.direction),
|
||||||
startedAt:
|
startedAt:
|
||||||
typeof payload.dateAdded === "string"
|
parseGhlTimestamp(payload.dateAdded) ??
|
||||||
? new Date(payload.dateAdded).getTime()
|
parseGhlTimestamp(payload.lastMessageDate) ??
|
||||||
: Date.now(),
|
Date.now(),
|
||||||
lastMessageAt:
|
lastMessageAt:
|
||||||
typeof payload.dateAdded === "string"
|
parseGhlTimestamp(payload.dateAdded) ??
|
||||||
? new Date(payload.dateAdded).getTime()
|
parseGhlTimestamp(payload.lastMessageDate) ??
|
||||||
: Date.now(),
|
Date.now(),
|
||||||
lastMessagePreview: payload.body || payload.message,
|
lastMessagePreview: payload.body || payload.message,
|
||||||
ghlConversationId: payload.conversationId,
|
ghlConversationId: payload.conversationId,
|
||||||
})
|
})
|
||||||
|
|
@ -716,20 +797,15 @@ export const importMessage = mutation({
|
||||||
: payload.direction === "outbound"
|
: payload.direction === "outbound"
|
||||||
? "outbound"
|
? "outbound"
|
||||||
: "system",
|
: "system",
|
||||||
channel:
|
channel: deriveGhlConversationChannel(payload),
|
||||||
payload.channel === "SMS" || payload.messageType === "SMS"
|
|
||||||
? "sms"
|
|
||||||
: payload.channel === "Call"
|
|
||||||
? "call"
|
|
||||||
: "unknown",
|
|
||||||
source: `${args.provider}:mirror`,
|
source: `${args.provider}:mirror`,
|
||||||
messageType: payload.messageType || payload.type,
|
messageType: payload.messageType || payload.type,
|
||||||
body: payload.body || payload.message || payload.transcript || "",
|
body: payload.body || payload.message || payload.transcript || "",
|
||||||
status: payload.status,
|
status: payload.status,
|
||||||
sentAt:
|
sentAt:
|
||||||
typeof payload.dateAdded === "string"
|
parseGhlTimestamp(payload.dateAdded) ??
|
||||||
? new Date(payload.dateAdded).getTime()
|
parseGhlTimestamp(payload.lastMessageDate) ??
|
||||||
: Date.now(),
|
Date.now(),
|
||||||
ghlMessageId: payload.id || args.entityId,
|
ghlMessageId: payload.id || args.entityId,
|
||||||
metadata: JSON.stringify(payload),
|
metadata: JSON.stringify(payload),
|
||||||
})
|
})
|
||||||
|
|
@ -765,13 +841,9 @@ export const importRecording = mutation({
|
||||||
status: "closed",
|
status: "closed",
|
||||||
direction: normalizeConversationDirection(payload.direction),
|
direction: normalizeConversationDirection(payload.direction),
|
||||||
startedAt:
|
startedAt:
|
||||||
typeof payload.createdAt === "string"
|
parseGhlTimestamp(payload.createdAt) ?? Date.now(),
|
||||||
? new Date(payload.createdAt).getTime()
|
|
||||||
: Date.now(),
|
|
||||||
lastMessageAt:
|
lastMessageAt:
|
||||||
typeof payload.createdAt === "string"
|
parseGhlTimestamp(payload.createdAt) ?? Date.now(),
|
||||||
? new Date(payload.createdAt).getTime()
|
|
||||||
: Date.now(),
|
|
||||||
lastMessagePreview: payload.summary || payload.transcript,
|
lastMessagePreview: payload.summary || payload.transcript,
|
||||||
ghlConversationId: payload.conversationId,
|
ghlConversationId: payload.conversationId,
|
||||||
livekitRoomName: payload.livekitRoomName,
|
livekitRoomName: payload.livekitRoomName,
|
||||||
|
|
@ -791,13 +863,8 @@ export const importRecording = mutation({
|
||||||
? payload.duration * 1000
|
? payload.duration * 1000
|
||||||
: undefined,
|
: undefined,
|
||||||
startedAt:
|
startedAt:
|
||||||
typeof payload.createdAt === "string"
|
parseGhlTimestamp(payload.createdAt),
|
||||||
? new Date(payload.createdAt).getTime()
|
endedAt: parseGhlTimestamp(payload.endedAt),
|
||||||
: undefined,
|
|
||||||
endedAt:
|
|
||||||
typeof payload.endedAt === "string"
|
|
||||||
? new Date(payload.endedAt).getTime()
|
|
||||||
: undefined,
|
|
||||||
ghlMessageId: payload.messageId,
|
ghlMessageId: payload.messageId,
|
||||||
livekitRoomName: payload.livekitRoomName,
|
livekitRoomName: payload.livekitRoomName,
|
||||||
metadata: JSON.stringify(payload),
|
metadata: JSON.stringify(payload),
|
||||||
|
|
@ -1082,70 +1149,36 @@ export const runGhlMirror = action({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateRunning("conversations")
|
await updateRunning("conversations")
|
||||||
const previous = await ctx.runQuery(api.crm.getAdminSyncOverview, {})
|
const fetched = await fetchGhlConversationsPage(config, {
|
||||||
const conversationCursors = {
|
limit: 100,
|
||||||
SMS:
|
|
||||||
!args.forceFullBackfill &&
|
|
||||||
previous.stages.conversations.metadata?.cursors?.SMS
|
|
||||||
? String(previous.stages.conversations.metadata.cursors.SMS)
|
|
||||||
: undefined,
|
|
||||||
Call:
|
|
||||||
!args.forceFullBackfill &&
|
|
||||||
previous.stages.conversations.metadata?.cursors?.Call
|
|
||||||
? String(previous.stages.conversations.metadata.cursors.Call)
|
|
||||||
: undefined,
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const channel of ["SMS", "Call"] as const) {
|
|
||||||
let pages = 0
|
|
||||||
while (pages < maxPagesPerRun) {
|
|
||||||
const fetched = await fetchGhlMessagesPage(config, {
|
|
||||||
limit: messagesLimit,
|
|
||||||
cursor: conversationCursors[channel],
|
|
||||||
channel,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!fetched.items.length) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
const grouped = new Map<string, any>()
|
|
||||||
for (const item of fetched.items) {
|
for (const item of fetched.items) {
|
||||||
const conversationId = String(item.conversationId || item.id || "")
|
const entityId = String(item.id || "")
|
||||||
if (!conversationId || grouped.has(conversationId)) {
|
if (!entityId) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
grouped.set(conversationId, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [entityId, item] of grouped.entries()) {
|
|
||||||
await ctx.runMutation(api.crm.importConversation, {
|
await ctx.runMutation(api.crm.importConversation, {
|
||||||
provider: GHL_SYNC_PROVIDER,
|
provider: GHL_SYNC_PROVIDER,
|
||||||
entityId,
|
entityId,
|
||||||
payload: item,
|
payload: item,
|
||||||
})
|
})
|
||||||
hydrationTargets.set(entityId, item.channel || "")
|
hydrationTargets.set(entityId, item.lastMessageType || item.type || "")
|
||||||
summary.conversations += 1
|
summary.conversations += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pages += 1
|
|
||||||
conversationCursors[channel] = fetched.nextCursor
|
|
||||||
if (!fetched.nextCursor) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await ctx.runMutation(api.crm.updateSyncCheckpoint, {
|
await ctx.runMutation(api.crm.updateSyncCheckpoint, {
|
||||||
provider: GHL_SYNC_PROVIDER,
|
provider: GHL_SYNC_PROVIDER,
|
||||||
entityType: "conversations",
|
entityType: "conversations",
|
||||||
entityId: "conversations",
|
entityId: "conversations",
|
||||||
cursor: conversationCursors.Call || conversationCursors.SMS,
|
cursor: undefined,
|
||||||
status: "synced",
|
status: "synced",
|
||||||
error: "",
|
error: "",
|
||||||
metadata: JSON.stringify({
|
metadata: JSON.stringify({
|
||||||
imported: summary.conversations,
|
imported: summary.conversations,
|
||||||
cursors: conversationCursors,
|
total: fetched.total,
|
||||||
|
source: "search",
|
||||||
completedAt: Date.now(),
|
completedAt: Date.now(),
|
||||||
reason: args.reason || "cron",
|
reason: args.reason || "cron",
|
||||||
}),
|
}),
|
||||||
|
|
@ -1719,6 +1752,7 @@ export const listAdminConversations = query({
|
||||||
|
|
||||||
const conversations = await ctx.db.query("conversations").collect()
|
const conversations = await ctx.db.query("conversations").collect()
|
||||||
const filtered = []
|
const filtered = []
|
||||||
|
const staleShellCutoff = Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
for (const conversation of conversations) {
|
for (const conversation of conversations) {
|
||||||
if (args.channel && conversation.channel !== args.channel) {
|
if (args.channel && conversation.channel !== args.channel) {
|
||||||
|
|
@ -1748,14 +1782,29 @@ export const listAdminConversations = query({
|
||||||
) {
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
conversation.source === "ghl:mirror" &&
|
||||||
|
conversation.channel === "call" &&
|
||||||
|
!String(conversation.lastMessagePreview || "").trim() &&
|
||||||
|
(conversation.lastMessageAt ||
|
||||||
|
conversation.startedAt ||
|
||||||
|
conversation.updatedAt ||
|
||||||
|
0) < staleShellCutoff
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
filtered.push({ conversation, contact })
|
filtered.push({ conversation, contact })
|
||||||
}
|
}
|
||||||
|
|
||||||
filtered.sort(
|
filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
(b.conversation.lastMessageAt || b.conversation.updatedAt) -
|
(b.conversation.lastMessageAt ||
|
||||||
(a.conversation.lastMessageAt || a.conversation.updatedAt)
|
b.conversation.startedAt ||
|
||||||
|
b.conversation.updatedAt) -
|
||||||
|
(a.conversation.lastMessageAt ||
|
||||||
|
a.conversation.startedAt ||
|
||||||
|
a.conversation.updatedAt)
|
||||||
)
|
)
|
||||||
|
|
||||||
const paged = filtered.slice((page - 1) * limit, page * limit)
|
const paged = filtered.slice((page - 1) * limit, page * limit)
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,35 @@ export async function upsertConversationRecord(ctx, input) {
|
||||||
.unique()
|
.unique()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!existing && input.contactId) {
|
||||||
|
const candidates = await ctx.db
|
||||||
|
.query("conversations")
|
||||||
|
.withIndex("by_contactId", (q) => q.eq("contactId", input.contactId))
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
const targetMoment =
|
||||||
|
input.lastMessageAt ?? input.startedAt ?? input.updatedAt ?? now
|
||||||
|
|
||||||
|
existing =
|
||||||
|
candidates
|
||||||
|
.filter((candidate) => {
|
||||||
|
if (input.channel && candidate.channel !== input.channel) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const candidateMoment =
|
||||||
|
candidate.lastMessageAt ??
|
||||||
|
candidate.startedAt ??
|
||||||
|
candidate.updatedAt ??
|
||||||
|
0
|
||||||
|
return Math.abs(candidateMoment - targetMoment) <= 5 * 60 * 1000
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
const aMoment = a.lastMessageAt ?? a.startedAt ?? a.updatedAt ?? 0
|
||||||
|
const bMoment = b.lastMessageAt ?? b.startedAt ?? b.updatedAt ?? 0
|
||||||
|
return Math.abs(aMoment - targetMoment) - Math.abs(bMoment - targetMoment)
|
||||||
|
})[0] || null
|
||||||
|
}
|
||||||
|
|
||||||
const patch = {
|
const patch = {
|
||||||
contactId: input.contactId ?? existing?.contactId,
|
contactId: input.contactId ?? existing?.contactId,
|
||||||
title: input.title || existing?.title,
|
title: input.title || existing?.title,
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,29 @@ export async function fetchGhlMessagesPage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchGhlConversationsPage(
|
||||||
|
config: GhlMirrorConfig,
|
||||||
|
args?: {
|
||||||
|
limit?: number
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const url = new URL(`${config.baseUrl}/conversations/search`)
|
||||||
|
url.searchParams.set("locationId", config.locationId)
|
||||||
|
url.searchParams.set("limit", String(Math.min(100, Math.max(1, args?.limit || 100))))
|
||||||
|
|
||||||
|
const payload = await fetchGhlMirrorJson(config, url.pathname + url.search)
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: Array.isArray(payload?.conversations) ? payload.conversations : [],
|
||||||
|
total:
|
||||||
|
typeof payload?.total === "number"
|
||||||
|
? payload.total
|
||||||
|
: Array.isArray(payload?.conversations)
|
||||||
|
? payload.conversations.length
|
||||||
|
: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchGhlConversationMessages(
|
export async function fetchGhlConversationMessages(
|
||||||
config: GhlMirrorConfig,
|
config: GhlMirrorConfig,
|
||||||
args: {
|
args: {
|
||||||
|
|
@ -146,8 +169,12 @@ export async function fetchGhlConversationMessages(
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: Array.isArray(payload?.messages)
|
items: Array.isArray(payload?.messages?.messages)
|
||||||
|
? payload.messages.messages
|
||||||
|
: Array.isArray(payload?.messages)
|
||||||
? payload.messages
|
? payload.messages
|
||||||
|
: Array.isArray(payload?.data?.messages?.messages)
|
||||||
|
? payload.data.messages.messages
|
||||||
: Array.isArray(payload?.data?.messages)
|
: Array.isArray(payload?.data?.messages)
|
||||||
? payload.data.messages
|
? payload.data.messages
|
||||||
: Array.isArray(payload)
|
: Array.isArray(payload)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue