update detech new message and auto sync conversations

This commit is contained in:
Admin 2025-09-09 09:48:13 +07:00
parent 83785fdf0a
commit cf29ee8348
13 changed files with 193 additions and 39 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,7 @@
"name": "re-make-bid-extension",
"version": "0.0.0",
"dependencies": {
"axios": "^1.10.0",
"axios": "^1.11.0",
"class-variance-authority": "^0.7.1",
"date-fns": "^4.1.0",
"next-themes": "^0.4.6",
@ -1862,13 +1862,13 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
@ -2693,9 +2693,9 @@
}
},
"node_modules/form-data": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",

View File

@ -11,7 +11,7 @@
"dev:build": "vite build --watch"
},
"dependencies": {
"axios": "^1.10.0",
"axios": "^1.11.0",
"class-variance-authority": "^0.7.1",
"date-fns": "^4.1.0",
"next-themes": "^0.4.6",

View File

@ -0,0 +1,36 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import axios from "@/lib/axios";
class ConversationApiService {
async index(query: Record<string, any>) {
try {
const { data } = await axios({
url: "conversations",
params: query,
});
console.log("[NestJS] Response (conversation-index):", data);
return data;
} catch (err) {
console.error("[NestJS] Error (conversation-index):", err);
throw err;
}
}
async getConversationByPrefix() {
try {
const { data } = await axios({
url: "conversations/prefix",
method: "POST",
});
console.log("[NestJS] Response (conversation-prefix):", data);
return data;
} catch (err) {
console.error("[NestJS] Error (conversation-prefix):", err);
throw err;
}
}
}
export const conversationApi = new ConversationApiService();

View File

@ -22,6 +22,18 @@ class MessageApiService {
throw err;
}
}
async createAndSendToZulip(messages: IMessage[]) {
try {
const { data } = await axios.post("/messages/create-and-send", {
data: messages,
});
console.log("[NestJS] Response (create and send):", data);
return data;
} catch (err) {
console.error("[NestJS] Error (create and send):", err);
throw err;
}
}
}
export const messageApi = new MessageApiService();

View File

@ -39,12 +39,8 @@ port.onMessage.addListener((msg: IMsg<any>) => {
}
});
// DETECH NEW MESSAGE (INTERVAl)
contentService.detectNewMessage();
// SYNC CONVERSASIONS (INTERVAL)
contentService.startSyncConversations();
// const a = new TeamsChatService();
// a.start();
// AUTO SYNC MESAGE PREFIX (INTERNAL)
contentService.autoSyncConversationPrefixMessages();

View File

@ -6,6 +6,7 @@ import { EVENTS } from "@/lib/event";
import { typeingService } from "./typing.service";
import { delay } from "@/features/app";
import { messageApi } from "@/api/message-api.service";
import { conversationApi } from "@/api/conversation-api.service";
export class ContentService {
service: TeamsChatService;
@ -532,6 +533,61 @@ export class ContentService {
}
startSyncConversations() {
setInterval(() => this.getConversations(), 20000);
// Tạo namespace nếu chưa tồn tại
(window as any)._chatIntervals = (window as any)?._chatIntervals || {};
// Kiểm tra xem interval đã tồn tại chưa
if (!(window as any)._chatIntervals.syncConversations) {
(window as any)._chatIntervals.syncConversations = window.setInterval(
() => {
this.getConversations();
},
20000
);
console.log("✅ startSyncConversations running");
} else {
console.log(" startSyncConversations already running");
}
}
// Interval chạy, chỉ add task vào queue
autoSyncConversationPrefixMessages() {
(window as any)._chatIntervals = (window as any)?._chatIntervals || {};
if (!(window as any)._chatIntervals.syncPrefixMessages) {
(window as any)._chatIntervals.syncPrefixMessages = (
window as any
).setInterval(() => {
queue.add(async () => {
try {
const { data } =
(await conversationApi.getConversationByPrefix()) as {
data: ChatItem[];
};
if (!data) return;
for (const chat of data) {
this._clickToConversation(chat.id as string);
await delay(1000);
const currentRoom = this.service.getCurrentRoomInfo();
if (!currentRoom || currentRoom.room_id !== chat.id) return;
const messages = this.service.extractAllMessages();
await messageApi.createAndSendToZulip(messages);
}
} catch (err) {
console.error("❌ autoSyncConversationPrefixMessages error:", err);
}
});
}, 10000);
console.log("✅ autoSyncConversationPrefixMessages running with PQueue");
} else {
console.log(" autoSyncConversationPrefixMessages already running");
}
}
}

View File

@ -1,4 +1,4 @@
import { Controller, Get, Param } from '@nestjs/common';
import { Controller, Get, Param, Post, Query } from '@nestjs/common';
import { Paginate, PaginateQuery } from 'nestjs-paginate';
import { ConversationsService } from './conversations.service';
import { Conversation } from '@/entities/conversation.entity';
@ -8,8 +8,16 @@ export class ConversationsController {
constructor(private readonly service: ConversationsService) {}
@Get('')
getConversations(@Paginate() query: PaginateQuery) {
return this.service.index(query);
getConversations(
@Paginate() query: PaginateQuery,
@Query('current') current: boolean = true,
) {
return this.service.index({ ...query, current });
}
@Post('prefix')
getConversationsByPrefix() {
return this.service.getConversationsByPrefix();
}
@Get(':id')

View File

@ -4,11 +4,10 @@ import { SystemLang } from '@/system/lang/system.lang';
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { paginate, PaginateQuery } from 'nestjs-paginate';
import { Repository } from 'typeorm';
import { ILike, Repository } from 'typeorm';
import { CreateMessageDto } from '../messages/dtos/create-message.dto';
import { MessagesEventService } from '../messages/messages-event.service';
import { MessagesService } from '../messages/messages.service';
import { delay } from '@/features/delay';
@Injectable()
export class ConversationsService {
@ -19,26 +18,29 @@ export class ConversationsService {
private messageService: MessagesService,
) {}
async index(query: PaginateQuery) {
await this.event.sendEvent(
MessagesEventService.EVENTS.GET_CONVERSATIONS,
{},
);
try {
await this.event.waitForEvent<Conversation[]>(
MessagesEventService.LOCAL_EVENTS.RECEIVE_CONVERSATIONS,
async index(query: PaginateQuery & { current: boolean }) {
if (query.current) {
await this.event.sendEvent(
MessagesEventService.EVENTS.GET_CONVERSATIONS,
{},
);
} catch (error) {
console.log(error);
try {
await this.event.waitForEvent<Conversation[]>(
MessagesEventService.LOCAL_EVENTS.RECEIVE_CONVERSATIONS,
);
} catch (error) {
console.log(error);
}
}
const result = await paginate(query, this.repo, {
sortableColumns: ['created_at'],
sortableColumns: ['created_at', 'name'],
searchableColumns: ['name'],
defaultLimit: 10,
filterableColumns: {
id: true,
name: true,
},
maxLimit: 100,
defaultSortBy: [['created_at', 'DESC']],
@ -94,4 +96,16 @@ export class ConversationsService {
return AppResponse.toResponse(result);
}
async getConversationsByPrefix() {
const prefix = (process.env.GROUP_PREFIX || '').trim();
const conversations = await this.repo.find({
where: {
name: ILike(`%${prefix}%`),
},
});
return AppResponse.toResponse(conversations || []);
}
}

View File

@ -20,6 +20,11 @@ export class MessagesController {
return this.service.sendMessage(data);
}
@Post('create-and-send')
createAndSendToZulip(@Body() data: CreateBulkMessageDto) {
return this.service.createAndSendToZulip(data);
}
@Post('')
save(@Body() data: CreateMessageDto) {
return this.service.create(data);

View File

@ -16,6 +16,7 @@ import { ReplyMessageDto } from './dtos/reply-message.dto';
import { SendMessageDto } from './dtos/send-message.dto';
import { MessagesEventService } from './messages-event.service';
import { ZulipService } from './zulip.service';
import { CreateBulkMessageDto } from './dtos/create-bulk-message.dto';
@Injectable()
export class MessagesService {
constructor(
@ -194,4 +195,18 @@ export class MessagesService {
data,
});
}
async createAndSendToZulip({ data }: CreateBulkMessageDto) {
const results = [];
for (const mes of data) {
const result = await this.create(mes);
if (result) {
results.push(result);
}
}
return results;
}
}