npm package: @nhanaka/call
Description: WebRTC call library with group calls, permissions, and realtime chat for Node.js, Next.js, and React
Author: fo-nhan
Latest version: 2.0.2

﻿# @nhanaka/call

Thư viện WebRTC call đơn giản và mạnh mẽ cho Node.js, Next.js và React. Hỗ trợ cuộc gọi thoại, video và group call với Socket.IO signaling.

## ✨ Tính năng

- 🎥 **Cuộc gọi video & thoại**: Hỗ trợ cả video call và voice call 1-1
- 👥 **Group call**: Cuộc gọi nhóm nhiều người với phân quyền 3 cấp
- 💬 **Chat realtime**: Gửi tin nhắn trong cuộc gọi với validation
- 🔐 **Phân quyền**: HOST, MODERATOR, MEMBER với quyền hạn khác nhau
- ⚛️ **React hooks**: Tích hợp sẵn với React Context và hooks
- 🔒 **Bảo mật**: Xác thực token và domain validation
- 🌐 **WebRTC**: Kết nối peer-to-peer chất lượng cao
- 📡 **Socket.IO**: Signaling server với rooms support
- 🎛️ **Tùy chỉnh**: Callbacks linh hoạt cho mọi sự kiện
- 📱 **Responsive**: Hoạt động tốt trên mọi thiết bị

## 📦 Cài đặt

```bash
npm install @nhanaka/call socket.io socket.io-client react
```

## 🚀 Quick Start

### Server-side

```typescript
import { createCallServer } from '@nhanaka/call';
import { Server } from 'socket.io';
import { createServer } from 'http';

const httpServer = createServer();
const io = new Server(httpServer, {
  cors: { origin: 'http://localhost:3000', credentials: true }
});

const callServer = createCallServer({
  io,
  callbacks: {
    onValidateToken: async (token) => token === 'valid-token',
    onCallInitiated: async (data) => {
      console.log('New call:', data);
      return true;
    },
    onCallMessage: async (message) => {
      await db.messages.create(message);
      return true;
    }
  }
});

httpServer.listen(3001);
```

### Client-side (React)

```typescript
import { CallProvider, useCall, CallType } from '@nhanaka/call';

function App() {
  return (
    <CallProvider config={{
      serverUrl: 'http://localhost:3001',
      userId: 'user-123',
      token: 'your-token'
    }}>
      <VideoCall />
    </CallProvider>
  );
}

function VideoCall() {
  const { initiateCall } = useCall();
  
  const handleCall = () => {
    initiateCall('user-456', CallType.VIDEO, { audio: true, video: true });
  };
  
  const handleGroupCall = () => {
    initiateCall(['user-2', 'user-3'], CallType.GROUP, { audio: true, video: true });
  };
  
  return <button onClick={handleCall}>Call</button>;
}
```

## 📞 Group Call

### Hệ thống phân quyền 3 cấp

```typescript
enum ParticipantLevel {
  HOST = 0,        // Chủ phòng - quyền cao nhất
  MODERATOR = 1,   // Người điều hành
  MEMBER = 2       // Thành viên thường
}
```

| Quyền | HOST | MODERATOR | MEMBER |
|-------|------|-----------|--------|
| Kick người khác | ✅ | ✅ (chỉ MEMBER) | ❌ |
| Mute người khác | ✅ | ✅ (chỉ MEMBER) | ❌ |
| Tắt camera người khác | ✅ | ✅ (chỉ MEMBER) | ❌ |
| Mời người vào call | ✅ | ✅ | ❌ |
| Gửi tin nhắn | ✅ | ✅ | ✅ |

**Lưu ý về Force Toggle:**
- Khi admin tắt mic/camera với `forced: true`, user **KHÔNG THỂ** tự bật lại
- User chỉ có thể bật lại khi admin unmute/unblock họ
- Dùng cho trường hợp cần kiểm soát chặt chẽ (lớp học, hội nghị quan trọng)

### Quản lý participants

```typescript
// Kick participant
await client.kickParticipant(callId, 'user-3', 'Spam');

// Mute participant - user có thể tự bật lại
await client.toggleRemoteMedia(callId, 'user-3', 'audio', false);

// Force mute - user KHÔNG THỂ tự bật lại
await client.toggleRemoteMedia(callId, 'user-3', 'audio', false, true);

// Mời thêm người vào group call
await client.inviteParticipants(callId, ['user-5', 'user-6'], ParticipantLevel.MEMBER);
```

### Chat trong cuộc gọi

```typescript
// Gửi tin nhắn
await client.sendMessage(callId, 'Hello everyone!');

// Nhận tin nhắn
client.on('callMessage', (message) => {
  console.log(`: `);
});
```

## 🎯 API Reference

### Server API

**createCallServer(config)**
- `io`: Socket.IO server instance
- `callbacks`: onValidateToken, onCallInitiated, onCallMessage, etc.

**Methods:**
- `getIO()`: Lấy Socket.IO instance
- `getActiveCalls()`: Danh sách cuộc gọi
- `getOnlineUsers()`: Danh sách user online

### Client API

**createCallClient(config)**
- `serverUrl`: URL server
- `userId`: ID user hiện tại
- `token`: Token xác thực

**Methods:**
- `connect()`: Kết nối server
- `initiateCall(to, type, constraints)`: Tạo cuộc gọi
- `joinCall(callId, constraints, level?)`: Join group call
- `sendMessage(callId, message)`: Gửi tin nhắn
- `kickParticipant(callId, userId, reason?)`: Kick participant
- `toggleRemoteMedia(callId, userId, type, enabled, forced?)`: Toggle media
- `inviteParticipants(callId, userIds, level?)`: Mời thêm người

### React Hooks

**useCall()**
- `initiateCall()`, `acceptCall()`, `rejectCall()`, `endCall()`
- `toggleAudio()`, `toggleVideo()`
- `localStream`, `remoteStreams`, `incomingCall`

**useVideoElement(stream)**
```typescript
const videoRef = useVideoElement(localStream);
return <video ref={videoRef} autoPlay muted />;
```

## 📝 Types

```typescript
enum CallType {
  VOICE = 'voice',
  VIDEO = 'video',
  GROUP = 'group'
}

interface Participant {
  userId: string;
  level: ParticipantLevel;
  isAudioEnabled: boolean;
  isVideoEnabled: boolean;
  isAudioForcedOff: boolean;
  isVideoForcedOff: boolean;
}
```

## 🎯 Use Cases

### Online Class

```typescript
const callId = await client.initiateCall(
  students,
  CallType.GROUP,
  { audio: true, video: true }
);

// Force mute all students
for (const student of students) {
  await client.toggleRemoteMedia(callId, student, 'audio', false, true);
}

// Cho phép 1 học sinh phát biểu
await client.toggleRemoteMedia(callId, studentId, 'audio', true);
```

## 📄 License

MIT

## 🙏 Credits

Built with ❤️ using WebRTC, Socket.IO, and TypeScript

Repository: https://github.com/fo-nhan/call