Package Exports
- newchat.js
- newchat.js/src/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (newchat.js) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
newchat.js
Unofficial Node.js API wrapper for newchat.vn
Xây dựng chatbot cho newchat.vn bằng Node.js.
Lấy cảm hứng từ fca-unofficial.
⚠️ Disclaimer: Đây là thư viện không chính thức (unofficial), không được phát triển hay bảo trì bởi đội ngũ newchat.vn. Sử dụng trên tinh thần tự chịu trách nhiệm. API có thể thay đổi bất kỳ lúc nào mà không có thông báo trước.
Mục lục
- Cài đặt
- Quickstart — Echo Bot
- AppState — Không cần login lại
- API Reference
- Error Handling
- Yêu cầu hệ thống
- License
Cài đặt
npm install newchat.jsYêu cầu Node.js >= 18.
Quickstart — Echo Bot
const login = require('newchat.js');
const { loadAppState } = require('newchat.js');
const fs = require('fs');
const STATE_FILE = './appstate.json';
async function start() {
let api;
// Tái sử dụng session đã lưu, không cần login lại
if (fs.existsSync(STATE_FILE)) {
const saved = JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
api = await loadAppState(saved);
} else {
api = await login('your@email.com', 'yourpassword');
fs.writeFileSync(STATE_FILE, JSON.stringify(api.getAppState(), null, 2));
}
const me = await api.getMyProfile();
console.log(`✅ Bot đang chạy: ${me.fullName} (@${me.username})`);
const { stopListening } = api.listen((err, event) => {
if (err) return console.error('[ERROR]', err.message);
if (event.type === 'message') {
const { threadID, body, senderID } = event.data;
// Bỏ qua tin nhắn của chính bot
if (senderID === me._id) return;
console.log(`[${threadID}] ${senderID}: ${body}`);
api.sendMessage(threadID, `Echo: ${body}`).catch(console.error);
api.markAsRead(threadID).catch(console.error);
}
});
process.on('SIGINT', () => {
stopListening();
console.log('\nBot đã dừng.');
process.exit(0);
});
}
start().catch(console.error);AppState — Không cần login lại
Sau lần đăng nhập đầu tiên, lưu token vào file. Những lần sau dùng loadAppState() để khởi động nhanh — không tốn request login.
// Lần đầu: đăng nhập và lưu
const api = await login('your@email.com', 'yourpassword');
fs.writeFileSync('appstate.json', JSON.stringify(api.getAppState(), null, 2));
// Lần sau: load lại không cần login
const { loadAppState } = require('newchat.js');
const api = await loadAppState(JSON.parse(fs.readFileSync('appstate.json')));Token có hiệu lực ~30 ngày. Khi hết hạn sẽ nhận lỗi
Session expired, please re-login.
API Reference
login()
Đăng nhập bằng email + password. Trả về api object.
const login = require('newchat.js');
const api = await login(email, password);| Tham số | Kiểu | Mô tả |
|---|---|---|
email |
string |
Email tài khoản newchat.vn |
password |
string |
Mật khẩu tài khoản |
Returns: Promise<api>
loadAppState()
Khôi phục session từ AppState đã lưu. Bỏ qua bước đăng nhập.
const { loadAppState } = require('newchat.js');
const api = await loadAppState({ token: '...' });| Tham số | Kiểu | Mô tả |
|---|---|---|
appState.token |
string |
JWT token từ getAppState() |
Returns: Promise<api>
getAppState()
Trả về AppState hiện tại để lưu file.
const appState = api.getAppState();
// { token: 'eyJhbGci...' }
fs.writeFileSync('appstate.json', JSON.stringify(appState, null, 2));Returns: { token: string }
listen()
Lắng nghe tin nhắn và sự kiện realtime qua WebSocket (Socket.IO + MessagePack).
const { stopListening } = api.listen((err, event) => {
if (err) return console.error(err.message);
if (event.type === 'message') {
const { threadID, body, senderID, attachments } = event.data;
console.log(`${senderID}: ${body}`);
}
});
// Dừng listen
stopListening();Event types:
event.type |
Mô tả | event.data |
|---|---|---|
message |
Tin nhắn mới | Xem bên dưới |
typing |
Người dùng đang gõ | { channelId, userId } |
read |
Tin nhắn đã được đọc | { channelId, userId } |
notification |
Thông báo hệ thống | { ... } |
event |
Event khác (debug) | { type, data } |
Cấu trúc event.data khi type === 'message':
{
messageID: '699c774211e0...',
threadID: '691c4437a506...', // channelId
senderID: '691c410089308d...',
senderName: 'Nguyễn Văn A',
body: 'hello world', // plain text (HTML đã strip)
bodyHtml: '<p>hello world</p>',
attachments: [],
createdAt: '2026-02-23T15:02:52.695Z',
isSystem: false,
_raw: { /* payload gốc */ }
}Reconnect: Tự động kết nối lại tối đa 5 lần với exponential backoff (1s → 2s → 4s → 8s → 16s).
sendMessage()
Gửi tin nhắn văn bản đến một thread.
const result = await api.sendMessage(threadID, content);| Tham số | Kiểu | Mô tả |
|---|---|---|
threadID |
string |
ID của channel/thread |
content |
string |
Nội dung plain text |
await api.sendMessage('691c4437...', 'Xin chào!');Returns: Promise<{ message: { _id, body, user, createdAt, channelId, ... } }>
sendAttachment()
Gửi file hoặc ảnh đính kèm. Hỗ trợ stream trực tiếp, không load vào RAM.
const result = await api.sendAttachment(threadID, filePath, caption);| Tham số | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
threadID |
string |
✅ | ID của channel |
filePath |
string |
✅ | Đường dẫn file (tuyệt đối hoặc tương đối) |
caption |
string |
❌ | Caption đi kèm (mặc định '') |
// Gửi ảnh có caption
await api.sendAttachment(threadID, './photo.jpg', 'Ảnh hôm nay');
// Gửi PDF không caption
await api.sendAttachment(threadID, './report.pdf');Định dạng hỗ trợ: .jpg .png .gif .webp .mp4 .pdf .zip .mp3 .ogg
markAsRead()
Đánh dấu đã đọc toàn bộ tin nhắn trong thread.
await api.markAsRead(threadID);// Thường dùng trong listen callback
api.listen((err, event) => {
if (event?.type === 'message') {
api.markAsRead(event.data.threadID);
}
});Returns: Promise<true>
getThreadList()
Lấy danh sách tất cả cuộc trò chuyện của tài khoản.
const threads = await api.getThreadList();threads.forEach(t => {
console.log(t._id, t.name, t.scope); // scope: 'direct' | 'group'
});
// Lấy threadID đầu tiên
const threadID = threads[0]._id;Cấu trúc Thread:
{
_id: '691c4437a50691e99899726a',
type: 'text',
scope: 'direct',
name: 'Nguyễn Văn A',
icon: null,
isVerified: false,
lastMessage: { _id, body, user, createdAt },
lastMessageSentAt: '2026-02-23T15:50:26.453Z',
lastReadMessage: '...',
createdAt: '2025-11-18T10:02:31.601Z'
}Returns: Promise<Thread[]>
getThreadHistory()
Lấy lịch sử tin nhắn của một thread. Hỗ trợ cursor-based pagination.
const { items, nextCursor } = await api.getThreadHistory(threadID, options);| Tham số | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
threadID |
string |
— | ID của channel |
options.limit |
number |
20 |
Số tin nhắn mỗi trang |
options.before |
string |
— | Lấy tin nhắn cũ hơn messageID này |
options.after |
string |
— | Lấy tin nhắn mới hơn messageID này |
// Trang đầu
let page = await api.getThreadHistory(threadID, { limit: 20 });
console.log(page.items); // mảng Message[]
// Load thêm (cũ hơn)
if (page.nextCursor) {
const older = await api.getThreadHistory(threadID, {
limit: 20,
before: page.nextCursor,
});
}Returns: Promise<{ items: Message[], nextCursor: string|null, previousCursor: string|null }>
getUserInfo() / getMyProfile()
Lấy thông tin tài khoản đang đăng nhập. getMyProfile() là alias của getUserInfo().
const me = await api.getUserInfo();
// hoặc
const me = await api.getMyProfile();console.log(me._id); // userId — dùng để lọc tin nhắn của bot
console.log(me.email);
console.log(me.username);
console.log(me.fullName);Returns: Promise<User>
Error Handling
Mọi hàm đều throw error với message rõ ràng:
try {
await api.sendMessage(threadID, 'Hello');
} catch (err) {
if (err.message === 'Session expired, please re-login') {
// Token hết hạn, đăng nhập lại
api = await login(email, password);
fs.writeFileSync('appstate.json', JSON.stringify(api.getAppState(), null, 2));
} else {
console.error(err.message);
}
}Danh sách lỗi thường gặp:
| Lỗi | Nguyên nhân |
|---|---|
Session expired, please re-login |
Token hết hạn (401/403) |
Max reconnect attempts reached |
WebSocket mất kết nối > 5 lần |
sendMessage: channelId là bắt buộc |
Thiếu threadID |
sendMessage: content là bắt buộc |
Thiếu content |
sendAttachment: File không tồn tại: ... |
File không tồn tại tại đường dẫn đã cho |
loadAppState: token là bắt buộc |
AppState không hợp lệ |
Yêu cầu hệ thống
| Phiên bản | |
|---|---|
| Node.js | >= 18.0.0 |
| npm | >= 8.0.0 |
Dependencies chính:
| Package | Mục đích |
|---|---|
axios |
HTTP requests |
ws |
WebSocket client |
@msgpack/msgpack |
MessagePack encoding (Socket.IO auth) |
form-data |
Multipart upload cho sendMessage/sendAttachment |
uuid |
Tạo signId unique mỗi tin nhắn |
axios-cookiejar-support + tough-cookie |
Cookie persistence |
Cấu trúc dự án
src/
├── index.js # login() + loadAppState()
├── httpUtils.js # axios instance, setToken(), cookie jar
├── wsClient.js # WebSocket — Socket.IO + MessagePack
└── api/
├── listen.js
├── sendMessage.js
├── sendAttachment.js
├── markAsRead.js
├── getThreadList.js
├── getThreadHistory.js
└── getUserInfo.js