# API Gửi Tin Nhắn (Messaging API)

## Tổng Quan

Zalo Personal SDK cung cấp API mạnh mẽ để gửi nhiều loại tin nhắn khác nhau:
- Tin nhắn văn bản (có định dạng)
- Tin nhắn với file đính kèm
- Tin nhắn trả lời (quote)
- Tin nhắn có mention
- Tin nhắn tự hủy
- Sticker, link, card, voice, video

## Gửi Tin Nhắn Cơ Bản

### 1. Tin Nhắn Văn Bản Đơn Giản

```typescript
import { ThreadType } from 'zalo-personal-sdk';

// Gửi cho người dùng
await api.sendMessage('Xin chào!', 'user-id', ThreadType.User);

// Gửi cho nhóm
await api.sendMessage('Hello group!', 'group-id', ThreadType.Group);

// Sử dụng object
const message = {
  msg: 'Tin nhắn từ object'
};
await api.sendMessage(message, 'user-id', ThreadType.User);
```

### 2. Kết Quả Trả Về

```typescript
const result = await api.sendMessage('Hello', 'user-id', ThreadType.User);
console.log(result);
// {
//   message: { msgId: 123456789 },
//   attachment: []
// }
```

## Định Dạng Văn Bản

### 1. Các Kiểu Định Dạng

```typescript
import { TextStyle, MessageContent } from 'zalo-personal-sdk';

const messageContent: MessageContent = {
  msg: 'Văn bản có định dạng: in đậm, in nghiêng, gạch chân',
  styles: [
    { start: 22, len: 7, st: TextStyle.Bold },      // "in đậm"
    { start: 31, len: 10, st: TextStyle.Italic },   // "in nghiêng"
    { start: 43, len: 9, st: TextStyle.Underline }  // "gạch chân"
  ]
};

await api.sendMessage(messageContent, 'user-id', ThreadType.User);
```

### 2. Màu Sắc

```typescript
const colorMessage: MessageContent = {
  msg: 'Màu đỏ, màu cam, màu vàng, màu xanh',
  styles: [
    { start: 0, len: 7, st: TextStyle.Red },     // "Màu đỏ"
    { start: 9, len: 7, st: TextStyle.Orange },  // "màu cam"
    { start: 18, len: 8, st: TextStyle.Yellow }, // "màu vàng"
    { start: 28, len: 9, st: TextStyle.Green }   // "màu xanh"
  ]
};

await api.sendMessage(colorMessage, 'user-id', ThreadType.User);
```

### 3. Kích Thước Font

```typescript
const sizeMessage: MessageContent = {
  msg: 'Font nhỏ và Font lớn',
  styles: [
    { start: 0, len: 8, st: TextStyle.Small },  // "Font nhỏ"
    { start: 12, len: 9, st: TextStyle.Big }    // "Font lớn"
  ]
};

await api.sendMessage(sizeMessage, 'user-id', ThreadType.User);
```

### 4. Danh Sách

```typescript
const listMessage: MessageContent = {
  msg: 'Item 1\nItem 2\nItem 3',
  styles: [
    { start: 0, len: 6, st: TextStyle.UnorderedList }, // "Item 1"
    { start: 7, len: 6, st: TextStyle.UnorderedList }, // "Item 2"
    { start: 14, len: 6, st: TextStyle.UnorderedList } // "Item 3"
  ]
};

await api.sendMessage(listMessage, 'user-id', ThreadType.User);
```

### 5. Thụt Lề

```typescript
const indentMessage: MessageContent = {
  msg: 'Đoạn thụt lề với khoảng cách tùy chỉnh',
  styles: [
    { 
      start: 0, 
      len: 35, 
      st: TextStyle.Indent,
      indentSize: 2 // Thụt lề 20px (2 * 10px)
    }
  ]
};

await api.sendMessage(indentMessage, 'user-id', ThreadType.User);
```

### 6. Kết Hợp Nhiều Định Dạng

```typescript
const complexMessage: MessageContent = {
  msg: 'Văn bản phức tạp với nhiều định dạng',
  styles: [
    { start: 0, len: 8, st: TextStyle.Bold },
    { start: 0, len: 8, st: TextStyle.Red },
    { start: 9, len: 8, st: TextStyle.Italic },
    { start: 9, len: 8, st: TextStyle.Underline }
  ]
};

await api.sendMessage(complexMessage, 'user-id', ThreadType.User);
```

## Mức Độ Ưu Tiên

```typescript
import { Urgency } from 'zalo-personal-sdk';

const urgentMessage: MessageContent = {
  msg: 'Tin nhắn khẩn cấp!',
  urgency: Urgency.Urgent
};

const importantMessage: MessageContent = {
  msg: 'Tin nhắn quan trọng',
  urgency: Urgency.Important
};

await api.sendMessage(urgentMessage, 'user-id', ThreadType.User);
await api.sendMessage(importantMessage, 'user-id', ThreadType.User);
```

## Mention (Tag Người Dùng)

### 1. Mention Trong Nhóm

```typescript
const mentionMessage: MessageContent = {
  msg: '@John @Jane Chào các bạn!',
  mentions: [
    { pos: 0, len: 5, uid: 'john-user-id' },  // "@John"
    { pos: 6, len: 5, uid: 'jane-user-id' }   // "@Jane" 
  ]
};

// Chỉ hoạt động trong nhóm
await api.sendMessage(mentionMessage, 'group-id', ThreadType.Group);
```

### 2. Mention All

```typescript
const mentionAllMessage: MessageContent = {
  msg: '@all Thông báo quan trọng!',
  mentions: [
    { pos: 0, len: 4, uid: '-1' } // "-1" là ID đặc biệt cho mention all
  ]
};

await api.sendMessage(mentionAllMessage, 'group-id', ThreadType.Group);
```

### 3. Helper Function Cho Mention

```typescript
function createMentionMessage(text: string, mentions: Array<{name: string, uid: string}>) {
  let finalText = text;
  const mentionArray: Mention[] = [];
  
  mentions.forEach(mention => {
    const mentionText = `@${mention.name}`;
    const pos = finalText.indexOf(mentionText);
    
    if (pos !== -1) {
      mentionArray.push({
        pos: pos,
        len: mentionText.length,
        uid: mention.uid
      });
    }
  });
  
  return {
    msg: finalText,
    mentions: mentionArray
  };
}

// Sử dụng
const message = createMentionMessage(
  'Xin chào @John và @Jane!',
  [
    { name: 'John', uid: 'john-id' },
    { name: 'Jane', uid: 'jane-id' }
  ]
);

await api.sendMessage(message, 'group-id', ThreadType.Group);
```

## Trả Lời Tin Nhắn (Quote)

### 1. Quote Tin Nhắn Văn Bản

```typescript
// Tin nhắn gốc từ listener hoặc API khác
const originalMessage = {
  content: 'Tin nhắn gốc cần được trả lời',
  msgType: 'webchat',
  propertyExt: {},
  uidFrom: 'sender-id',
  msgId: 123456,
  cliMsgId: 'cli-msg-id',
  ts: Date.now(),
  ttl: 0
};

const replyMessage: MessageContent = {
  msg: 'Đây là câu trả lời',
  quote: originalMessage
};

await api.sendMessage(replyMessage, 'user-id', ThreadType.User);
```

### 2. Quote Với Định Dạng

```typescript
const styledReply: MessageContent = {
  msg: 'Trả lời với định dạng đẹp',
  quote: originalMessage,
  styles: [
    { start: 8, len: 4, st: TextStyle.Bold }
  ]
};

await api.sendMessage(styledReply, 'user-id', ThreadType.User);
```

### 3. Quote Helper Function

```typescript
function createReplyMessage(replyText: string, originalMessage: any) {
  return {
    msg: replyText,
    quote: {
      content: originalMessage.content,
      msgType: originalMessage.msgType,
      propertyExt: originalMessage.propertyExt || {},
      uidFrom: originalMessage.uidFrom,
      msgId: originalMessage.msgId,
      cliMsgId: originalMessage.cliMsgId,
      ts: originalMessage.ts,
      ttl: originalMessage.ttl || 0
    }
  };
}

// Sử dụng
const reply = createReplyMessage('Cảm ơn bạn!', receivedMessage);
await api.sendMessage(reply, threadId, threadType);
```

## Tin Nhắn Tự Hủy (TTL)

### 1. Cấu Hình TTL

```typescript
const selfDestructMessage: MessageContent = {
  msg: 'Tin nhắn này sẽ tự hủy sau 60 giây',
  ttl: 60 * 1000 // TTL tính bằng milliseconds
};

await api.sendMessage(selfDestructMessage, 'user-id', ThreadType.User);
```

### 2. Các Mức TTL Thông Dụng

```typescript
const TTL_PRESETS = {
  ONE_MINUTE: 60 * 1000,
  FIVE_MINUTES: 5 * 60 * 1000,
  ONE_HOUR: 60 * 60 * 1000,
  ONE_DAY: 24 * 60 * 60 * 1000,
  ONE_WEEK: 7 * 24 * 60 * 60 * 1000
};

const quickMessage: MessageContent = {
  msg: 'Tin nhắn tự hủy sau 5 phút',
  ttl: TTL_PRESETS.FIVE_MINUTES
};
```

## File Đính Kèm

### 1. Gửi File Từ Đường Dẫn Local

```typescript
const imageMessage: MessageContent = {
  msg: 'Ảnh đẹp không?',
  attachments: '/path/to/image.jpg'
};

await api.sendMessage(imageMessage, 'user-id', ThreadType.User);
```

### 2. Gửi Nhiều File

```typescript
const multiFileMessage: MessageContent = {
  msg: 'Gửi nhiều file cùng lúc',
  attachments: [
    '/path/to/image1.jpg',
    '/path/to/image2.png', 
    '/path/to/document.pdf'
  ]
};

await api.sendMessage(multiFileMessage, 'user-id', ThreadType.User);
```

### 3. Gửi File Từ URL

```typescript
const urlFileMessage: MessageContent = {
  msg: 'File từ internet',
  attachments: {
    url: 'https://example.com/image.jpg',
    filename: 'downloaded-image.jpg',
    headers: {
      'User-Agent': 'MyBot/1.0'
    }
  }
};

await api.sendMessage(urlFileMessage, 'user-id', ThreadType.User);
```

### 4. Gửi File Từ Buffer

```typescript
import fs from 'fs';

const fileBuffer = fs.readFileSync('./image.jpg');

const bufferFileMessage: MessageContent = {
  msg: 'File từ buffer',
  attachments: {
    data: fileBuffer,
    filename: 'buffer-image.jpg',
    metadata: {
      totalSize: fileBuffer.length,
      width: 1920,    // Cho ảnh
      height: 1080    // Cho ảnh
    }
  }
};

await api.sendMessage(bufferFileMessage, 'user-id', ThreadType.User);
```

### 5. Kiểm Tra File Trước Khi Gửi

```typescript
import fs from 'fs/promises';
import path from 'path';

async function validateAndSendFile(filePath: string, threadId: string, threadType: ThreadType) {
  try {
    // Kiểm tra file tồn tại
    await fs.access(filePath);
    
    // Kiểm tra kích thước file (max 25MB)
    const stats = await fs.stat(filePath);
    const maxSize = 25 * 1024 * 1024; // 25MB
    
    if (stats.size > maxSize) {
      throw new Error(`File quá lớn: ${(stats.size / (1024*1024)).toFixed(1)}MB > 25MB`);
    }
    
    // Kiểm tra loại file
    const ext = path.extname(filePath).toLowerCase();
    const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.mp4', '.pdf', '.doc', '.docx'];
    
    if (!allowedExtensions.includes(ext)) {
      throw new Error(`Loại file không được hỗ trợ: ${ext}`);
    }
    
    // Gửi file
    const message: MessageContent = {
      msg: `Đã gửi file: ${path.basename(filePath)}`,
      attachments: filePath
    };
    
    const result = await api.sendMessage(message, threadId, threadType);
    console.log('✅ Gửi file thành công:', result);
    
    return result;
  } catch (error) {
    console.error('❌ Lỗi gửi file:', error.message);
    throw error;
  }
}
```

## Các Loại Tin Nhắn Đặc Biệt

### 1. Sticker

```typescript
// Lấy danh sách sticker
const stickers = await api.getStickers();
console.log('Danh sách sticker:', stickers);

// Lấy chi tiết sticker category
const stickerDetail = await api.getStickersDetail('category-id');

// Gửi sticker
await api.sendSticker('sticker-id', 'user-id', ThreadType.User, 'category-id');
```

### 2. Link Preview

```typescript
const linkOptions = {
  url: 'https://github.com/zalo-sdk/zalo-personal-sdk',
  title: 'Zalo Personal SDK',
  description: 'SDK unofficial cho Zalo Personal API',
  thumbnail: 'https://github.com/image.jpg'
};

await api.sendLink(linkOptions, 'user-id', ThreadType.User);
```

### 3. Card/Danh Thiếp

```typescript
const cardOptions = {
  userId: 'target-user-id' // ID người muốn chia sẻ thông tin
};

await api.sendCard(cardOptions, 'recipient-id', ThreadType.User);
```

### 4. Voice Message

```typescript
const voiceOptions = {
  filePath: '/path/to/audio.m4a',
  duration: 15 // Thời lượng tính bằng giây
};

await api.sendVoice(voiceOptions, 'user-id', ThreadType.User);
```

### 5. Video Message

```typescript
const videoOptions = {
  filePath: '/path/to/video.mp4',
  duration: 30,   // Thời lượng (giây)
  width: 1920,    // Chiều rộng
  height: 1080    // Chiều cao
};

await api.sendVideo(videoOptions, 'user-id', ThreadType.User);
```

## Xử Lý Lỗi

### 1. Try-Catch Pattern

```typescript
import { ZaloApiError } from 'zalo-personal-sdk';

try {
  const result = await api.sendMessage('Hello', 'user-id', ThreadType.User);
  console.log('✅ Gửi thành công:', result);
} catch (error) {
  if (error instanceof ZaloApiError) {
    switch (error.message) {
      case 'Missing message content':
        console.error('❌ Thiếu nội dung tin nhắn');
        break;
      case 'Missing threadId':
        console.error('❌ Thiếu ID thread');
        break;
      case 'Exceed maximum file':
        console.error('❌ Vượt quá số lượng file tối đa');
        break;
      default:
        console.error('❌ Lỗi API:', error.message);
    }
  } else {
    console.error('❌ Lỗi hệ thống:', error);
  }
}
```

### 2. Retry Logic

```typescript
async function sendMessageWithRetry(
  message: MessageContent | string,
  threadId: string, 
  threadType: ThreadType,
  maxRetries: number = 3
) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await api.sendMessage(message, threadId, threadType);
      console.log(`✅ Gửi thành công lần thử ${attempt}`);
      return result;
    } catch (error) {
      console.error(`❌ Lần thử ${attempt} thất bại:`, error.message);
      
      if (attempt === maxRetries) {
        throw new Error(`Gửi tin nhắn thất bại sau ${maxRetries} lần thử`);
      }
      
      // Chờ trước khi thử lại (exponential backoff)
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
```

### 3. Rate Limiting

```typescript
class MessageRateLimiter {
  private messageQueue: Array<() => Promise<any>> = [];
  private isProcessing = false;
  private readonly delayBetweenMessages = 1000; // 1 giây
  
  async sendMessage(
    message: MessageContent | string,
    threadId: string,
    threadType: ThreadType
  ) {
    return new Promise((resolve, reject) => {
      const task = async () => {
        try {
          const result = await api.sendMessage(message, threadId, threadType);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      };
      
      this.messageQueue.push(task);
      this.processQueue();
    });
  }
  
  private async processQueue() {
    if (this.isProcessing || this.messageQueue.length === 0) return;
    
    this.isProcessing = true;
    
    while (this.messageQueue.length > 0) {
      const task = this.messageQueue.shift()!;
      await task();
      
      // Delay giữa các tin nhắn
      if (this.messageQueue.length > 0) {
        await new Promise(resolve => setTimeout(resolve, this.delayBetweenMessages));
      }
    }
    
    this.isProcessing = false;
  }
}

// Sử dụng
const rateLimiter = new MessageRateLimiter();

await rateLimiter.sendMessage('Message 1', 'user-id', ThreadType.User);
await rateLimiter.sendMessage('Message 2', 'user-id', ThreadType.User);
await rateLimiter.sendMessage('Message 3', 'user-id', ThreadType.User);
```

## Utility Functions

### 1. Message Builder

```typescript
class MessageBuilder {
  private message: MessageContent = { msg: '' };
  
  text(text: string) {
    this.message.msg = text;
    return this;
  }
  
  style(start: number, length: number, style: TextStyle) {
    if (!this.message.styles) this.message.styles = [];
    this.message.styles.push({ start, len: length, st: style });
    return this;
  }
  
  urgent() {
    this.message.urgency = Urgency.Urgent;
    return this;
  }
  
  important() {
    this.message.urgency = Urgency.Important;
    return this;
  }
  
  ttl(milliseconds: number) {
    this.message.ttl = milliseconds;
    return this;
  }
  
  attachment(source: AttachmentSource) {
    if (!this.message.attachments) {
      this.message.attachments = source;
    } else if (Array.isArray(this.message.attachments)) {
      this.message.attachments.push(source as any);
    } else {
      this.message.attachments = [this.message.attachments as any, source as any];
    }
    return this;
  }
  
  mention(pos: number, length: number, uid: string) {
    if (!this.message.mentions) this.message.mentions = [];
    this.message.mentions.push({ pos, len: length, uid });
    return this;
  }
  
  quote(originalMessage: any) {
    this.message.quote = originalMessage;
    return this;
  }
  
  build(): MessageContent {
    return { ...this.message };
  }
  
  async send(threadId: string, threadType: ThreadType) {
    return await api.sendMessage(this.build(), threadId, threadType);
  }
}

// Sử dụng
const result = await new MessageBuilder()
  .text('Tin nhắn quan trọng với file đính kèm!')
  .style(0, 11, TextStyle.Bold)
  .style(12, 9, TextStyle.Red)
  .important()
  .attachment('/path/to/file.pdf')
  .ttl(5 * 60 * 1000)
  .send('user-id', ThreadType.User);
```

### 2. Template Messages

```typescript
const MessageTemplates = {
  welcome: (name: string) => ({
    msg: `Xin chào ${name}! 👋 Chào mừng bạn đến với hệ thống.`,
    styles: [
      { start: 9, len: name.length, st: TextStyle.Bold }
    ]
  }),
  
  error: (error: string) => ({
    msg: `⚠️ Lỗi: ${error}`,
    styles: [
      { start: 0, len: 6, st: TextStyle.Red }
    ],
    urgency: Urgency.Important
  }),
  
  success: (message: string) => ({
    msg: `✅ Thành công: ${message}`,
    styles: [
      { start: 0, len: 14, st: TextStyle.Green }
    ]
  }),
  
  reminder: (content: string, minutes: number) => ({
    msg: `⏰ Nhắc nhở: ${content}`,
    ttl: minutes * 60 * 1000,
    urgency: Urgency.Important
  })
};

// Sử dụng
await api.sendMessage(MessageTemplates.welcome('John'), 'user-id', ThreadType.User);
await api.sendMessage(MessageTemplates.error('Không thể tải file'), 'user-id', ThreadType.User);
await api.sendMessage(MessageTemplates.reminder('Họp lúc 3h chiều', 60), 'user-id', ThreadType.User);
```

## Best Practices

### 1. Validate Input

```typescript
function validateMessageInput(message: any, threadId: string, threadType: ThreadType) {
  if (!message) throw new Error('Message không được để trống');
  if (!threadId) throw new Error('ThreadId không được để trống');
  if (![ThreadType.User, ThreadType.Group].includes(threadType)) {
    throw new Error('ThreadType không hợp lệ');
  }
  
  if (typeof message === 'object' && message.msg && message.msg.length > 1000) {
    throw new Error('Tin nhắn quá dài (max 1000 ký tự)');
  }
}
```

### 2. Async Queue cho Bulk Messages

```typescript
async function sendBulkMessages(messages: Array<{content: any, threadId: string, threadType: ThreadType}>) {
  const results = [];
  const errors = [];
  
  for (const [index, msg] of messages.entries()) {
    try {
      console.log(`Gửi tin nhắn ${index + 1}/${messages.length}...`);
      const result = await api.sendMessage(msg.content, msg.threadId, msg.threadType);
      results.push({ index, result });
      
      // Delay giữa các tin nhắn
      if (index < messages.length - 1) {
        await new Promise(resolve => setTimeout(resolve, 1000));
      }
    } catch (error) {
      errors.push({ index, error: error.message });
    }
  }
  
  return { results, errors };
}
```

### 3. Message History

```typescript
class MessageHistory {
  private history: Array<{id: string, content: any, threadId: string, timestamp: Date}> = [];
  
  async sendAndRecord(message: any, threadId: string, threadType: ThreadType) {
    const result = await api.sendMessage(message, threadId, threadType);
    
    this.history.push({
      id: result.message?.msgId?.toString() || 'unknown',
      content: message,
      threadId,
      timestamp: new Date()
    });
    
    return result;
  }
  
  getHistory() {
    return [...this.history];
  }
  
  findByThreadId(threadId: string) {
    return this.history.filter(msg => msg.threadId === threadId);
  }
}
```

## Lưu Ý Quan Trọng

1. **Rate Limiting**: Tránh gửi quá nhiều tin nhắn trong thời gian ngắn
2. **File Size**: Kiểm tra kích thước file trước khi gửi (max ~25MB)
3. **Character Limit**: Tin nhắn văn bản có giới hạn độ dài
4. **Mention**: Chỉ hoạt động trong nhóm
5. **Quote**: Một số loại tin nhắn không thể quote được
6. **TTL**: Không phải tất cả client đều hỗ trợ tin nhắn tự hủy
7. **Attachment**: Kiểm tra loại file được hỗ trợ trước khi gửi
8. **Error Handling**: Luôn wrap API calls trong try-catch