mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2026-03-21 20:18:40 -06:00
Merge pull request #2333 from augustolima1/fix/mysql-compat-lid
fix(mysql): compatibilidade da coluna lid e queries RAW
This commit is contained in:
@@ -131,8 +131,7 @@ ALTER TABLE `IntegrationSession` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRE
|
|||||||
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
-- AlterTable
|
-- AlterTable
|
||||||
ALTER TABLE `IsOnWhatsapp` DROP COLUMN `lid`,
|
ALTER TABLE `IsOnWhatsapp` MODIFY `createdAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
MODIFY `createdAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
-- AlterTable
|
-- AlterTable
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
-- Re-add lid column that was incorrectly dropped by previous migration
|
||||||
|
-- This migration ensures backward compatibility for existing installations
|
||||||
|
|
||||||
|
-- Check if column exists before adding
|
||||||
|
SET @dbname = DATABASE();
|
||||||
|
SET @tablename = 'IsOnWhatsapp';
|
||||||
|
SET @columnname = 'lid';
|
||||||
|
SET @preparedStatement = (SELECT IF(
|
||||||
|
(
|
||||||
|
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE
|
||||||
|
(table_name = @tablename)
|
||||||
|
AND (table_schema = @dbname)
|
||||||
|
AND (column_name = @columnname)
|
||||||
|
) > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
CONCAT('ALTER TABLE `', @tablename, '` ADD COLUMN `', @columnname, '` VARCHAR(100);')
|
||||||
|
));
|
||||||
|
PREPARE alterIfNotExists FROM @preparedStatement;
|
||||||
|
EXECUTE alterIfNotExists;
|
||||||
|
DEALLOCATE PREPARE alterIfNotExists;
|
||||||
@@ -655,6 +655,7 @@ model IsOnWhatsapp {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
remoteJid String @unique @db.VarChar(100)
|
remoteJid String @unique @db.VarChar(100)
|
||||||
jidOptions String
|
jidOptions String
|
||||||
|
lid String? @db.VarChar(100)
|
||||||
createdAt DateTime @default(dbgenerated("CURRENT_TIMESTAMP")) @db.Timestamp
|
createdAt DateTime @default(dbgenerated("CURRENT_TIMESTAMP")) @db.Timestamp
|
||||||
updatedAt DateTime @updatedAt @db.Timestamp
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -567,12 +567,27 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
private async getMessage(key: proto.IMessageKey, full = false) {
|
private async getMessage(key: proto.IMessageKey, full = false) {
|
||||||
try {
|
try {
|
||||||
// Use raw SQL to avoid JSON path issues
|
const provider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
const webMessageInfo = (await this.prismaRepository.$queryRaw`
|
|
||||||
SELECT * FROM "Message"
|
let webMessageInfo: proto.IWebMessageInfo[];
|
||||||
WHERE "instanceId" = ${this.instanceId}
|
|
||||||
AND "key"->>'id' = ${key.id}
|
if (provider === 'mysql') {
|
||||||
`) as proto.IWebMessageInfo[];
|
// MySQL version
|
||||||
|
webMessageInfo = (await this.prismaRepository.$queryRaw`
|
||||||
|
SELECT * FROM Message
|
||||||
|
WHERE instanceId = ${this.instanceId}
|
||||||
|
AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.id')) = ${key.id}
|
||||||
|
LIMIT 1
|
||||||
|
`) as proto.IWebMessageInfo[];
|
||||||
|
} else {
|
||||||
|
// PostgreSQL version
|
||||||
|
webMessageInfo = (await this.prismaRepository.$queryRaw`
|
||||||
|
SELECT * FROM "Message"
|
||||||
|
WHERE "instanceId" = ${this.instanceId}
|
||||||
|
AND "key"->>'id' = ${key.id}
|
||||||
|
LIMIT 1
|
||||||
|
`) as proto.IWebMessageInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
if (full) {
|
if (full) {
|
||||||
return webMessageInfo[0];
|
return webMessageInfo[0];
|
||||||
@@ -1687,29 +1702,25 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const searchId = originalMessageId || key.id;
|
const searchId = originalMessageId || key.id;
|
||||||
|
const dbProvider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
|
|
||||||
let retries = 0;
|
let messages: any[];
|
||||||
const maxRetries = 3;
|
if (dbProvider === 'mysql') {
|
||||||
const retryDelay = 500; // 500ms delay to avoid blocking for too long
|
messages = (await this.prismaRepository.$queryRaw`
|
||||||
|
SELECT * FROM Message
|
||||||
while (retries < maxRetries) {
|
WHERE instanceId = ${this.instanceId}
|
||||||
const messages = (await this.prismaRepository.$queryRaw`
|
AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.id')) = ${searchId}
|
||||||
|
LIMIT 1
|
||||||
|
`) as any[];
|
||||||
|
} else {
|
||||||
|
messages = (await this.prismaRepository.$queryRaw`
|
||||||
SELECT * FROM "Message"
|
SELECT * FROM "Message"
|
||||||
WHERE "instanceId" = ${this.instanceId}
|
WHERE "instanceId" = ${this.instanceId}
|
||||||
AND "key"->>'id' = ${searchId}
|
AND "key"->>'id' = ${searchId}
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`) as any[];
|
`) as any[];
|
||||||
findMessage = messages[0] || null;
|
|
||||||
|
|
||||||
if (findMessage?.id) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
retries++;
|
|
||||||
if (retries < maxRetries) {
|
|
||||||
await delay(retryDelay);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
findMessage = messages[0] || null;
|
||||||
|
|
||||||
if (!findMessage?.id) {
|
if (!findMessage?.id) {
|
||||||
this.logger.verbose(
|
this.logger.verbose(
|
||||||
@@ -4835,16 +4846,32 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
private async updateMessagesReadedByTimestamp(remoteJid: string, timestamp?: number): Promise<number> {
|
private async updateMessagesReadedByTimestamp(remoteJid: string, timestamp?: number): Promise<number> {
|
||||||
if (timestamp === undefined || timestamp === null) return 0;
|
if (timestamp === undefined || timestamp === null) return 0;
|
||||||
|
|
||||||
// Use raw SQL to avoid JSON path issues
|
const provider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
const result = await this.prismaRepository.$executeRaw`
|
let result: number;
|
||||||
UPDATE "Message"
|
|
||||||
SET "status" = ${status[4]}
|
if (provider === 'mysql') {
|
||||||
WHERE "instanceId" = ${this.instanceId}
|
// MySQL version
|
||||||
AND "key"->>'remoteJid' = ${remoteJid}
|
result = await this.prismaRepository.$executeRaw`
|
||||||
AND ("key"->>'fromMe')::boolean = false
|
UPDATE Message
|
||||||
AND "messageTimestamp" <= ${timestamp}
|
SET status = ${status[4]}
|
||||||
AND ("status" IS NULL OR "status" = ${status[3]})
|
WHERE instanceId = ${this.instanceId}
|
||||||
`;
|
AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.remoteJid')) = ${remoteJid}
|
||||||
|
AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.fromMe')) = 'false'
|
||||||
|
AND messageTimestamp <= ${timestamp}
|
||||||
|
AND (status IS NULL OR status = ${status[3]})
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
// PostgreSQL version
|
||||||
|
result = await this.prismaRepository.$executeRaw`
|
||||||
|
UPDATE "Message"
|
||||||
|
SET "status" = ${status[4]}
|
||||||
|
WHERE "instanceId" = ${this.instanceId}
|
||||||
|
AND "key"->>'remoteJid' = ${remoteJid}
|
||||||
|
AND ("key"->>'fromMe')::boolean = false
|
||||||
|
AND "messageTimestamp" <= ${timestamp}
|
||||||
|
AND ("status" IS NULL OR "status" = ${status[3]})
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
@@ -4858,16 +4885,33 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async updateChatUnreadMessages(remoteJid: string): Promise<number> {
|
private async updateChatUnreadMessages(remoteJid: string): Promise<number> {
|
||||||
const [chat, unreadMessages] = await Promise.all([
|
const provider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
this.prismaRepository.chat.findFirst({ where: { remoteJid } }),
|
|
||||||
// Use raw SQL to avoid JSON path issues
|
let unreadMessagesPromise: Promise<number>;
|
||||||
this.prismaRepository.$queryRaw`
|
|
||||||
|
if (provider === 'mysql') {
|
||||||
|
// MySQL version
|
||||||
|
unreadMessagesPromise = this.prismaRepository.$queryRaw`
|
||||||
|
SELECT COUNT(*) as count FROM Message
|
||||||
|
WHERE instanceId = ${this.instanceId}
|
||||||
|
AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.remoteJid')) = ${remoteJid}
|
||||||
|
AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.fromMe')) = 'false'
|
||||||
|
AND status = ${status[3]}
|
||||||
|
`.then((result: any[]) => Number(result[0]?.count) || 0);
|
||||||
|
} else {
|
||||||
|
// PostgreSQL version
|
||||||
|
unreadMessagesPromise = this.prismaRepository.$queryRaw`
|
||||||
SELECT COUNT(*)::int as count FROM "Message"
|
SELECT COUNT(*)::int as count FROM "Message"
|
||||||
WHERE "instanceId" = ${this.instanceId}
|
WHERE "instanceId" = ${this.instanceId}
|
||||||
AND "key"->>'remoteJid' = ${remoteJid}
|
AND "key"->>'remoteJid' = ${remoteJid}
|
||||||
AND ("key"->>'fromMe')::boolean = false
|
AND ("key"->>'fromMe')::boolean = false
|
||||||
AND "status" = ${status[3]}
|
AND "status" = ${status[3]}
|
||||||
`.then((result: any[]) => result[0]?.count || 0),
|
`.then((result: any[]) => result[0]?.count || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [chat, unreadMessages] = await Promise.all([
|
||||||
|
this.prismaRepository.chat.findFirst({ where: { remoteJid } }),
|
||||||
|
unreadMessagesPromise,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (chat && chat.unreadMessages !== unreadMessages) {
|
if (chat && chat.unreadMessages !== unreadMessages) {
|
||||||
@@ -4879,50 +4923,95 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
private async addLabel(labelId: string, instanceId: string, chatId: string) {
|
private async addLabel(labelId: string, instanceId: string, chatId: string) {
|
||||||
const id = cuid();
|
const id = cuid();
|
||||||
|
const provider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
|
|
||||||
await this.prismaRepository.$executeRawUnsafe(
|
if (provider === 'mysql') {
|
||||||
`INSERT INTO "Chat" ("id", "instanceId", "remoteJid", "labels", "createdAt", "updatedAt")
|
// MySQL version - use INSERT ... ON DUPLICATE KEY UPDATE
|
||||||
VALUES ($4, $2, $3, to_jsonb(ARRAY[$1]::text[]), NOW(), NOW()) ON CONFLICT ("instanceId", "remoteJid")
|
await this.prismaRepository.$executeRawUnsafe(
|
||||||
DO
|
`INSERT INTO Chat (id, instanceId, remoteJid, labels, createdAt, updatedAt)
|
||||||
UPDATE
|
VALUES (?, ?, ?, JSON_ARRAY(?), NOW(), NOW())
|
||||||
SET "labels" = (
|
ON DUPLICATE KEY UPDATE
|
||||||
SELECT to_jsonb(array_agg(DISTINCT elem))
|
labels = JSON_ARRAY_APPEND(
|
||||||
FROM (
|
COALESCE(labels, JSON_ARRAY()),
|
||||||
SELECT jsonb_array_elements_text("Chat"."labels") AS elem
|
'$',
|
||||||
UNION
|
?
|
||||||
SELECT $1::text AS elem
|
),
|
||||||
) sub
|
updatedAt = NOW()`,
|
||||||
),
|
id,
|
||||||
"updatedAt" = NOW();`,
|
instanceId,
|
||||||
labelId,
|
chatId,
|
||||||
instanceId,
|
labelId,
|
||||||
chatId,
|
labelId,
|
||||||
id,
|
);
|
||||||
);
|
} else {
|
||||||
|
// PostgreSQL version
|
||||||
|
await this.prismaRepository.$executeRawUnsafe(
|
||||||
|
`INSERT INTO "Chat" ("id", "instanceId", "remoteJid", "labels", "createdAt", "updatedAt")
|
||||||
|
VALUES ($4, $2, $3, to_jsonb(ARRAY[$1]::text[]), NOW(), NOW()) ON CONFLICT ("instanceId", "remoteJid")
|
||||||
|
DO
|
||||||
|
UPDATE
|
||||||
|
SET "labels" = (
|
||||||
|
SELECT to_jsonb(array_agg(DISTINCT elem))
|
||||||
|
FROM (
|
||||||
|
SELECT jsonb_array_elements_text("Chat"."labels") AS elem
|
||||||
|
UNION
|
||||||
|
SELECT $1::text AS elem
|
||||||
|
) sub
|
||||||
|
),
|
||||||
|
"updatedAt" = NOW();`,
|
||||||
|
labelId,
|
||||||
|
instanceId,
|
||||||
|
chatId,
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeLabel(labelId: string, instanceId: string, chatId: string) {
|
private async removeLabel(labelId: string, instanceId: string, chatId: string) {
|
||||||
const id = cuid();
|
const id = cuid();
|
||||||
|
const provider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
|
|
||||||
await this.prismaRepository.$executeRawUnsafe(
|
if (provider === 'mysql') {
|
||||||
`INSERT INTO "Chat" ("id", "instanceId", "remoteJid", "labels", "createdAt", "updatedAt")
|
// MySQL version - use INSERT ... ON DUPLICATE KEY UPDATE
|
||||||
VALUES ($4, $2, $3, '[]'::jsonb, NOW(), NOW()) ON CONFLICT ("instanceId", "remoteJid")
|
await this.prismaRepository.$executeRawUnsafe(
|
||||||
DO
|
`INSERT INTO Chat (id, instanceId, remoteJid, labels, createdAt, updatedAt)
|
||||||
UPDATE
|
VALUES (?, ?, ?, JSON_ARRAY(), NOW(), NOW())
|
||||||
SET "labels" = COALESCE (
|
ON DUPLICATE KEY UPDATE
|
||||||
(
|
labels = COALESCE(
|
||||||
SELECT jsonb_agg(elem)
|
JSON_REMOVE(
|
||||||
FROM jsonb_array_elements_text("Chat"."labels") AS elem
|
labels,
|
||||||
WHERE elem <> $1
|
JSON_UNQUOTE(JSON_SEARCH(labels, 'one', ?))
|
||||||
),
|
),
|
||||||
'[]'::jsonb
|
JSON_ARRAY()
|
||||||
),
|
),
|
||||||
"updatedAt" = NOW();`,
|
updatedAt = NOW()`,
|
||||||
labelId,
|
id,
|
||||||
instanceId,
|
instanceId,
|
||||||
chatId,
|
chatId,
|
||||||
id,
|
labelId,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// PostgreSQL version
|
||||||
|
await this.prismaRepository.$executeRawUnsafe(
|
||||||
|
`INSERT INTO "Chat" ("id", "instanceId", "remoteJid", "labels", "createdAt", "updatedAt")
|
||||||
|
VALUES ($4, $2, $3, '[]'::jsonb, NOW(), NOW()) ON CONFLICT ("instanceId", "remoteJid")
|
||||||
|
DO
|
||||||
|
UPDATE
|
||||||
|
SET "labels" = COALESCE (
|
||||||
|
(
|
||||||
|
SELECT jsonb_agg(elem)
|
||||||
|
FROM jsonb_array_elements_text("Chat"."labels") AS elem
|
||||||
|
WHERE elem <> $1
|
||||||
|
),
|
||||||
|
'[]'::jsonb
|
||||||
|
),
|
||||||
|
"updatedAt" = NOW();`,
|
||||||
|
labelId,
|
||||||
|
instanceId,
|
||||||
|
chatId,
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async baileysOnWhatsapp(jid: string) {
|
public async baileysOnWhatsapp(jid: string) {
|
||||||
|
|||||||
@@ -1617,18 +1617,36 @@ export class ChatwootService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use raw SQL to avoid JSON path issues
|
const provider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
const result = await this.prismaRepository.$executeRaw`
|
let result: number;
|
||||||
UPDATE "Message"
|
|
||||||
SET
|
if (provider === 'mysql') {
|
||||||
"chatwootMessageId" = ${chatwootMessageIds.messageId},
|
// MySQL version
|
||||||
"chatwootConversationId" = ${chatwootMessageIds.conversationId},
|
result = await this.prismaRepository.$executeRaw`
|
||||||
"chatwootInboxId" = ${chatwootMessageIds.inboxId},
|
UPDATE Message
|
||||||
"chatwootContactInboxSourceId" = ${chatwootMessageIds.contactInboxSourceId},
|
SET
|
||||||
"chatwootIsRead" = ${chatwootMessageIds.isRead || false}
|
chatwootMessageId = ${chatwootMessageIds.messageId},
|
||||||
WHERE "instanceId" = ${instance.instanceId}
|
chatwootConversationId = ${chatwootMessageIds.conversationId},
|
||||||
AND "key"->>'id' = ${key.id}
|
chatwootInboxId = ${chatwootMessageIds.inboxId},
|
||||||
`;
|
chatwootContactInboxSourceId = ${chatwootMessageIds.contactInboxSourceId},
|
||||||
|
chatwootIsRead = ${chatwootMessageIds.isRead || false}
|
||||||
|
WHERE instanceId = ${instance.instanceId}
|
||||||
|
AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.id')) = ${key.id}
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
// PostgreSQL version
|
||||||
|
result = await this.prismaRepository.$executeRaw`
|
||||||
|
UPDATE "Message"
|
||||||
|
SET
|
||||||
|
"chatwootMessageId" = ${chatwootMessageIds.messageId},
|
||||||
|
"chatwootConversationId" = ${chatwootMessageIds.conversationId},
|
||||||
|
"chatwootInboxId" = ${chatwootMessageIds.inboxId},
|
||||||
|
"chatwootContactInboxSourceId" = ${chatwootMessageIds.contactInboxSourceId},
|
||||||
|
"chatwootIsRead" = ${chatwootMessageIds.isRead || false}
|
||||||
|
WHERE "instanceId" = ${instance.instanceId}
|
||||||
|
AND "key"->>'id' = ${key.id}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.verbose(`Update result: ${result} rows affected`);
|
this.logger.verbose(`Update result: ${result} rows affected`);
|
||||||
|
|
||||||
@@ -1642,15 +1660,28 @@ export class ChatwootService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getMessageByKeyId(instance: InstanceDto, keyId: string): Promise<MessageModel> {
|
private async getMessageByKeyId(instance: InstanceDto, keyId: string): Promise<MessageModel> {
|
||||||
// Use raw SQL query to avoid JSON path issues with Prisma
|
const provider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
const messages = await this.prismaRepository.$queryRaw`
|
let messages: MessageModel[];
|
||||||
SELECT * FROM "Message"
|
|
||||||
WHERE "instanceId" = ${instance.instanceId}
|
|
||||||
AND "key"->>'id' = ${keyId}
|
|
||||||
LIMIT 1
|
|
||||||
`;
|
|
||||||
|
|
||||||
return (messages as MessageModel[])[0] || null;
|
if (provider === 'mysql') {
|
||||||
|
// MySQL version
|
||||||
|
messages = await this.prismaRepository.$queryRaw`
|
||||||
|
SELECT * FROM Message
|
||||||
|
WHERE instanceId = ${instance.instanceId}
|
||||||
|
AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.id')) = ${keyId}
|
||||||
|
LIMIT 1
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
// PostgreSQL version
|
||||||
|
messages = await this.prismaRepository.$queryRaw`
|
||||||
|
SELECT * FROM "Message"
|
||||||
|
WHERE "instanceId" = ${instance.instanceId}
|
||||||
|
AND "key"->>'id' = ${keyId}
|
||||||
|
LIMIT 1
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages[0] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getReplyToIds(
|
private async getReplyToIds(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { TypebotService } from '@api/integrations/chatbot/typebot/services/typeb
|
|||||||
import { PrismaRepository, Query } from '@api/repository/repository.service';
|
import { PrismaRepository, Query } from '@api/repository/repository.service';
|
||||||
import { eventManager, waMonitor } from '@api/server.module';
|
import { eventManager, waMonitor } from '@api/server.module';
|
||||||
import { Events, wa } from '@api/types/wa.types';
|
import { Events, wa } from '@api/types/wa.types';
|
||||||
import { Auth, Chatwoot, ConfigService, HttpServer, Proxy } from '@config/env.config';
|
import { Auth, Chatwoot, ConfigService, Database, HttpServer, Proxy } from '@config/env.config';
|
||||||
import { Logger } from '@config/logger.config';
|
import { Logger } from '@config/logger.config';
|
||||||
import { NotFoundException } from '@exceptions';
|
import { NotFoundException } from '@exceptions';
|
||||||
import { Contact, Message, Prisma } from '@prisma/client';
|
import { Contact, Message, Prisma } from '@prisma/client';
|
||||||
@@ -731,63 +731,127 @@ export class ChannelStartupService {
|
|||||||
where['remoteJid'] = remoteJid;
|
where['remoteJid'] = remoteJid;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timestampFilter =
|
const provider = this.configService.get<Database>('DATABASE').PROVIDER;
|
||||||
query?.where?.messageTimestamp?.gte && query?.where?.messageTimestamp?.lte
|
|
||||||
? Prisma.sql`
|
|
||||||
AND "Message"."messageTimestamp" >= ${Math.floor(new Date(query.where.messageTimestamp.gte).getTime() / 1000)}
|
|
||||||
AND "Message"."messageTimestamp" <= ${Math.floor(new Date(query.where.messageTimestamp.lte).getTime() / 1000)}`
|
|
||||||
: Prisma.sql``;
|
|
||||||
|
|
||||||
const limit = query?.take ? Prisma.sql`LIMIT ${query.take}` : Prisma.sql``;
|
const limit = query?.take ? Prisma.sql`LIMIT ${query.take}` : Prisma.sql``;
|
||||||
const offset = query?.skip ? Prisma.sql`OFFSET ${query.skip}` : Prisma.sql``;
|
const offset = query?.skip ? Prisma.sql`OFFSET ${query.skip}` : Prisma.sql``;
|
||||||
|
|
||||||
const results = await this.prismaRepository.$queryRaw`
|
let results: any[];
|
||||||
WITH rankedMessages AS (
|
|
||||||
SELECT DISTINCT ON ("Message"."key"->>'remoteJid')
|
if (provider === 'mysql') {
|
||||||
"Contact"."id" as "contactId",
|
// MySQL version
|
||||||
"Message"."key"->>'remoteJid' as "remoteJid",
|
const timestampFilterMysql =
|
||||||
CASE
|
query?.where?.messageTimestamp?.gte && query?.where?.messageTimestamp?.lte
|
||||||
WHEN "Message"."key"->>'remoteJid' LIKE '%@g.us' THEN COALESCE("Chat"."name", "Contact"."pushName")
|
? Prisma.sql`
|
||||||
ELSE COALESCE("Contact"."pushName", "Message"."pushName")
|
AND Message.messageTimestamp >= ${Math.floor(new Date(query.where.messageTimestamp.gte).getTime() / 1000)}
|
||||||
END as "pushName",
|
AND Message.messageTimestamp <= ${Math.floor(new Date(query.where.messageTimestamp.lte).getTime() / 1000)}`
|
||||||
"Contact"."profilePicUrl",
|
: Prisma.sql``;
|
||||||
COALESCE(
|
|
||||||
to_timestamp("Message"."messageTimestamp"::double precision),
|
results = await this.prismaRepository.$queryRaw`
|
||||||
"Contact"."updatedAt"
|
SELECT
|
||||||
) as "updatedAt",
|
Contact.id as contactId,
|
||||||
"Chat"."name" as "pushName",
|
JSON_UNQUOTE(JSON_EXTRACT(Message.key, '$.remoteJid')) as remoteJid,
|
||||||
"Chat"."createdAt" as "windowStart",
|
|
||||||
"Chat"."createdAt" + INTERVAL '24 hours' as "windowExpires",
|
|
||||||
"Chat"."unreadMessages" as "unreadMessages",
|
|
||||||
CASE WHEN "Chat"."createdAt" + INTERVAL '24 hours' > NOW() THEN true ELSE false END as "windowActive",
|
|
||||||
"Message"."id" AS "lastMessageId",
|
|
||||||
"Message"."key" AS "lastMessage_key",
|
|
||||||
CASE
|
CASE
|
||||||
WHEN "Message"."key"->>'fromMe' = 'true' THEN 'Você'
|
WHEN JSON_UNQUOTE(JSON_EXTRACT(Message.key, '$.remoteJid')) LIKE '%@g.us' THEN COALESCE(Chat.name, Contact.pushName)
|
||||||
ELSE "Message"."pushName"
|
ELSE COALESCE(Contact.pushName, Message.pushName)
|
||||||
END AS "lastMessagePushName",
|
END as pushName,
|
||||||
"Message"."participant" AS "lastMessageParticipant",
|
Contact.profilePicUrl,
|
||||||
"Message"."messageType" AS "lastMessageMessageType",
|
COALESCE(
|
||||||
"Message"."message" AS "lastMessageMessage",
|
FROM_UNIXTIME(Message.messageTimestamp),
|
||||||
"Message"."contextInfo" AS "lastMessageContextInfo",
|
Contact.updatedAt
|
||||||
"Message"."source" AS "lastMessageSource",
|
) as updatedAt,
|
||||||
"Message"."messageTimestamp" AS "lastMessageMessageTimestamp",
|
Chat.name as chatName,
|
||||||
"Message"."instanceId" AS "lastMessageInstanceId",
|
Chat.createdAt as windowStart,
|
||||||
"Message"."sessionId" AS "lastMessageSessionId",
|
DATE_ADD(Chat.createdAt, INTERVAL 24 HOUR) as windowExpires,
|
||||||
"Message"."status" AS "lastMessageStatus"
|
Chat.unreadMessages as unreadMessages,
|
||||||
FROM "Message"
|
CASE WHEN DATE_ADD(Chat.createdAt, INTERVAL 24 HOUR) > NOW() THEN 1 ELSE 0 END as windowActive,
|
||||||
LEFT JOIN "Contact" ON "Contact"."remoteJid" = "Message"."key"->>'remoteJid' AND "Contact"."instanceId" = "Message"."instanceId"
|
Message.id AS lastMessageId,
|
||||||
LEFT JOIN "Chat" ON "Chat"."remoteJid" = "Message"."key"->>'remoteJid' AND "Chat"."instanceId" = "Message"."instanceId"
|
Message.key AS lastMessage_key,
|
||||||
WHERE "Message"."instanceId" = ${this.instanceId}
|
CASE
|
||||||
${remoteJid ? Prisma.sql`AND "Message"."key"->>'remoteJid' = ${remoteJid}` : Prisma.sql``}
|
WHEN JSON_UNQUOTE(JSON_EXTRACT(Message.key, '$.fromMe')) = 'true' THEN 'Você'
|
||||||
${timestampFilter}
|
ELSE Message.pushName
|
||||||
ORDER BY "Message"."key"->>'remoteJid', "Message"."messageTimestamp" DESC
|
END AS lastMessagePushName,
|
||||||
)
|
Message.participant AS lastMessageParticipant,
|
||||||
SELECT * FROM rankedMessages
|
Message.messageType AS lastMessageMessageType,
|
||||||
ORDER BY "updatedAt" DESC NULLS LAST
|
Message.message AS lastMessageMessage,
|
||||||
${limit}
|
Message.contextInfo AS lastMessageContextInfo,
|
||||||
${offset};
|
Message.source AS lastMessageSource,
|
||||||
`;
|
Message.messageTimestamp AS lastMessageMessageTimestamp,
|
||||||
|
Message.instanceId AS lastMessageInstanceId,
|
||||||
|
Message.sessionId AS lastMessageSessionId,
|
||||||
|
Message.status AS lastMessageStatus
|
||||||
|
FROM Message
|
||||||
|
LEFT JOIN Contact ON Contact.remoteJid = JSON_UNQUOTE(JSON_EXTRACT(Message.key, '$.remoteJid')) AND Contact.instanceId = Message.instanceId
|
||||||
|
LEFT JOIN Chat ON Chat.remoteJid = JSON_UNQUOTE(JSON_EXTRACT(Message.key, '$.remoteJid')) AND Chat.instanceId = Message.instanceId
|
||||||
|
WHERE Message.instanceId = ${this.instanceId}
|
||||||
|
${remoteJid ? Prisma.sql`AND JSON_UNQUOTE(JSON_EXTRACT(Message.key, '$.remoteJid')) = ${remoteJid}` : Prisma.sql``}
|
||||||
|
${timestampFilterMysql}
|
||||||
|
AND Message.messageTimestamp = (
|
||||||
|
SELECT MAX(m2.messageTimestamp)
|
||||||
|
FROM Message m2
|
||||||
|
WHERE JSON_UNQUOTE(JSON_EXTRACT(m2.key, '$.remoteJid')) = JSON_UNQUOTE(JSON_EXTRACT(Message.key, '$.remoteJid'))
|
||||||
|
AND m2.instanceId = Message.instanceId
|
||||||
|
)
|
||||||
|
ORDER BY updatedAt DESC
|
||||||
|
${limit}
|
||||||
|
${offset};
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
// PostgreSQL version
|
||||||
|
const timestampFilter =
|
||||||
|
query?.where?.messageTimestamp?.gte && query?.where?.messageTimestamp?.lte
|
||||||
|
? Prisma.sql`
|
||||||
|
AND "Message"."messageTimestamp" >= ${Math.floor(new Date(query.where.messageTimestamp.gte).getTime() / 1000)}
|
||||||
|
AND "Message"."messageTimestamp" <= ${Math.floor(new Date(query.where.messageTimestamp.lte).getTime() / 1000)}`
|
||||||
|
: Prisma.sql``;
|
||||||
|
|
||||||
|
results = await this.prismaRepository.$queryRaw`
|
||||||
|
WITH rankedMessages AS (
|
||||||
|
SELECT DISTINCT ON ("Message"."key"->>'remoteJid')
|
||||||
|
"Contact"."id" as "contactId",
|
||||||
|
"Message"."key"->>'remoteJid' as "remoteJid",
|
||||||
|
CASE
|
||||||
|
WHEN "Message"."key"->>'remoteJid' LIKE '%@g.us' THEN COALESCE("Chat"."name", "Contact"."pushName")
|
||||||
|
ELSE COALESCE("Contact"."pushName", "Message"."pushName")
|
||||||
|
END as "pushName",
|
||||||
|
"Contact"."profilePicUrl",
|
||||||
|
COALESCE(
|
||||||
|
to_timestamp("Message"."messageTimestamp"::double precision),
|
||||||
|
"Contact"."updatedAt"
|
||||||
|
) as "updatedAt",
|
||||||
|
"Chat"."name" as "pushName",
|
||||||
|
"Chat"."createdAt" as "windowStart",
|
||||||
|
"Chat"."createdAt" + INTERVAL '24 hours' as "windowExpires",
|
||||||
|
"Chat"."unreadMessages" as "unreadMessages",
|
||||||
|
CASE WHEN "Chat"."createdAt" + INTERVAL '24 hours' > NOW() THEN true ELSE false END as "windowActive",
|
||||||
|
"Message"."id" AS "lastMessageId",
|
||||||
|
"Message"."key" AS "lastMessage_key",
|
||||||
|
CASE
|
||||||
|
WHEN "Message"."key"->>'fromMe' = 'true' THEN 'Você'
|
||||||
|
ELSE "Message"."pushName"
|
||||||
|
END AS "lastMessagePushName",
|
||||||
|
"Message"."participant" AS "lastMessageParticipant",
|
||||||
|
"Message"."messageType" AS "lastMessageMessageType",
|
||||||
|
"Message"."message" AS "lastMessageMessage",
|
||||||
|
"Message"."contextInfo" AS "lastMessageContextInfo",
|
||||||
|
"Message"."source" AS "lastMessageSource",
|
||||||
|
"Message"."messageTimestamp" AS "lastMessageMessageTimestamp",
|
||||||
|
"Message"."instanceId" AS "lastMessageInstanceId",
|
||||||
|
"Message"."sessionId" AS "lastMessageSessionId",
|
||||||
|
"Message"."status" AS "lastMessageStatus"
|
||||||
|
FROM "Message"
|
||||||
|
LEFT JOIN "Contact" ON "Contact"."remoteJid" = "Message"."key"->>'remoteJid' AND "Contact"."instanceId" = "Message"."instanceId"
|
||||||
|
LEFT JOIN "Chat" ON "Chat"."remoteJid" = "Message"."key"->>'remoteJid' AND "Chat"."instanceId" = "Message"."instanceId"
|
||||||
|
WHERE "Message"."instanceId" = ${this.instanceId}
|
||||||
|
${remoteJid ? Prisma.sql`AND "Message"."key"->>'remoteJid' = ${remoteJid}` : Prisma.sql``}
|
||||||
|
${timestampFilter}
|
||||||
|
ORDER BY "Message"."key"->>'remoteJid', "Message"."messageTimestamp" DESC
|
||||||
|
)
|
||||||
|
SELECT * FROM rankedMessages
|
||||||
|
ORDER BY "updatedAt" DESC NULLS LAST
|
||||||
|
${limit}
|
||||||
|
${offset};
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
if (results && isArray(results) && results.length > 0) {
|
if (results && isArray(results) && results.length > 0) {
|
||||||
const mappedResults = results.map((contact) => {
|
const mappedResults = results.map((contact) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user