# User API

The User API provides access to user data, chat history, and communication capabilities within your tools and jobs.

## Import

```typescript
import { User } from 'lua-cli';
```

## Overview

The User API allows you to:
- Retrieve user profile data
- Update user information
- Send messages to users
- Access chat history
- Manage user preferences

## Methods

### `User.get()`

Retrieves user data by userId, email, or phone.

**Signature:**
```typescript
async get(identifier?: string | UserLookupOptions): Promise<UserDataInstance | null>
```

**Parameters:**
- `identifier` (optional) - One of:
  - No parameter: Returns current user's data (in tools with conversation context)
  - `string`: User ID to retrieve
  - `{ email: string }`: Look up user by email address
  - `{ phone: string }`: Look up user by phone number (with or without `+` prefix)

**Returns:** `UserDataInstance` with proxy access to user data, or `null` if user not found (for email/phone lookup)

**Examples:**

```typescript
// Get current user (in tools with conversation context)
const user = await User.get();

// Access properties directly
console.log(user.email);
console.log(user.name);

// Or access via .data
console.log(user.data.email);
```

### `User.get(userId)`

Retrieves a specific user's data by ID. This allows tools, webhooks, and jobs to access any user's data, not just the current user.

```typescript
// Get current user (existing behavior)
const currentUser = await User.get();

// Get specific user by ID
const specificUser = await User.get('user_123456');
console.log(specificUser.email);
```

**Use Cases:**
- Fetch data for specific users in admin tools
- Access user information in webhooks/jobs
- Multi-user data operations
- User management features

### `User.get({ email })` / `User.get({ phone })`

Look up a user by email or phone number. This is particularly useful in webhooks where you don't have the user ID but know the user's contact information.

**Signature:**
```typescript
async get(options: { email?: string; phone?: string }): Promise<UserDataInstance | null>
```

**Returns:** `UserDataInstance` if found, or `null` if no user exists with that email/phone

**Examples:**

```typescript
// Look up user by email
const user = await User.get({ email: 'customer@example.com' });
if (!user) {
  return { error: 'User not found' };
}
console.log(user.name);

// Look up user by phone (both formats work)
const user1 = await User.get({ phone: '+1234567890' });
const user2 = await User.get({ phone: '1234567890' });

// Handle not found gracefully
const user = await User.get({ email: 'unknown@example.com' });
if (!user) {
  // User doesn't exist in the system
  return { error: 'User not found' };
}
```

**Use Cases:**
- Webhooks that receive user email/phone from external systems
- Admin tools where operators search by contact info
- Integration with third-party systems that don't have Lua user IDs

### `User.getChatHistory()`

Gets the conversation history for the current user.

**Signature:**
```typescript
async getChatHistory(): Promise<ChatHistoryMessage[]>
```

**Returns:** Array of chat messages

**Example:**
```typescript
const history = await User.getChatHistory();

// Each message has:
// - role: 'user' | 'assistant'
// - content: Array of content items
// - createdAt: ISO timestamp
// - id: Message ID
// - threadId: Thread ID

for (const message of history) {
  console.log(`${message.role}: ${message.content[0].text}`);
}
```

## UserDataInstance

The user object returned by `User.get()` uses a Proxy for convenient property access:

```typescript
const user = await User.get();

// Direct property access
user.email = 'new@email.com';
user.preferences = { theme: 'dark' };

// Or explicit data access
user.data.email = 'new@email.com';
```

### Immutable User Profile (`user._luaProfile`)

Starting in v3.1.0, the user object includes a read-only `_luaProfile` property that provides secure access to core user identity data.

**Properties:**
- `userId` - The unique user identifier
- `fullName` - User's full name
- `mobileNumbers` - Array of mobile numbers
- `emailAddresses` - Array of email addresses

**Important:**
- `_luaProfile` is **read-only** - attempts to modify it are silently ignored
- Uses the `_lua` prefix to avoid conflicts with custom `user.profile` fields
- `user.userId` is deprecated in favor of `user._luaProfile.userId`

**Example:**
```typescript
const user = await User.get();

// ✅ Correct way to access user ID
const userId = user._luaProfile.userId;

// ⚠️ Deprecated (still works, but shows as deprecated in IDE)
const deprecatedId = user.userId;

// ✅ Custom profile field remains unaffected
user.profile = { customData: 'value' }; // Still works for custom data

// ❌ Attempts to modify _luaProfile are ignored
user._luaProfile.userId = 'new-id'; // Silently ignored
```

### Methods

#### `user.update(data)`

Updates user data.

**Parameters:**
- `data` (object) - Data to update

**Returns:** Promise<object> - Updated user data

**Example:**
```typescript
const user = await User.get();

await user.update({
  preferences: {
    theme: 'dark',
    language: 'en',
    notifications: true
  },
  metadata: {
    lastActive: new Date().toISOString()
  }
});
```

#### `user.send(messages)`

Sends messages to the user.

**Parameters:**
- `messages` (ChatMessage[]) - Array of messages to send

**Returns:** Promise<boolean>

**Example:**
```typescript
const user = await User.get();

// Send text message
await user.send([{
  type: 'text',
  text: 'Hello! Your order has shipped.'
}]);

// Send image
await user.send([{
  type: 'image',
  imageUrl: 'https://example.com/product.jpg',
  altText: 'Product image'
}]);

// Send multiple messages
await user.send([
  { type: 'text', text: 'Your order summary:' },
  { type: 'text', text: '- Item 1: $10' },
  { type: 'text', text: 'Total: $10' }
]);
```

#### `user.clear()`

Clears all user data.

**Returns:** Promise<boolean>

**Example:**
```typescript
const user = await User.get();
await user.clear();
```

#### `user.save()`

Saves current user data state.

**Returns:** Promise<boolean>

**Example:**
```typescript
const user = await User.get();
user.data.lastLogin = new Date().toISOString();
await user.save();
```

#### `user.getChatHistory()`

Gets chat history for this user.

**Returns:** Promise<ChatHistoryMessage[]>

**Example:**
```typescript
const user = await User.get();
const history = await user.getChatHistory();

// Analyze conversation
const userMessages = history.filter(m => m.role === 'user');
console.log(`User sent ${userMessages.length} messages`);
```

## Complete Examples

### Update User Preferences

```typescript
import { LuaTool, User } from 'lua-cli/skill';
import { z } from 'zod';

export default class UpdatePreferencesTool implements LuaTool {
  name = 'update_preferences';
  description = 'Update user preferences';
  
  inputSchema = z.object({
    theme: z.enum(['light', 'dark']).optional(),
    language: z.string().optional(),
    notifications: z.boolean().optional()
  });
  
  async execute(input: z.infer<typeof this.inputSchema>) {
    const user = await User.get();
    
    // Get current preferences
    const currentPrefs = user.data.preferences || {};
    
    // Merge with new preferences
    const newPrefs = {
      ...currentPrefs,
      ...input
    };
    
    // Update
    await user.update({ preferences: newPrefs });
    
    return {
      success: true,
      message: 'Preferences updated',
      preferences: newPrefs
    };
  }
}
```

### Send Notification

```typescript
import { LuaTool, User } from 'lua-cli/skill';
import { z } from 'zod';

export default class NotifyUserTool implements LuaTool {
  name = 'notify_user';
  description = 'Send a notification to the user';
  
  inputSchema = z.object({
    title: z.string(),
    message: z.string(),
    urgent: z.boolean().optional()
  });
  
  async execute(input: z.infer<typeof this.inputSchema>) {
    const user = await User.get();
    
    const prefix = input.urgent ? '🚨 URGENT: ' : '📬 ';
    const fullMessage = `${prefix}${input.title}\n\n${input.message}`;
    
    await user.send([{
      type: 'text',
      text: fullMessage
    }]);
    
    // Log the notification
    await user.update({
      lastNotification: {
        title: input.title,
        sentAt: new Date().toISOString(),
        urgent: input.urgent || false
      }
    });
    
    return {
      success: true,
      message: 'Notification sent'
    };
  }
}
```

### Analyze Chat History

```typescript
import { LuaTool, User } from 'lua-cli/skill';
import { z } from 'zod';

export default class ChatAnalyticsTool implements LuaTool {
  name = 'analyze_conversation';
  description = 'Analyze the conversation history';
  
  inputSchema = z.object({});
  
  async execute(input: z.infer<typeof this.inputSchema>) {
    const history = await User.getChatHistory();
    
    const stats = {
      totalMessages: history.length,
      userMessages: history.filter(m => m.role === 'user').length,
      assistantMessages: history.filter(m => m.role === 'assistant').length,
      averageLength: 0,
      topics: [] as string[]
    };
    
    // Calculate average message length
    const lengths = history.map(m => 
      m.content.reduce((sum, c) => sum + (c.text?.length || 0), 0)
    );
    stats.averageLength = Math.round(
      lengths.reduce((a, b) => a + b, 0) / lengths.length
    );
    
    return {
      success: true,
      analytics: stats,
      recentMessages: history.slice(-5).map(m => ({
        role: m.role,
        preview: m.content[0]?.text?.substring(0, 50) + '...'
      }))
    };
  }
}
```

## Message Types

### Text Message
```typescript
{
  type: 'text',
  text: string
}
```

### Image Message
```typescript
{
  type: 'image',
  imageUrl: string,
  altText?: string
}
```

### File Message
```typescript
{
  type: 'file',
  fileUrl: string,
  fileName?: string,
  mimeType?: string
}
```

## Best Practices

### ✅ DO
- Always handle missing user data gracefully
- Use `user.update()` instead of modifying and saving separately
- Check if data exists before accessing nested properties
- Send clear, actionable messages to users

### ❌ DON'T
- Don't assume user data structure
- Don't modify user data without validation
- Don't send empty or malformed messages
- Don't expose internal IDs to users

## Common Patterns

### Safe Data Access

```typescript
const user = await User.get();

// ✅ Safe access with optional chaining
const theme = user.data?.preferences?.theme || 'light';

// ❌ Unsafe - may throw
const theme = user.data.preferences.theme;
```

### Batch Updates

```typescript
const user = await User.get();

// ✅ Update multiple fields at once
await user.update({
  name: 'John Doe',
  email: 'john@example.com',
  preferences: { theme: 'dark' }
});

// ❌ Multiple separate updates (slower)
await user.update({ name: 'John Doe' });
await user.update({ email: 'john@example.com' });
await user.update({ preferences: { theme: 'dark' } });
```

### Message Formatting

```typescript
// ✅ Clear, formatted message
await user.send([{
  type: 'text',
  text: `Order Confirmation\n\nOrder ID: ${orderId}\nTotal: $${total}\n\nThank you for your order!`
}]);

// ❌ Unformatted dump
await user.send([{
  type: 'text',
  text: JSON.stringify(orderData)
}]);
```

## Webhook Example: User Lookup by Phone

A common use case is building webhooks that allow external systems to modify user data. Here's an example of a webhook that updates user routing preferences:

```typescript
import { LuaWebhook, User } from 'lua-cli';

export default new LuaWebhook({
  name: 'update-routing-preference',
  description: 'Update user routing preference by phone number',
  
  execute: async (event) => {
    const { phone, routingEnabled } = event.body;
    
    if (!phone) {
      return { error: 'Phone number is required' };
    }
    
    // Look up user by phone
    const user = await User.get({ phone });
    if (!user) {
      return { error: 'User not found' };
    }
    
    // Update routing preference
    user.routingEnabled = routingEnabled;
    await user.save();
    
    return { 
      success: true, 
      userId: user._luaProfile.userId,
      routingEnabled 
    };
  }
});
```

## Types

### UserLookupOptions

```typescript
interface UserLookupOptions {
  /** Email address to look up */
  email?: string;
  /** Phone number to look up (with or without + prefix) */
  phone?: string;
}
```

## Related APIs

- [Data API](./Data.md) - Store custom user data
- [JobInstance](./instances/JobInstance.md) - Access user from jobs
- [ChatHistoryMessage](./types/ChatHistoryMessage.md) - Message types

## See Also

- [User Data Tool Example](../../template/src/skills/tools/UserDataTool.ts)
- [Notification Patterns](../guides/NotificationPatterns.md)

