[repo] reorganization
This commit is contained in:
36
src/logger/formatter.js
Normal file
36
src/logger/formatter.js
Normal file
@@ -0,0 +1,36 @@
|
||||
// ── Paleta ANSI ──────────────────────────────────────────────
|
||||
export const c = {
|
||||
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
|
||||
green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m",
|
||||
red: "\x1b[31m", gray: "\x1b[90m", white: "\x1b[37m",
|
||||
blue: "\x1b[34m", magenta: "\x1b[35m",
|
||||
};
|
||||
|
||||
export const SEP = `${c.gray}${"─".repeat(52)}${c.reset}`;
|
||||
|
||||
export const now = () =>
|
||||
new Date().toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "medium" });
|
||||
|
||||
export const formatType = (type) => ({
|
||||
sticker: `${c.magenta}sticker${c.reset}`,
|
||||
image: `${c.cyan}imagem${c.reset}`,
|
||||
video: `${c.cyan}vídeo${c.reset}`,
|
||||
audio: `${c.cyan}áudio${c.reset}`,
|
||||
ptt: `${c.cyan}áudio${c.reset}`,
|
||||
document: `${c.cyan}arquivo${c.reset}`,
|
||||
chat: `${c.white}texto${c.reset}`,
|
||||
}[type] ?? `${c.gray}${type}${c.reset}`);
|
||||
|
||||
export const formatContext = (chatName, isGroup) =>
|
||||
isGroup
|
||||
? `${c.bold}${chatName}${c.reset} ${c.dim}(grupo)${c.reset}`
|
||||
: `${c.bold}${chatName}${c.reset} ${c.dim}(privado)${c.reset}`;
|
||||
|
||||
export const formatBody = (body, isCommand) =>
|
||||
body?.trim()
|
||||
? `${isCommand ? c.yellow : c.green}"${body.length > 200 ? body.slice(0, 200) + "..." : body}"${c.reset}`
|
||||
: `${c.dim}<mídia>${c.reset}`;
|
||||
|
||||
export const formatReply = (quotedName, quotedNumber, quotedPreview) =>
|
||||
`\n${c.gray} ↩ Para: ${c.reset}${c.white}${quotedName}${c.reset} ${c.dim}+${quotedNumber}${c.reset}` +
|
||||
`\n${c.gray} ↩ Msg: ${c.reset}${c.dim}${quotedPreview}${c.reset}`;
|
||||
53
src/logger/logger.js
Normal file
53
src/logger/logger.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
c, SEP, now,
|
||||
formatType, formatContext, formatBody, formatReply,
|
||||
} from "./formatter.js";
|
||||
|
||||
/**
|
||||
* Logger central do ManyBot.
|
||||
* Cada método lida apenas com saída — sem lógica de negócio ou I/O externo.
|
||||
*/
|
||||
export const logger = {
|
||||
info: (...a) => console.log(`${SEP}\n${c.gray}[${now()}]${c.reset} ${c.cyan}INFO ${c.reset}`, ...a),
|
||||
success: (...a) => console.log(`${c.gray}[${now()}]${c.reset} ${c.green}OK ${c.reset}`, ...a),
|
||||
warn: (...a) => console.log(`${c.gray}[${now()}]${c.reset} ${c.yellow}WARN ${c.reset}`, ...a),
|
||||
error: (...a) => console.log(`${c.gray}[${now()}]${c.reset} ${c.red}ERROR ${c.reset}`, ...a),
|
||||
|
||||
/**
|
||||
* Loga uma mensagem recebida a partir de um contexto já resolvido.
|
||||
* @param {import("./messageContext.js").MessageContext} ctx
|
||||
*/
|
||||
msg(ctx) {
|
||||
const { chatName, isGroup, senderName, senderNumber, type, body, isCommand, quoted } = ctx;
|
||||
|
||||
const typeLabel = formatType(type);
|
||||
const context = formatContext(chatName, isGroup);
|
||||
const bodyText = formatBody(body, isCommand);
|
||||
const replyLine = quoted
|
||||
? formatReply(quoted.name, quoted.number, quoted.preview)
|
||||
: "";
|
||||
|
||||
console.log(
|
||||
`${SEP}\n` +
|
||||
`${c.gray}[${now()}]${c.reset} ${c.cyan}MSG ${c.reset}${context}\n` +
|
||||
`${c.gray} De: ${c.reset}${c.white}${senderName}${c.reset} ${c.dim}+${senderNumber}${c.reset}\n` +
|
||||
`${c.gray} Tipo: ${c.reset}${typeLabel}${isCommand ? ` ${c.yellow}(bot)${c.reset}` : ""}\n` +
|
||||
`${c.gray} Text: ${c.reset}${bodyText}` +
|
||||
replyLine
|
||||
);
|
||||
},
|
||||
|
||||
cmd: (cmd, extra = "") =>
|
||||
console.log(
|
||||
`${c.gray}[${now()}]${c.reset} ${c.yellow}CMD ${c.reset}` +
|
||||
`${c.bold}${cmd}${c.reset}` +
|
||||
(extra ? ` ${c.dim}${extra}${c.reset}` : "")
|
||||
),
|
||||
|
||||
done: (cmd, detail = "") =>
|
||||
console.log(
|
||||
`${c.gray}[${now()}]${c.reset} ${c.green}DONE ${c.reset}` +
|
||||
`${c.dim}${cmd}${c.reset}` +
|
||||
(detail ? ` — ${detail}` : "")
|
||||
),
|
||||
};
|
||||
83
src/logger/messageContext.js
Normal file
83
src/logger/messageContext.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import client from "../client/whatsappClient.js";
|
||||
|
||||
/**
|
||||
* Extrai o número limpo de uma mensagem.
|
||||
* @param {import("whatsapp-web.js").Message} msg
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
export async function getNumber(msg) {
|
||||
if (msg.fromMe) return String(msg.from).split("@")[0];
|
||||
const contact = await msg.getContact();
|
||||
return contact.number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Monta o contexto completo de uma mensagem para logging.
|
||||
* Resolve contato, quoted message e metadados do chat.
|
||||
*
|
||||
* @param {import("whatsapp-web.js").Message} msg
|
||||
* @param {import("whatsapp-web.js").Chat} chat
|
||||
* @param {string} botPrefix
|
||||
* @returns {Promise<MessageContext>}
|
||||
*
|
||||
* @typedef {Object} MessageContext
|
||||
* @property {string} chatName
|
||||
* @property {string} chatId
|
||||
* @property {boolean} isGroup
|
||||
* @property {string} senderName
|
||||
* @property {string} senderNumber
|
||||
* @property {string} type
|
||||
* @property {string} body
|
||||
* @property {boolean} isCommand
|
||||
* @property {{ name: string, number: string, preview: string } | null} quoted
|
||||
*/
|
||||
export async function buildMessageContext(msg, chat, botPrefix) {
|
||||
const chatId = chat.id._serialized;
|
||||
const isGroup = /@g\.us$/.test(chatId);
|
||||
const number = await getNumber(msg);
|
||||
const name = msg._data?.notifyName || String(msg.from).replace(/(:\d+)?@.*$/, "");
|
||||
|
||||
const quoted = await resolveQuotedMessage(msg);
|
||||
|
||||
return {
|
||||
chatName: chat.name || chat.id.user,
|
||||
chatId,
|
||||
isGroup,
|
||||
senderName: name,
|
||||
senderNumber: number,
|
||||
type: msg?.type || "text",
|
||||
body: msg.body,
|
||||
isCommand: !!msg.body?.trimStart().startsWith(botPrefix),
|
||||
quoted,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve os dados da mensagem citada, se existir.
|
||||
* Retorna null em caso de erro ou ausência.
|
||||
*
|
||||
* @param {import("whatsapp-web.js").Message} msg
|
||||
* @returns {Promise<{ name: string, number: string, preview: string } | null>}
|
||||
*/
|
||||
async function resolveQuotedMessage(msg) {
|
||||
if (!msg?.hasQuotedMsg) return null;
|
||||
|
||||
try {
|
||||
const quoted = await msg.getQuotedMessage();
|
||||
const quotedNumber = String(quoted.from).split("@")[0];
|
||||
|
||||
let quotedName = quotedNumber;
|
||||
try {
|
||||
const contact = await client.getContactById(quoted.from);
|
||||
quotedName = contact?.pushname || contact?.formattedName || quotedNumber;
|
||||
} catch { /* contato não encontrado — usa o número */ }
|
||||
|
||||
const quotedPreview = quoted.body?.trim()
|
||||
? `"${quoted.body.length > 80 ? quoted.body.slice(0, 80) + "…" : quoted.body}"`
|
||||
: `<${quoted.type}>`;
|
||||
|
||||
return { name: quotedName, number: quotedNumber, preview: quotedPreview };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user