update new version
This commit is contained in:
parent
3cbaedbfe5
commit
b008e6a420
File diff suppressed because one or more lines are too long
|
|
@ -22,6 +22,7 @@ class MessageApiService {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createAndSendToZulip(messages: IMessage[]) {
|
async createAndSendToZulip(messages: IMessage[]) {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post("/messages/create-and-send", {
|
const { data } = await axios.post("/messages/create-and-send", {
|
||||||
|
|
@ -34,6 +35,17 @@ class MessageApiService {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendLog(log: ILog) {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post("/messages/logs", log);
|
||||||
|
console.log("[NestJS] Response (logs):", data);
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[NestJS] Error (logs):", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const messageApi = new MessageApiService();
|
export const messageApi = new MessageApiService();
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,12 @@ interface IMessage {
|
||||||
date_time?: number;
|
date_time?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ILog {
|
||||||
|
message: string;
|
||||||
|
type: "error" | "success";
|
||||||
|
id?: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface IInputGeo {
|
interface IInputGeo {
|
||||||
top: number;
|
top: number;
|
||||||
left: number;
|
left: number;
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,10 @@ export class ContentService {
|
||||||
let wrapper = document.querySelector(selector);
|
let wrapper = document.querySelector(selector);
|
||||||
if (!wrapper) {
|
if (!wrapper) {
|
||||||
console.error("Wrapper not found:", selector);
|
console.error("Wrapper not found:", selector);
|
||||||
|
await messageApi.sendLog({
|
||||||
|
type: "error",
|
||||||
|
message: `Not found selector: ${selector}`,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -400,27 +404,6 @@ export class ContentService {
|
||||||
console.log({ message });
|
console.log({ message });
|
||||||
|
|
||||||
await typeingService.send(message);
|
await typeingService.send(message);
|
||||||
|
|
||||||
// if (!initialMessages) return;
|
|
||||||
|
|
||||||
// // Sroll xuống
|
|
||||||
// this.service._scrollToBottomByXPath();
|
|
||||||
|
|
||||||
// // Theo dỗi lấy detech new message
|
|
||||||
// const newMessages = await this._waitForNewMessages(initialMessages, 3000);
|
|
||||||
// console.log("New messages appeared:", newMessages);
|
|
||||||
|
|
||||||
// await this._waitToloadMessages();
|
|
||||||
|
|
||||||
// // Lấy hết message mới nhất
|
|
||||||
// const data = this.service.extractAllMessages();
|
|
||||||
|
|
||||||
// // Gửi lên server cập nhật
|
|
||||||
// this.port.postMessage({
|
|
||||||
// type: "socket-response",
|
|
||||||
// event: EVENTS.RECEIVE_CONVERSATION,
|
|
||||||
// data,
|
|
||||||
// } as IMsg<any>);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -440,6 +423,10 @@ export class ContentService {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.warn("✘ Element not found with provided XPath after retries");
|
console.warn("✘ Element not found with provided XPath after retries");
|
||||||
|
messageApi.sendLog({
|
||||||
|
type: "error",
|
||||||
|
message: `Not found selector: ${this.service.elTags.chat_input}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -582,7 +569,14 @@ export class ContentService {
|
||||||
await delay(5000);
|
await delay(5000);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("❌ autoSyncConversationPrefixMessages error:", err);
|
console.error(
|
||||||
|
"❌ autoSyncConversationPrefixMessages error:",
|
||||||
|
(err as any)?.message
|
||||||
|
);
|
||||||
|
messageApi.sendLog({
|
||||||
|
type: "error",
|
||||||
|
message: `auto_to_sync_conersations: ${(err as any)?.message}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { messageApi } from "@/api/message-api.service";
|
||||||
|
|
||||||
export class TeamsChatService {
|
export class TeamsChatService {
|
||||||
private readonly MY_NAME = "Apactech com";
|
private readonly MY_NAME = "Apactech com";
|
||||||
public lastMessage?: IMessage;
|
public lastMessage?: IMessage;
|
||||||
|
|
@ -173,6 +175,10 @@ export class TeamsChatService {
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
console.log("Không tìm thấy phần tử theo XPath.");
|
console.log("Không tìm thấy phần tử theo XPath.");
|
||||||
|
messageApi.sendLog({
|
||||||
|
type: "error",
|
||||||
|
message: `Not found selector: ${xpath}`,
|
||||||
|
});
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,6 +239,10 @@ export class TeamsChatService {
|
||||||
|
|
||||||
if (!container) {
|
if (!container) {
|
||||||
console.warn("❌ Không tìm thấy phần tử với XPath:", xpath);
|
console.warn("❌ Không tìm thấy phần tử với XPath:", xpath);
|
||||||
|
messageApi.sendLog({
|
||||||
|
type: "error",
|
||||||
|
message: `Not found selector: ${xpath}`,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
@Entity('logs')
|
||||||
|
export class Log {
|
||||||
|
@PrimaryGeneratedColumn('increment')
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
message: string; // nội dung
|
||||||
|
|
||||||
|
@Column({ type: 'varchar' })
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
created_at: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updated_at: Date;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
function isTextInFormat(text: string) {
|
||||||
|
const lines = text
|
||||||
|
.split('\n')
|
||||||
|
.map((l) => l.trim())
|
||||||
|
.filter((l) => l !== '');
|
||||||
|
if (lines.length < 3) return false;
|
||||||
|
|
||||||
|
const date = lines[1];
|
||||||
|
const productCode = lines[2];
|
||||||
|
|
||||||
|
const dateRegex = /\d{1,2}\/\d{1,2}\/\d{4},?\s+\d{1,2}:\d{2}\s[AP]M/;
|
||||||
|
const productCodeRegex = /.+/; // ít nhất 1 ký tự
|
||||||
|
|
||||||
|
return dateRegex.test(date) && productCodeRegex.test(productCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTextIfValid(text: string) {
|
||||||
|
if (!isTextInFormat(text)) return text; // Không đúng format thì trả về nguyên bản
|
||||||
|
|
||||||
|
const lines = text
|
||||||
|
.split('\n')
|
||||||
|
.map((l) => l.trim())
|
||||||
|
.filter((l) => l !== '');
|
||||||
|
const name = lines[0];
|
||||||
|
const date = lines[1];
|
||||||
|
const productCode = lines[2];
|
||||||
|
const rest = lines.slice(3).join('\n');
|
||||||
|
|
||||||
|
return `${name} -- ${date}\n${productCode}\n\n${rest}`;
|
||||||
|
}
|
||||||
|
|
@ -3,5 +3,5 @@ import * as moment from 'moment-timezone';
|
||||||
export function formatTimeAU(timestamp: number) {
|
export function formatTimeAU(timestamp: number) {
|
||||||
return moment(timestamp)
|
return moment(timestamp)
|
||||||
.tz('Etc/GMT-10') // Luôn cố định UTC+10
|
.tz('Etc/GMT-10') // Luôn cố định UTC+10
|
||||||
.format('DD/MM/YYYY, h:mm A [AEST]');
|
.format('DD/MMM/YY, h:mm A [AEST]');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class SendLogDto {
|
||||||
|
@IsString()
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Log } from '@/entities/log.entity';
|
||||||
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { SendLogDto } from './dtos/send-log.dto';
|
||||||
|
import { ZulipService } from './zulip.service';
|
||||||
|
import AppResponse from '@/system/filters/response/app-response';
|
||||||
|
import { SystemLang } from '@/system/lang/system.lang';
|
||||||
|
@Injectable()
|
||||||
|
export class LogsService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Log)
|
||||||
|
readonly repo: Repository<Log>,
|
||||||
|
|
||||||
|
private readonly zulipService: ZulipService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async saveLog(data: SendLogDto) {
|
||||||
|
const result = await this.repo.save({
|
||||||
|
message: data.message,
|
||||||
|
type: data.type,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
throw new BadRequestException(
|
||||||
|
AppResponse.toResponse(null, {
|
||||||
|
message: SystemLang.getText('messages', 'error'),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.zulipService.sendMessageToTopic(
|
||||||
|
process.env.ZULIP_STREAMS_NAME,
|
||||||
|
process.env.ZULIP_TOPPIC_LOG_NAME,
|
||||||
|
`[${result.type.toUpperCase()}] - ${result.message}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return AppResponse.toResponse(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
import { Body, Controller, Get, Post } from '@nestjs/common';
|
import { Body, Controller, Get, Post } from '@nestjs/common';
|
||||||
import { MessagesService } from './messages.service';
|
|
||||||
import { CreateMessageDto } from './dtos/create-message.dto';
|
|
||||||
import { CreateBulkMessageDto } from './dtos/create-bulk-message.dto';
|
|
||||||
import { SendMessageDto } from './dtos/send-message.dto';
|
|
||||||
import { ReplyMessageDto } from './dtos/reply-message.dto';
|
|
||||||
import { Paginate, PaginateQuery } from 'nestjs-paginate';
|
import { Paginate, PaginateQuery } from 'nestjs-paginate';
|
||||||
|
import { CreateBulkMessageDto } from './dtos/create-bulk-message.dto';
|
||||||
|
import { CreateMessageDto } from './dtos/create-message.dto';
|
||||||
|
import { ReplyMessageDto } from './dtos/reply-message.dto';
|
||||||
|
import { SendLogDto } from './dtos/send-log.dto';
|
||||||
|
import { SendMessageDto } from './dtos/send-message.dto';
|
||||||
|
import { LogsService } from './logs.service';
|
||||||
|
import { MessagesService } from './messages.service';
|
||||||
|
|
||||||
@Controller('messages')
|
@Controller('messages')
|
||||||
export class MessagesController {
|
export class MessagesController {
|
||||||
constructor(private readonly service: MessagesService) {}
|
constructor(
|
||||||
|
private readonly service: MessagesService,
|
||||||
|
private readonly logService: LogsService,
|
||||||
|
) {}
|
||||||
|
|
||||||
@Get('')
|
@Get('')
|
||||||
index(@Paginate() query: PaginateQuery) {
|
index(@Paginate() query: PaginateQuery) {
|
||||||
|
|
@ -34,4 +39,9 @@ export class MessagesController {
|
||||||
replyMessage(@Body() data: ReplyMessageDto) {
|
replyMessage(@Body() data: ReplyMessageDto) {
|
||||||
return this.service.replyMessage(data);
|
return this.service.replyMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('logs')
|
||||||
|
saveLog(@Body() data: SendLogDto) {
|
||||||
|
return this.logService.saveLog(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,18 @@ import { MessagesService } from './messages.service';
|
||||||
import { MessagesListener } from './messages.listener';
|
import { MessagesListener } from './messages.listener';
|
||||||
import { ZulipService } from './zulip.service';
|
import { ZulipService } from './zulip.service';
|
||||||
import { HttpModule } from '@nestjs/axios';
|
import { HttpModule } from '@nestjs/axios';
|
||||||
|
import { Log } from '@/entities/log.entity';
|
||||||
|
import { LogsService } from './logs.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([Message, Conversation]), HttpModule],
|
imports: [TypeOrmModule.forFeature([Message, Conversation, Log]), HttpModule],
|
||||||
providers: [
|
providers: [
|
||||||
MessagesService,
|
MessagesService,
|
||||||
MessagesGateway,
|
MessagesGateway,
|
||||||
MessagesEventService,
|
MessagesEventService,
|
||||||
MessagesListener,
|
MessagesListener,
|
||||||
ZulipService,
|
ZulipService,
|
||||||
|
LogsService,
|
||||||
],
|
],
|
||||||
controllers: [MessagesController],
|
controllers: [MessagesController],
|
||||||
exports: [MessagesGateway, MessagesService, MessagesEventService],
|
exports: [MessagesGateway, MessagesService, MessagesEventService],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { Conversation } from '@/entities/conversation.entity';
|
import { Conversation } from '@/entities/conversation.entity';
|
||||||
import { Message } from '@/entities/message.entity';
|
import { Message } from '@/entities/message.entity';
|
||||||
|
import { formatTextIfValid } from '@/features/conver-message';
|
||||||
|
import { formatTimeAU } from '@/features/format-time-au';
|
||||||
import AppResponse from '@/system/filters/response/app-response';
|
import AppResponse from '@/system/filters/response/app-response';
|
||||||
import { SystemLang } from '@/system/lang/system.lang';
|
import { SystemLang } from '@/system/lang/system.lang';
|
||||||
import {
|
import {
|
||||||
|
|
@ -11,13 +13,12 @@ import {
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { paginate, PaginateQuery } from 'nestjs-paginate';
|
import { paginate, PaginateQuery } from 'nestjs-paginate';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
import { CreateBulkMessageDto } from './dtos/create-bulk-message.dto';
|
||||||
import { CreateMessageDto } from './dtos/create-message.dto';
|
import { CreateMessageDto } from './dtos/create-message.dto';
|
||||||
import { ReplyMessageDto } from './dtos/reply-message.dto';
|
import { ReplyMessageDto } from './dtos/reply-message.dto';
|
||||||
import { SendMessageDto } from './dtos/send-message.dto';
|
import { SendMessageDto } from './dtos/send-message.dto';
|
||||||
import { MessagesEventService } from './messages-event.service';
|
import { MessagesEventService } from './messages-event.service';
|
||||||
import { ZulipService } from './zulip.service';
|
import { ZulipService } from './zulip.service';
|
||||||
import { CreateBulkMessageDto } from './dtos/create-bulk-message.dto';
|
|
||||||
import { formatTimeAU } from '@/features/format-time-au';
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MessagesService {
|
export class MessagesService {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -26,7 +27,7 @@ export class MessagesService {
|
||||||
@InjectRepository(Conversation)
|
@InjectRepository(Conversation)
|
||||||
readonly conversationRepo: Repository<Conversation>,
|
readonly conversationRepo: Repository<Conversation>,
|
||||||
private readonly event: MessagesEventService,
|
private readonly event: MessagesEventService,
|
||||||
private readonly zulupService: ZulipService,
|
private readonly zulipService: ZulipService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async index(query: PaginateQuery) {
|
async index(query: PaginateQuery) {
|
||||||
|
|
@ -94,10 +95,10 @@ export class MessagesService {
|
||||||
|
|
||||||
const content = `** :rocket: ${result.name} sent - ${formatTimeAU(result.time_raw)}:**
|
const content = `** :rocket: ${result.name} sent - ${formatTimeAU(result.time_raw)}:**
|
||||||
\`\`\`
|
\`\`\`
|
||||||
${result.message}
|
${formatTextIfValid(result.message)}
|
||||||
\`\`\``;
|
\`\`\``;
|
||||||
|
|
||||||
await this.zulupService.sendMessageToTopic(
|
await this.zulipService.sendMessageToTopic(
|
||||||
process.env.ZULIP_STREAMS_NAME,
|
process.env.ZULIP_STREAMS_NAME,
|
||||||
conversation.name,
|
conversation.name,
|
||||||
content,
|
content,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue