# textme-unofficial-api

> ⚠️ **UNOFFICIAL API** - This library is not affiliated with, endorsed by, or connected to TextMe Inc. in any way. Use at your own risk. This library may break at any time if TextMe changes their internal APIs.

An unofficial TypeScript/JavaScript client for the TextMe messaging service. Provides authentication, REST API access, and realtime WebSocket support.

## Installation

```bash
npm install textme-unofficial-api
```

## Requirements

- Node.js >= 18.0.0
- A TextMe account

## Quick Start

```typescript
import { TextMeClient } from 'textme-unofficial-api';

const client = new TextMeClient({
  credentials: {
    email: 'your-email@example.com',
    password: 'your-password'
  }
});

// Connect and authenticate
await client.connect();

// Listen for incoming messages
client.on('message.received', (event) => {
  console.log('New message from:', event.message.senderPhoneNumber);
  console.log('Content:', event.message.content.text);
});

// Send a message
await client.sendMessage({
  recipientPhoneNumber: '+1234567890',
  senderPhoneNumber: '+0987654321', // Your TextMe number
  text: 'Hello from TextMe!'
});

// Get conversations
const conversations = await client.getConversations({ limit: 20 });
console.log(`You have ${conversations.data.length} conversations`);

// Disconnect when done
await client.disconnect();
```

## API Reference

### TextMeClient

The unified client that combines all functionality.

#### Constructor Options

```typescript
interface TextMeClientOptions {
  // Authentication (provide one)
  credentials?: { email: string; password: string };
  tokens?: AuthTokens;
  
  // Configuration
  baseUrl?: string;      // Custom API base URL
  wsUrl?: string;        // Custom WebSocket URL
  timeout?: number;      // Request timeout (ms)
  debug?: boolean;       // Enable debug logging
  
  // Reconnection
  autoReconnect?: boolean;       // Auto-reconnect on disconnect
  maxReconnectAttempts?: number; // Max reconnection attempts
  reconnectDelay?: number;       // Delay between attempts (ms)
}
```

#### Connection Methods

```typescript
// Connect and authenticate
await client.connect(): Promise<AuthSession>

// Disconnect from services
await client.disconnect(): Promise<void>

// Check connection status
client.isConnected(): boolean
```

#### Authentication Methods

```typescript
// Login with credentials
await client.login(credentials): Promise<AuthSession>

// Refresh tokens
await client.refreshTokens(): Promise<AuthTokens>

// Get current user info
await client.getCurrentUser(): Promise<User>
```

#### Messaging Methods

```typescript
// Send a message
await client.sendMessage({
  recipientPhoneNumber: string,
  senderPhoneNumber: string,
  text?: string,
  mediaUrls?: string[]
}): Promise<SendMessageResponse>

// Get conversations
await client.getConversations({
  limit?: number,
  offset?: number,
  before?: string,
  after?: string
}): Promise<PaginatedResponse<Conversation>>

// Get messages in a conversation
await client.getMessages({
  conversationId: string,
  limit?: number,
  before?: string,
  after?: string
}): Promise<PaginatedResponse<Message>>

// Mark messages as read
await client.markAsRead(conversationId: string, messageIds?: string[]): Promise<void>

// Delete a message
await client.deleteMessage(messageId: string): Promise<void>
```

#### Realtime Events

```typescript
// Subscribe to events
client.on('message.received', (event) => {
  console.log(event.message);
});

client.on('message.status', (event) => {
  console.log(`Message ${event.messageId} is now ${event.status}`);
});

client.on('typing.start', (event) => {
  console.log(`Someone is typing in ${event.conversationId}`);
});

client.on('connection.close', (event) => {
  console.log('Disconnected');
});

// Unsubscribe
client.off('message.received', handler);

// Send typing indicator
client.sendTyping(conversationId, true);  // Started typing
client.sendTyping(conversationId, false); // Stopped typing
```

### Individual Modules

For advanced use cases, you can use the modules directly:

```typescript
import { TextMeAuth, TextMeAPI, TextMeRealtime } from 'textme-unofficial-api';

// Authentication only
const auth = new TextMeAuth({ debug: true });
const session = await auth.login({ email, password });

// API only
const api = new TextMeAPI();
api.setTokens(session.tokens);
const conversations = await api.getConversations();

// Realtime only
const realtime = new TextMeRealtime({ autoReconnect: true });
await realtime.connect(session.tokens);
realtime.on('message.received', console.log);
```

## Types

### User

```typescript
interface User {
  id: string;
  email: string;
  displayName: string;
  avatarUrl?: string;
  phoneNumbers: PhoneNumber[];
  createdAt: string;
  updatedAt: string;
}
```

### PhoneNumber

```typescript
interface PhoneNumber {
  id: string;
  number: string;
  countryCode: string;
  type: 'mobile' | 'landline' | 'voip' | 'unknown';
  isPrimary: boolean;
  isVerified: boolean;
  capabilities: {
    sms: boolean;
    mms: boolean;
    voice: boolean;
  };
}
```

### Conversation

```typescript
interface Conversation {
  id: string;
  type: 'direct' | 'group';
  participants: Participant[];
  lastMessage?: Message;
  unreadCount: number;
  isPinned: boolean;
  isMuted: boolean;
  createdAt: string;
  updatedAt: string;
}
```

### Message

```typescript
interface Message {
  id: string;
  conversationId: string;
  senderId: string;
  senderPhoneNumber: string;
  recipientPhoneNumber: string;
  content: {
    type: 'text' | 'media' | 'mixed';
    text?: string;
    media?: MediaAttachment[];
  };
  status: 'pending' | 'sent' | 'delivered' | 'read' | 'failed';
  direction: 'inbound' | 'outbound';
  timestamp: string;
  readAt?: string;
  deliveredAt?: string;
}
```

## Error Handling

```typescript
import { TextMeError, AuthenticationError, RateLimitError } from 'textme-unofficial-api';

try {
  await client.connect();
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Invalid credentials');
  } else if (error instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${error.retryAfter}ms`);
  } else if (error instanceof TextMeError) {
    console.error(`API error: ${error.code} - ${error.message}`);
  }
}
```

## Token Persistence

To avoid logging in every time, save and restore tokens:

```typescript
import { TextMeClient } from 'textme-unofficial-api';
import fs from 'fs/promises';

// First time: login and save tokens
const client = new TextMeClient({
  credentials: { email, password }
});
const session = await client.connect();

await fs.writeFile('tokens.json', JSON.stringify(session.tokens));

// Later: restore from saved tokens
const savedTokens = JSON.parse(await fs.readFile('tokens.json', 'utf-8'));

const client2 = new TextMeClient({
  tokens: savedTokens
});
await client2.connect();
```

## ⚠️ Disclaimer

This is an **unofficial, reverse-engineered API client**. By using this library, you acknowledge:

1. **No Affiliation**: This project is not affiliated with TextMe Inc.
2. **Terms of Service**: Using this library may violate TextMe's Terms of Service
3. **No Warranty**: This library is provided "as is" without warranty of any kind
4. **Breakage Risk**: TextMe may change their internal APIs at any time, breaking this library
5. **Account Risk**: Your TextMe account could be suspended for using unofficial clients
6. **Legal Risk**: You are responsible for how you use this library

**Use responsibly and at your own risk.**

## License

MIT

---

*This library was created for educational purposes. Please respect TextMe's services and their users.*
