fix something idon't no
This commit is contained in:
parent
d96884ab75
commit
23ea081757
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,23 +1,26 @@
|
||||||
import js from '@eslint/js'
|
import js from "@eslint/js";
|
||||||
import globals from 'globals'
|
import globals from "globals";
|
||||||
import reactHooks from 'eslint-plugin-react-hooks'
|
import reactHooks from "eslint-plugin-react-hooks";
|
||||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
import reactRefresh from "eslint-plugin-react-refresh";
|
||||||
import tseslint from 'typescript-eslint'
|
import tseslint from "typescript-eslint";
|
||||||
import { globalIgnores } from 'eslint/config'
|
import { globalIgnores } from "eslint/config";
|
||||||
|
|
||||||
export default tseslint.config([
|
export default tseslint.config([
|
||||||
globalIgnores(['dist']),
|
globalIgnores(["dist"]),
|
||||||
{
|
{
|
||||||
files: ['**/*.{ts,tsx}'],
|
files: ["**/*.{ts,tsx}"],
|
||||||
extends: [
|
extends: [
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
reactHooks.configs['recommended-latest'],
|
reactHooks.configs["recommended-latest"],
|
||||||
reactRefresh.configs.vite,
|
reactRefresh.configs.vite,
|
||||||
],
|
],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2020,
|
||||||
globals: globals.browser,
|
globals: globals.browser,
|
||||||
},
|
},
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-unused-vars": ["off"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
])
|
]);
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ function initSocket() {
|
||||||
|
|
||||||
// Socket.IO events
|
// Socket.IO events
|
||||||
socket.on("connect", () => {
|
socket.on("connect", () => {
|
||||||
console.log("✅ Socket.IO connected");
|
console.log("✅ Socket.IO connectedd");
|
||||||
broadcastToPorts({
|
broadcastToPorts({
|
||||||
type: "socket",
|
type: "socket",
|
||||||
event: "connect",
|
event: "connect",
|
||||||
|
|
@ -60,6 +60,7 @@ function initSocket() {
|
||||||
|
|
||||||
eventsToListen.forEach((event) => {
|
eventsToListen.forEach((event) => {
|
||||||
socket?.on(event, (data: any) => {
|
socket?.on(event, (data: any) => {
|
||||||
|
console.log({ event });
|
||||||
broadcastToPorts({
|
broadcastToPorts({
|
||||||
type: "socket",
|
type: "socket",
|
||||||
event,
|
event,
|
||||||
|
|
@ -143,7 +144,7 @@ chrome.alarms.onAlarm.addListener((alarm) => {
|
||||||
if (alarm.name === "reloadEvery4Hours") {
|
if (alarm.name === "reloadEvery4Hours") {
|
||||||
console.log(
|
console.log(
|
||||||
"🔄 Reloading all tabs (every 4 hours)",
|
"🔄 Reloading all tabs (every 4 hours)",
|
||||||
new Date().toLocaleString()
|
new Date().toLocaleString(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reload tất cả các tab đang mở
|
// Reload tất cả các tab đang mở
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,3 @@ contentService.startSyncConversations();
|
||||||
|
|
||||||
// AUTO SYNC MESAGE PREFIX (INTERNAL)
|
// AUTO SYNC MESAGE PREFIX (INTERNAL)
|
||||||
contentService.autoSyncConversationPrefixMessages();
|
contentService.autoSyncConversationPrefixMessages();
|
||||||
|
|
||||||
// setTimeout(async () => {
|
|
||||||
// const a = new TeamsChatService();
|
|
||||||
|
|
||||||
// const conversations = await a.handleGetConversations();
|
|
||||||
|
|
||||||
// console.log({ conversations });
|
|
||||||
// }, 10000);
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export class ContentService {
|
||||||
document,
|
document,
|
||||||
null,
|
null,
|
||||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||||
null
|
null,
|
||||||
).singleNodeValue;
|
).singleNodeValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ export class ContentService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _waitForMessagesToAppear(
|
private async _waitForMessagesToAppear(
|
||||||
timeoutMs = 10000
|
timeoutMs = 10000,
|
||||||
): Promise<HTMLElement[]> {
|
): Promise<HTMLElement[]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const chatPaneList = document.getElementById("chat-pane-list");
|
const chatPaneList = document.getElementById("chat-pane-list");
|
||||||
|
|
@ -114,46 +114,46 @@ export class ContentService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _waitForNewMessages(
|
// private async _waitForNewMessages(
|
||||||
existingItems: HTMLElement[],
|
// existingItems: HTMLElement[],
|
||||||
timeoutMs = 10000
|
// timeoutMs = 10000
|
||||||
): Promise<HTMLElement[]> {
|
// ): Promise<HTMLElement[]> {
|
||||||
return new Promise((resolve, reject) => {
|
// return new Promise((resolve, reject) => {
|
||||||
const chatPaneList = document.getElementById("chat-pane-list");
|
// const chatPaneList = document.getElementById("chat-pane-list");
|
||||||
|
|
||||||
if (!chatPaneList) {
|
// if (!chatPaneList) {
|
||||||
return reject(new Error("#chat-pane-list not found"));
|
// return reject(new Error("#chat-pane-list not found"));
|
||||||
}
|
// }
|
||||||
|
|
||||||
const getChildren = () =>
|
// const getChildren = () =>
|
||||||
Array.from(chatPaneList.children) as HTMLElement[];
|
// Array.from(chatPaneList.children) as HTMLElement[];
|
||||||
|
|
||||||
const existingSet = new Set(existingItems);
|
// const existingSet = new Set(existingItems);
|
||||||
|
|
||||||
let timeoutHandle: any = null;
|
// let timeoutHandle: any = null;
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
// const observer = new MutationObserver(() => {
|
||||||
const currentChildren = getChildren();
|
// const currentChildren = getChildren();
|
||||||
const newItems = currentChildren.filter((el) => !existingSet.has(el));
|
// const newItems = currentChildren.filter((el) => !existingSet.has(el));
|
||||||
|
|
||||||
if (newItems.length > 0) {
|
// if (newItems.length > 0) {
|
||||||
observer.disconnect();
|
// observer.disconnect();
|
||||||
if (timeoutHandle) clearTimeout(timeoutHandle);
|
// if (timeoutHandle) clearTimeout(timeoutHandle);
|
||||||
resolve(newItems);
|
// resolve(newItems);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
observer.observe(chatPaneList, {
|
// observer.observe(chatPaneList, {
|
||||||
childList: true,
|
// childList: true,
|
||||||
subtree: true,
|
// subtree: true,
|
||||||
});
|
// });
|
||||||
|
|
||||||
timeoutHandle = setTimeout(() => {
|
// timeoutHandle = setTimeout(() => {
|
||||||
observer.disconnect();
|
// observer.disconnect();
|
||||||
reject(new Error("Timeout waiting for new messages"));
|
// reject(new Error("Timeout waiting for new messages"));
|
||||||
}, timeoutMs);
|
// }, timeoutMs);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
private async _waitToloadMessages(stableTime = 300): Promise<any> {
|
private async _waitToloadMessages(stableTime = 300): Promise<any> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
|
@ -189,24 +189,24 @@ export class ContentService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getTypeGeo() {
|
// private _getTypeGeo() {
|
||||||
const el = document.querySelector(".ck-placeholder");
|
// const el = document.querySelector(".ck-placeholder");
|
||||||
|
|
||||||
if (el) {
|
// if (el) {
|
||||||
const rect = el.getBoundingClientRect();
|
// const rect = el.getBoundingClientRect();
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
top: rect.top,
|
// top: rect.top,
|
||||||
left: rect.left,
|
// left: rect.left,
|
||||||
right: rect.right,
|
// right: rect.right,
|
||||||
bottom: rect.bottom,
|
// bottom: rect.bottom,
|
||||||
width: rect.width,
|
// width: rect.width,
|
||||||
height: rect.height,
|
// height: rect.height,
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
private async _rightClickMessage(date_time: number) {
|
private async _rightClickMessage(date_time: number) {
|
||||||
const selector = this.service.elTags.container_chat;
|
const selector = this.service.elTags.container_chat;
|
||||||
|
|
@ -234,7 +234,7 @@ export class ContentService {
|
||||||
|
|
||||||
scrollContainer.scrollTop = Math.max(
|
scrollContainer.scrollTop = Math.max(
|
||||||
0,
|
0,
|
||||||
scrollContainer.scrollTop - scrollStep
|
scrollContainer.scrollTop - scrollStep,
|
||||||
);
|
);
|
||||||
await delay(200); // Đợi scroll và DOM render lại
|
await delay(200); // Đợi scroll và DOM render lại
|
||||||
|
|
||||||
|
|
@ -385,6 +385,8 @@ export class ContentService {
|
||||||
|
|
||||||
initialMessages = await this._waitForMessagesToAppear(); // Đợi có item đầu tiên
|
initialMessages = await this._waitForMessagesToAppear(); // Đợi có item đầu tiên
|
||||||
await this._waitToloadMessages();
|
await this._waitToloadMessages();
|
||||||
|
|
||||||
|
console.log({ initialMessages });
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._clickIfExists(this.service.elTags.close_reply_btn);
|
await this._clickIfExists(this.service.elTags.close_reply_btn);
|
||||||
|
|
@ -392,7 +394,7 @@ export class ContentService {
|
||||||
await this._rightClickMessage(time);
|
await this._rightClickMessage(time);
|
||||||
|
|
||||||
const replyBtn: any = document.querySelector(
|
const replyBtn: any = document.querySelector(
|
||||||
this.service.elTags.reply_btn
|
this.service.elTags.reply_btn,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (replyBtn) {
|
if (replyBtn) {
|
||||||
|
|
@ -410,7 +412,7 @@ export class ContentService {
|
||||||
fixedHeightChatInput(retry = 20, interval = 2000) {
|
fixedHeightChatInput(retry = 20, interval = 2000) {
|
||||||
const tryFind = () => {
|
const tryFind = () => {
|
||||||
const el = document.querySelector(
|
const el = document.querySelector(
|
||||||
this.service.elTags.chat_input
|
this.service.elTags.chat_input,
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
|
|
@ -419,7 +421,7 @@ export class ContentService {
|
||||||
} else if (retry > 0) {
|
} else if (retry > 0) {
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => this.fixedHeightChatInput(retry - 1, interval),
|
() => this.fixedHeightChatInput(retry - 1, interval),
|
||||||
interval
|
interval,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.warn("✘ Element not found with provided XPath after retries");
|
console.warn("✘ Element not found with provided XPath after retries");
|
||||||
|
|
@ -448,7 +450,7 @@ export class ContentService {
|
||||||
// Lấy attribute aria-labelledby
|
// Lấy attribute aria-labelledby
|
||||||
const ariaValue = document
|
const ariaValue = document
|
||||||
.querySelector(
|
.querySelector(
|
||||||
'[aria-labelledby^="cn-normal-notification-main-content-"]'
|
'[aria-labelledby^="cn-normal-notification-main-content-"]',
|
||||||
)
|
)
|
||||||
?.getAttribute("aria-labelledby");
|
?.getAttribute("aria-labelledby");
|
||||||
|
|
||||||
|
|
@ -478,7 +480,7 @@ export class ContentService {
|
||||||
|
|
||||||
if (!roomId) {
|
if (!roomId) {
|
||||||
console.warn(
|
console.warn(
|
||||||
"[Monitor] Could not extract room_id from aria-labelledby"
|
"[Monitor] Could not extract room_id from aria-labelledby",
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -563,7 +565,7 @@ export class ContentService {
|
||||||
() => {
|
() => {
|
||||||
this.getConversations();
|
this.getConversations();
|
||||||
},
|
},
|
||||||
120000
|
120000,
|
||||||
);
|
);
|
||||||
console.log("✅ startSyncConversations running");
|
console.log("✅ startSyncConversations running");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -572,13 +574,73 @@ export class ContentService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interval chạy, chỉ add task vào queue
|
// Interval chạy, chỉ add task vào queue
|
||||||
autoSyncConversationPrefixMessages() {
|
// autoSyncConversationPrefixMessages() {
|
||||||
(window as any)._chatIntervals = (window as any)?._chatIntervals || {};
|
// (window as any)._chatIntervals = (window as any)?._chatIntervals || {};
|
||||||
|
|
||||||
|
// if (!(window as any)._chatIntervals.syncPrefixMessages) {
|
||||||
|
// (window as any)._chatIntervals.syncPrefixMessages = (
|
||||||
|
// window as any
|
||||||
|
// ).setInterval(() => {
|
||||||
|
// (window as any)["is_sync_conversation_prefix"] = true;
|
||||||
|
|
||||||
|
// 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(5000);
|
||||||
|
// const currentRoom = this.service.getCurrentRoomInfo();
|
||||||
|
// if (!currentRoom || currentRoom.room_id !== chat.id) return;
|
||||||
|
// const filesMessages = this.filesMessages();
|
||||||
|
// if (filesMessages.length) {
|
||||||
|
// await this.clickToRefreshToken(filesMessages);
|
||||||
|
// }
|
||||||
|
// const messages = this.service.extractAllMessages();
|
||||||
|
// await messageApi.createAndSendToZulip(messages);
|
||||||
|
// await delay(5000);
|
||||||
|
// }
|
||||||
|
// } catch (err) {
|
||||||
|
// console.error(
|
||||||
|
// "❌ autoSyncConversationPrefixMessages error:",
|
||||||
|
// (err as any)?.message,
|
||||||
|
// );
|
||||||
|
// messageApi.sendLog({
|
||||||
|
// type: "error",
|
||||||
|
// message: `auto_to_sync_conersations: ${(err as any)?.message}`,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }, 60000);
|
||||||
|
|
||||||
|
// console.log("✅ autoSyncConversationPrefixMessages running with PQueue");
|
||||||
|
// } else {
|
||||||
|
// console.log("ℹ️ autoSyncConversationPrefixMessages already running");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
autoSyncConversationPrefixMessages() {
|
||||||
|
(window as any)._chatIntervals = (window as any)._chatIntervals || {};
|
||||||
|
|
||||||
|
if ((window as any)._chatIntervals.syncPrefixMessages) {
|
||||||
|
console.log("autoSyncConversationPrefixMessages already running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isRunning = false;
|
||||||
|
|
||||||
|
(window as any)._chatIntervals.syncPrefixMessages = window.setInterval(
|
||||||
|
() => {
|
||||||
|
if (isRunning) {
|
||||||
|
console.log("syncPrefixMessages skipped (still running)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRunning = true;
|
||||||
|
|
||||||
if (!(window as any)._chatIntervals.syncPrefixMessages) {
|
|
||||||
(window as any)._chatIntervals.syncPrefixMessages = (
|
|
||||||
window as any
|
|
||||||
).setInterval(() => {
|
|
||||||
queue.add(async () => {
|
queue.add(async () => {
|
||||||
try {
|
try {
|
||||||
const { data } =
|
const { data } =
|
||||||
|
|
@ -586,7 +648,7 @@ export class ContentService {
|
||||||
data: ChatItem[];
|
data: ChatItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!data) return;
|
if (!data?.length) return;
|
||||||
|
|
||||||
for (const chat of data) {
|
for (const chat of data) {
|
||||||
this._clickToConversation(chat.id as string);
|
this._clickToConversation(chat.id as string);
|
||||||
|
|
@ -594,37 +656,39 @@ export class ContentService {
|
||||||
await delay(5000);
|
await delay(5000);
|
||||||
|
|
||||||
const currentRoom = this.service.getCurrentRoomInfo();
|
const currentRoom = this.service.getCurrentRoomInfo();
|
||||||
|
if (!currentRoom || currentRoom.room_id !== chat.id) {
|
||||||
if (!currentRoom || currentRoom.room_id !== chat.id) return;
|
console.warn("room mismatch, skip:", chat.id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const filesMessages = this.filesMessages();
|
const filesMessages = this.filesMessages();
|
||||||
|
|
||||||
if (filesMessages.length) {
|
if (filesMessages.length) {
|
||||||
await this.clickToRefreshToken(filesMessages);
|
await this.clickToRefreshToken(filesMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = this.service.extractAllMessages();
|
const messages = this.service.extractAllMessages();
|
||||||
|
|
||||||
await messageApi.createAndSendToZulip(messages);
|
await messageApi.createAndSendToZulip(messages);
|
||||||
|
|
||||||
await delay(5000);
|
await delay(5000);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
"❌ autoSyncConversationPrefixMessages error:",
|
"autoSyncConversationPrefixMessages error:",
|
||||||
(err as any)?.message
|
(err as any)?.message,
|
||||||
);
|
);
|
||||||
|
|
||||||
messageApi.sendLog({
|
messageApi.sendLog({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: `auto_to_sync_conersations: ${(err as any)?.message}`,
|
message: `auto_to_sync_conersations: ${(err as any)?.message}`,
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
isRunning = false; // 🔑 cực kỳ quan trọng
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 40000);
|
},
|
||||||
|
60000,
|
||||||
|
);
|
||||||
|
|
||||||
console.log("✅ autoSyncConversationPrefixMessages running with PQueue");
|
console.log("autoSyncConversationPrefixMessages running safely");
|
||||||
} else {
|
|
||||||
console.log("ℹ️ autoSyncConversationPrefixMessages already running");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,13 @@
|
||||||
import { messageApi } from "@/api/message-api.service";
|
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;
|
||||||
public initialHistories: IMessage[] = [];
|
public initialHistories: IMessage[] = [];
|
||||||
|
|
||||||
public elTags = {
|
public elTags = {
|
||||||
container_scroll:
|
container_scroll: '//*[@data-testid="simple-collab-rail"]',
|
||||||
"/html/body/div[1]/div/div/div/div[5]/div[1]/div[1]/div[2]/div[1]/div[1]/div",
|
conatainer_conversations: '//*[@data-testid="simple-collab-dnd-rail"]',
|
||||||
conatainer_conversations:
|
|
||||||
"/html/body/div[1]/div/div/div/div[5]/div[1]/div[1]/div[2]/div[1]/div[1]/div/div[1]",
|
|
||||||
container_chat: '[data-testid="message-wrapper"]',
|
container_chat: '[data-testid="message-wrapper"]',
|
||||||
|
|
||||||
root_id: '[aria-selected="true"] [id^="chat-list-item"]',
|
root_id: '[aria-selected="true"] [id^="chat-list-item"]',
|
||||||
|
|
@ -24,13 +22,13 @@ export class TeamsChatService {
|
||||||
private _getImageFormEl(el: HTMLElement): HTMLImageElement[] {
|
private _getImageFormEl(el: HTMLElement): HTMLImageElement[] {
|
||||||
// Tìm tất cả img có data-gallery-src trong el
|
// Tìm tất cả img có data-gallery-src trong el
|
||||||
let sharedImages = Array.from(
|
let sharedImages = Array.from(
|
||||||
el.querySelectorAll("img[data-gallery-src]")
|
el.querySelectorAll("img[data-gallery-src]"),
|
||||||
) as HTMLImageElement[];
|
) as HTMLImageElement[];
|
||||||
|
|
||||||
// Nếu không tìm thấy thì thử tìm trong parentElement
|
// Nếu không tìm thấy thì thử tìm trong parentElement
|
||||||
if (sharedImages.length === 0 && el.parentElement) {
|
if (sharedImages.length === 0 && el.parentElement) {
|
||||||
sharedImages = Array.from(
|
sharedImages = Array.from(
|
||||||
el.parentElement.querySelectorAll("img[data-gallery-src]")
|
el.parentElement.querySelectorAll("img[data-gallery-src]"),
|
||||||
) as HTMLImageElement[];
|
) as HTMLImageElement[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,7 +172,7 @@ export class TeamsChatService {
|
||||||
|
|
||||||
// Lấy emoji
|
// Lấy emoji
|
||||||
const emojiImgs = Array.from(
|
const emojiImgs = Array.from(
|
||||||
el.querySelectorAll("img[itemtype]")
|
el.querySelectorAll("img[itemtype]"),
|
||||||
) as HTMLImageElement[];
|
) as HTMLImageElement[];
|
||||||
const emojiAlts = emojiImgs
|
const emojiAlts = emojiImgs
|
||||||
.map((img) => img.getAttribute("alt") || "")
|
.map((img) => img.getAttribute("alt") || "")
|
||||||
|
|
@ -189,11 +187,11 @@ export class TeamsChatService {
|
||||||
|
|
||||||
public parseMessageElement(el: Element, isMine = false): IMessage | null {
|
public parseMessageElement(el: Element, isMine = false): IMessage | null {
|
||||||
const timestampEl = el.querySelector(
|
const timestampEl = el.querySelector(
|
||||||
isMine ? ".fui-ChatMyMessage__timestamp" : ".fui-ChatMessage__timestamp"
|
isMine ? ".fui-ChatMyMessage__timestamp" : ".fui-ChatMessage__timestamp",
|
||||||
) as HTMLElement | null;
|
) as HTMLElement | null;
|
||||||
|
|
||||||
const authorEl = el.querySelector(
|
const authorEl = el.querySelector(
|
||||||
isMine ? ".fui-ChatMyMessage__author" : ".fui-ChatMessage__author"
|
isMine ? ".fui-ChatMyMessage__author" : ".fui-ChatMessage__author",
|
||||||
) as HTMLElement | null;
|
) as HTMLElement | null;
|
||||||
|
|
||||||
if (!timestampEl) return null;
|
if (!timestampEl) return null;
|
||||||
|
|
@ -207,7 +205,7 @@ export class TeamsChatService {
|
||||||
: Number(timestampEl.id.replace("timestamp-", ""));
|
: Number(timestampEl.id.replace("timestamp-", ""));
|
||||||
|
|
||||||
const contentEl = document.querySelector(
|
const contentEl = document.querySelector(
|
||||||
`#content-${dateTime}`
|
`#content-${dateTime}`,
|
||||||
) as HTMLElement | null;
|
) as HTMLElement | null;
|
||||||
|
|
||||||
(contentEl as any)["date_time"] = dateTime;
|
(contentEl as any)["date_time"] = dateTime;
|
||||||
|
|
@ -242,13 +240,13 @@ export class TeamsChatService {
|
||||||
|
|
||||||
extractAllMessages(): IMessage[] {
|
extractAllMessages(): IMessage[] {
|
||||||
const myMessages: IMessage[] = Array.from(
|
const myMessages: IMessage[] = Array.from(
|
||||||
document.querySelectorAll(".fui-ChatMyMessage")
|
document.querySelectorAll(".fui-ChatMyMessage"),
|
||||||
)
|
)
|
||||||
.map((el) => this.parseMessageElement(el, true))
|
.map((el) => this.parseMessageElement(el, true))
|
||||||
.filter((msg): msg is IMessage => msg !== null);
|
.filter((msg): msg is IMessage => msg !== null);
|
||||||
|
|
||||||
const otherMessages: IMessage[] = Array.from(
|
const otherMessages: IMessage[] = Array.from(
|
||||||
document.querySelectorAll(".fui-ChatMessage")
|
document.querySelectorAll(".fui-ChatMessage"),
|
||||||
)
|
)
|
||||||
.map((el) => this.parseMessageElement(el, false))
|
.map((el) => this.parseMessageElement(el, false))
|
||||||
.filter((msg): msg is IMessage => msg !== null);
|
.filter((msg): msg is IMessage => msg !== null);
|
||||||
|
|
@ -267,7 +265,7 @@ export class TeamsChatService {
|
||||||
const allMessages = this.extractAllMessages();
|
const allMessages = this.extractAllMessages();
|
||||||
|
|
||||||
const lastIndex = allMessages.findIndex(
|
const lastIndex = allMessages.findIndex(
|
||||||
(msg) => msg.time === this.lastMessage?.time
|
(msg) => msg.time === this.lastMessage?.time,
|
||||||
);
|
);
|
||||||
|
|
||||||
const newMessages = allMessages.slice(lastIndex + 1);
|
const newMessages = allMessages.slice(lastIndex + 1);
|
||||||
|
|
@ -302,14 +300,14 @@ export class TeamsChatService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _getConversationsInfo(
|
private async _getConversationsInfo(
|
||||||
xpath: string = this.elTags.conatainer_conversations
|
xpath: string = this.elTags.conatainer_conversations,
|
||||||
): Promise<ChatItem[]> {
|
): Promise<ChatItem[]> {
|
||||||
const result = document.evaluate(
|
const result = document.evaluate(
|
||||||
xpath,
|
xpath,
|
||||||
document,
|
document,
|
||||||
null,
|
null,
|
||||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||||
null
|
null,
|
||||||
).singleNodeValue as HTMLElement | null;
|
).singleNodeValue as HTMLElement | null;
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|
@ -321,15 +319,15 @@ export class TeamsChatService {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lọc phần tử con có role="none"
|
const chatItems = Array.from(
|
||||||
const matchedChildren = Array.from(result.children).filter(
|
result.querySelectorAll('[data-item-type="chat"]'),
|
||||||
(child: Element) => {
|
|
||||||
return child.getAttribute("role") === "none";
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const data: ChatItem[] = matchedChildren.map((child: Element): ChatItem => {
|
const data: ChatItem[] = chatItems.map((child: Element): ChatItem => {
|
||||||
const id = child.id || null;
|
const treeItemValue = child.getAttribute("data-fui-tree-item-value") || "";
|
||||||
|
const lastSegment = treeItemValue.split("/").pop() || "";
|
||||||
|
const id = lastSegment.includes("|") ? lastSegment.split("|")[1] : null;
|
||||||
|
|
||||||
const titleId = `title-chat-list-item_${id}`;
|
const titleId = `title-chat-list-item_${id}`;
|
||||||
const titleElement = document.getElementById(titleId);
|
const titleElement = document.getElementById(titleId);
|
||||||
const spanText = titleElement?.innerText || null;
|
const spanText = titleElement?.innerText || null;
|
||||||
|
|
@ -360,7 +358,7 @@ export class TeamsChatService {
|
||||||
maxStableRounds?: number; // Số vòng scroll không thay đổi trước khi dừng
|
maxStableRounds?: number; // Số vòng scroll không thay đổi trước khi dừng
|
||||||
delay?: number; // Thời gian chờ giữa mỗi lần scroll (ms)
|
delay?: number; // Thời gian chờ giữa mỗi lần scroll (ms)
|
||||||
maxScrolls?: number; // Giới hạn số lần scroll tối đa
|
maxScrolls?: number; // Giới hạn số lần scroll tối đa
|
||||||
}
|
},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {
|
const {
|
||||||
maxStableRounds = 5,
|
maxStableRounds = 5,
|
||||||
|
|
@ -373,7 +371,7 @@ export class TeamsChatService {
|
||||||
document,
|
document,
|
||||||
null,
|
null,
|
||||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||||
null
|
null,
|
||||||
).singleNodeValue as HTMLElement | null;
|
).singleNodeValue as HTMLElement | null;
|
||||||
|
|
||||||
if (!container) {
|
if (!container) {
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,13 @@ import * as fs from 'fs';
|
||||||
import { SocketIoAdapter } from './socket-adapter';
|
import { SocketIoAdapter } from './socket-adapter';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const httpsOptions = {
|
// const httpsOptions = {
|
||||||
key: fs.readFileSync('ssl/localhost+1-key.pem'),
|
// key: fs.readFileSync('ssl/localhost+1-key.pem'),
|
||||||
cert: fs.readFileSync('ssl/localhost+1.pem'),
|
// cert: fs.readFileSync('ssl/localhost+1.pem'),
|
||||||
};
|
// };
|
||||||
|
|
||||||
const app = await NestFactory.create(AppModule, { httpsOptions });
|
const app = await NestFactory.create(AppModule);
|
||||||
|
// const app = await NestFactory.create(AppModule, { httpsOptions });
|
||||||
|
|
||||||
const prefix_version = process.env.PREFIX_VERSION;
|
const prefix_version = process.env.PREFIX_VERSION;
|
||||||
|
|
||||||
|
|
@ -54,12 +55,14 @@ async function bootstrap() {
|
||||||
|
|
||||||
useContainer(app.select(AppModule), { fallbackOnErrors: true });
|
useContainer(app.select(AppModule), { fallbackOnErrors: true });
|
||||||
|
|
||||||
|
app.useWebSocketAdapter(new SocketIoAdapter(app));
|
||||||
|
|
||||||
await app.listen(Number(process.env.APP_API_PORT));
|
await app.listen(Number(process.env.APP_API_PORT));
|
||||||
|
|
||||||
// ===== App cho WebSocket (WS hoặc WSS) =====
|
// // ===== App cho WebSocket (WS hoặc WSS) =====
|
||||||
const wsApp = await NestFactory.create(AppModule);
|
// const wsApp = await NestFactory.create(AppModule);
|
||||||
wsApp.useWebSocketAdapter(new SocketIoAdapter(wsApp));
|
// wsApp.useWebSocketAdapter(new SocketIoAdapter(wsApp));
|
||||||
await wsApp.listen(Number(process.env.APP_SOCKET_PORT));
|
// await wsApp.listen(Number(process.env.APP_SOCKET_PORT));
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ export class MessagesGateway implements OnGatewayConnection {
|
||||||
|
|
||||||
for (const event of eventsToForward) {
|
for (const event of eventsToForward) {
|
||||||
this.event.on(event, (data) => {
|
this.event.on(event, (data) => {
|
||||||
|
console.log({ event, data });
|
||||||
this.server.emit(event, data);
|
this.server.emit(event, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue