Metadata-Version: 2.4
Name: evolution-sdk-python
Version: 0.0.1
Summary: Client Python para a API Evolution
Project-URL: Homepage, https://github.com/jacksonvieiracs/evolution-sdk-python
Project-URL: Repository, https://github.com/jacksonvieiracs/evolution-sdk-python
Project-URL: Issues, https://github.com/jacksonvieiracs/evolution-sdk-python
Author-email: Jackson Vieira <dev.jacksonvieira@gmail.com>, Davidson Gomes <contato@agenciadgcode.com>
License: MIT
Keywords: api,client,evolution,whatsapp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Requires-Dist: python-socketio>=5.11.1
Requires-Dist: requests-toolbelt>=1.0.0
Requires-Dist: requests>=2.25.1
Description-Content-Type: text/markdown

# Evolution Client Python

Python client to interact with the evolutionapi.

## Installation

```bash
pip install evolutionapi
```

## Basic Usage

### Initializing the Client

```python
from evolutionapi.client import EvolutionClient

client = EvolutionClient(
    base_url='http://your-server:port',
    api_token='your-api-token'
)
```

### Instance Management

#### List Instances

```python
instances = client.instances.fetch_instances()
```

#### Create New Instance

```python
from evolutionapi.models.instance import InstanceConfig

# Configuração básica
config = InstanceConfig(
    instanceName="minha-instancia",
    integration="WHATSAPP-BAILEYS",
    qrcode=True
)

# Configuração completa
config = InstanceConfig(
    instanceName="minha-instancia",
    integration="WHATSAPP-BAILEYS",
    token="token_da_instancia",
    number="5511999999999",
    qrcode=True,
    rejectCall=True,
    msgCall="Mensagem de chamada rejeitada",
    groupsIgnore=True,
    alwaysOnline=True,
    readMessages=True,
    readStatus=True,
    syncFullHistory=True
)

new_instance = client.instances.create_instance(config)
```

#### Configure Webhook

```python
from evolutionapi.models.instance import WebhookConfig

config = WebhookConfig(
    url="https://seu-servidor.com/webhook",
    byEvents=True,
    base64=True,
    headers={
        "Authorization": "Bearer seu-token"
    },
    events=[
        "messages.upsert",
        "messages.update",
        "messages.delete",
        "groups.upsert",
        "groups.update",
        "groups.delete",
        "group-participants.update",
        "contacts.upsert",
        "contacts.update",
        "contacts.delete",
        "presence.update",
        "chats.upsert",
        "chats.update",
        "chats.delete",
        "call"
    ]
)

response = client.instances.set_webhook(instance_id, config, instance_token)
```

#### Configure Events

```python
from evolutionapi.models.instance import EventsConfig

config = EventsConfig(
    enabled=True,
    events=[
        "messages.upsert",
        "messages.update",
        "messages.delete",
        "groups.upsert",
        "groups.update",
        "groups.delete",
        "group-participants.update",
        "contacts.upsert",
        "contacts.update",
        "contacts.delete",
        "presence.update",
        "chats.upsert",
        "chats.update",
        "chats.delete",
        "call"
    ]
)

response = client.instances.set_events(instance_id, config, instance_token)
```

#### Configure Chatwoot Integration

```python
from evolutionapi.models.instance import ChatwootConfig

config = ChatwootConfig(
    accountId="seu-account-id",
    token="seu-token",
    url="https://seu-chatwoot.com",
    signMsg=True,
    reopenConversation=True,
    conversationPending=False,
    importContacts=True,
    nameInbox="evolution",
    mergeBrazilContacts=True,
    importMessages=True,
    daysLimitImportMessages=3,
    organization="Evolution Bot",
    logo="https://evolution-api.com/files/evolution-api-favicon.png"
)

response = client.instances.set_chatwoot(instance_id, config, instance_token)
```

#### Delete Instance

```python
response = client.instances.delete_instance(instance_id, instance_token)
```

#### Get Instance Info

```python
response = client.instances.get_instance_info(instance_id, instance_token)
```

#### Get Instance QR Code

```python
response = client.instances.get_instance_qrcode(instance_id, instance_token)
```

#### Get Instance Status

```python
response = client.instances.get_instance_status(instance_id, instance_token)
```

#### Logout Instance

```python
response = client.instances.logout_instance(instance_id, instance_token)
```

#### Restart Instance

```python
response = client.instances.restart_instance(instance_id, instance_token)
```

### Instance Operations

#### Connect Instance

```python
state = client.instance_operations.connect(instance_id, instance_token)
```

#### Check Connection State

```python
state = client.instance_operations.get_connection_state(instance_id, instance_token)
```

#### Set Presence

```python
from evolutionapi.models.presence import PresenceConfig, PresenceStatus

# Definir como disponível
config = PresenceConfig(
    presence=PresenceStatus.AVAILABLE
)

# Definir como indisponível
config = PresenceConfig(
    presence=PresenceStatus.UNAVAILABLE
)

response = client.instance_operations.set_presence(instance_id, config, instance_token)
```

### Sending Messages

#### Text Message

```python
from evolutionapi.models.message import TextMessage, QuotedMessage

# Mensagem simples
message = TextMessage(
    number="5511999999999",
    text="Olá, como você está?",
    delay=1000  # delay opcional em ms
)

# Mensagem com menções
message = TextMessage(
    number="5511999999999",
    text="@everyone Olá a todos!",
    mentionsEveryOne=True,
    mentioned=["5511999999999", "5511888888888"]
)

# Mensagem com link preview
message = TextMessage(
    number="5511999999999",
    text="Confira este link: https://exemplo.com",
    linkPreview=True
)

# Mensagem com citação
quoted = QuotedMessage(
    key={
        "remoteJid": "5511999999999@s.whatsapp.net",
        "fromMe": False,
        "participant": "5511999999999@s.whatsapp.net",
        "id": "123456789",
        "owner": "5511999999999@s.whatsapp.net"
    }
)

message = TextMessage(
    number="5511999999999",
    text="Esta é uma resposta",
    quoted=quoted
)

response = client.messages.send_text(instance_id, message, instance_token)
```

#### Media Message

```python
from evolutionapi.models.message import MediaMessage, MediaType, QuotedMessage

# Mensagem com imagem
message = MediaMessage(
    number="5511999999999",
    mediatype=MediaType.IMAGE.value,
    mimetype="image/jpeg",
    caption="Minha imagem",
    media="base64_da_imagem_ou_url",
    fileName="imagem.jpg",
    delay=1000  # delay opcional
)

# Mensagem com vídeo
message = MediaMessage(
    number="5511999999999",
    mediatype=MediaType.VIDEO.value,
    mimetype="video/mp4",
    caption="Meu vídeo",
    media="base64_do_video_ou_url",
    fileName="video.mp4"
)

# Mensagem com documento
message = MediaMessage(
    number="5511999999999",
    mediatype=MediaType.DOCUMENT.value,
    mimetype="application/pdf",
    caption="Meu documento",
    media="base64_do_documento_ou_url",
    fileName="documento.pdf"
)

# Mensagem com menções
message = MediaMessage(
    number="5511999999999",
    mediatype=MediaType.IMAGE.value,
    mimetype="image/jpeg",
    caption="@everyone Olhem esta imagem!",
    media="base64_da_imagem",
    mentionsEveryOne=True,
    mentioned=["5511999999999", "5511888888888"]
)

response = client.messages.send_media(instance_id, message, instance_token)
```

#### Status Message

```python
from evolutionapi.models.message import StatusMessage, StatusType, FontType

# Status de texto
message = StatusMessage(
    type=StatusType.TEXT,
    content="Meu status de texto",
    caption="Legenda opcional",
    backgroundColor="#FF0000",
    font=FontType.BEBASNEUE_REGULAR,
    allContacts=True
)

# Status de imagem
message = StatusMessage(
    type=StatusType.IMAGE,
    content="base64_da_imagem",
    caption="Minha imagem de status"
)

# Status de vídeo
message = StatusMessage(
    type=StatusType.VIDEO,
    content="base64_do_video",
    caption="Meu vídeo de status"
)

# Status de áudio
message = StatusMessage(
    type=StatusType.AUDIO,
    content="base64_do_audio",
    caption="Meu áudio de status"
)

response = client.messages.send_status(instance_id, message, instance_token)
```

#### Location Message

```python
from evolutionapi.models.message import LocationMessage

message = LocationMessage(
    number="5511999999999",
    name="Localização",
    address="Endereço completo",
    latitude=-23.550520,
    longitude=-46.633308,
    delay=1000  # delay opcional
)

response = client.messages.send_location(instance_id, message, instance_token)
```

#### Contact Message

```python
from evolutionapi.models.message import ContactMessage, Contact

contact = Contact(
    fullName="Nome Completo",
    wuid="5511999999999",
    phoneNumber="5511999999999",
    organization="Empresa",
    email="email@exemplo.com",
    url="https://exemplo.com"
)

message = ContactMessage(
    number="5511999999999",
    contact=[contact]
)

response = client.messages.send_contact(instance_id, message, instance_token)
```

#### Reaction Message

```python
from evolutionapi.models.message import ReactionMessage

message = ReactionMessage(
    key={
        "remoteJid": "5511999999999@s.whatsapp.net",
        "fromMe": False,
        "participant": "5511999999999@s.whatsapp.net",
        "id": "123456789",
        "owner": "5511999999999@s.whatsapp.net"
    },
    reaction="👍"
)

response = client.messages.send_reaction(instance_id, message, instance_token)
```

#### Poll Message

```python
from evolutionapi.models.message import PollMessage

message = PollMessage(
    number="5511999999999",
    name="Minha Enquete",
    selectableCount=1,  # número de opções que podem ser selecionadas
    values=["Opção 1", "Opção 2", "Opção 3"],
    delay=1000  # delay opcional
)

response = client.messages.send_poll(instance_id, message, instance_token)
```

#### Button Message

```python
from evolutionapi.models.message import ButtonMessage, Button

# Botão de resposta simples
buttons = [
    Button(
        type="reply",
        displayText="Opção 1",
        id="1"
    ),
    Button(
        type="reply",
        displayText="Opção 2",
        id="2"
    )
]

# Botão com URL
buttons = [
    Button(
        type="url",
        displayText="Visitar Site",
        url="https://exemplo.com"
    )
]

# Botão com número de telefone
buttons = [
    Button(
        type="phoneNumber",
        displayText="Ligar",
        phoneNumber="5511999999999"
    )
]

# Botão com código de cópia
buttons = [
    Button(
        type="copyCode",
        displayText="Copiar Código",
        copyCode="ABC123"
    )
]

message = ButtonMessage(
    number="5511999999999",
    title="Título",
    description="Descrição",
    footer="Rodapé",
    buttons=buttons,
    delay=1000  # delay opcional
)

response = client.messages.send_buttons(instance_id, message, instance_token)
```

#### List Message

```python
from evolutionapi.models.message import ListMessage, ListSection, ListRow

rows = [
    ListRow(
        title="Item 1",
        description="Descrição do item 1",
        rowId="1"
    ),
    ListRow(
        title="Item 2",
        description="Descrição do item 2",
        rowId="2"
    )
]

section = ListSection(
    title="Seção 1",
    rows=rows
)

message = ListMessage(
    number="5511999999999",
    title="Título da Lista",
    description="Descrição da lista",
    buttonText="Clique aqui",
    footerText="Rodapé",
    sections=[section],
    delay=1000  # delay opcional
)

response = client.messages.send_list(instance_id, message, instance_token)
```

### Group Management

#### Create Group

```python
from evolutionapi.models.group import CreateGroup

config = CreateGroup(
    subject="Nome do Grupo",
    participants=["5511999999999", "5511888888888"],
    description="Descrição do grupo"
)

response = client.group.create_group(instance_id, config, instance_token)
```

#### Update Group Picture

```python
from evolutionapi.models.group import GroupPicture

config = GroupPicture(
    image="base64_da_imagem"
)

response = client.group.update_group_picture(instance_id, "group_jid", config, instance_token)
```

#### Update Group Subject

```python
from evolutionapi.models.group import GroupSubject

config = GroupSubject(
    subject="Novo Nome do Grupo"
)

response = client.group.update_group_subject(instance_id, "group_jid", config, instance_token)
```

#### Update Group Description

```python
from evolutionapi.models.group import GroupDescription

config = GroupDescription(
    description="Nova descrição do grupo"
)

response = client.group.update_group_description(instance_id, "group_jid", config, instance_token)
```

#### Send Group Invite

```python
from evolutionapi.models.group import GroupInvite

config = GroupInvite(
    groupJid="group_jid",
    description="Convite para o grupo",
    numbers=["5511999999999", "5511888888888"]
)

response = client.group.send_group_invite(instance_id, config, instance_token)
```

#### Manage Participants

```python
from evolutionapi.models.group import UpdateParticipant

# Adicionar participantes
config = UpdateParticipant(
    action="add",
    participants=["5511999999999", "5511888888888"]
)

# Remover participantes
config = UpdateParticipant(
    action="remove",
    participants=["5511999999999"]
)

# Promover a administrador
config = UpdateParticipant(
    action="promote",
    participants=["5511999999999"]
)

# Rebaixar de administrador
config = UpdateParticipant(
    action="demote",
    participants=["5511999999999"]
)

response = client.group.update_participant(instance_id, "group_jid", config, instance_token)
```

#### Update Group Settings

```python
from evolutionapi.models.group import UpdateSetting

# Ativar modo anúncio
config = UpdateSetting(
    action="announcement"
)

# Desativar modo anúncio
config = UpdateSetting(
    action="not_announcement"
)

# Bloquear grupo
config = UpdateSetting(
    action="locked"
)

# Desbloquear grupo
config = UpdateSetting(
    action="unlocked"
)

response = client.group.update_setting(instance_id, "group_jid", config, instance_token)
```

#### Toggle Ephemeral Messages

```python
from evolutionapi.models.group import ToggleEphemeral

config = ToggleEphemeral(
    expiration=86400  # 24 horas em segundos
)

response = client.group.toggle_ephemeral(instance_id, "group_jid", config, instance_token)
```

### Profile Management

#### Fetch Profile

```python
from evolutionapi.models.profile import FetchProfile

config = FetchProfile(
    number="5511999999999"
)

response = client.profile.fetch_profile(instance_id, config, instance_token)
```

#### Update Profile Name

```python
from evolutionapi.models.profile import ProfileName

config = ProfileName(
    name="Novo Nome"
)

response = client.profile.update_profile_name(instance_id, config, instance_token)
```

#### Update Status

```python
from evolutionapi.models.profile import ProfileStatus

config = ProfileStatus(
    status="Novo status"
)

response = client.profile.update_profile_status(instance_id, config, instance_token)
```

#### Update Profile Picture

```python
from evolutionapi.models.profile import ProfilePicture

config = ProfilePicture(
    picture="base64_da_imagem"
)

response = client.profile.update_profile_picture(instance_id, config, instance_token)
```

#### Configure Privacy Settings

```python
from evolutionapi.models.profile import PrivacySettings

config = PrivacySettings(
    readreceipts="all",           # "all" ou "none"
    profile="contacts",           # "all", "contacts", "contact_blacklist" ou "none"
    status="contacts",            # "all", "contacts", "contact_blacklist" ou "none"
    online="all",                 # "all" ou "match_last_seen"
    last="contacts",              # "all", "contacts", "contact_blacklist" ou "none"
    groupadd="contacts"           # "all", "contacts" ou "contact_blacklist"
)

response = client.profile.update_privacy_settings(instance_id, config, instance_token)
```

### Chat Operations

#### Check WhatsApp Numbers

```python
from evolutionapi.models.chat import CheckIsWhatsappNumber

config = CheckIsWhatsappNumber(
    numbers=["5511999999999", "5511888888888"]
)

response = client.chat.check_is_whatsapp_numbers(instance_id, config, instance_token)
```

#### Mark Message as Read

```python
from evolutionapi.models.chat import ReadMessage

message = ReadMessage(
    remote_jid="5511999999999@s.whatsapp.net",
    from_me=False,
    id="message_id"
)

response = client.chat.mark_message_as_read(instance_id, [message], instance_token)
```

#### Archive Chat

```python
from evolutionapi.models.chat import ArchiveChat

config = ArchiveChat(
    last_message={
        "key": {
            "remoteJid": "5511999999999@s.whatsapp.net",
            "fromMe": False,
            "id": "message_id",
            "participant": "5511999999999@s.whatsapp.net"
        },
        "message": {
            "conversation": "Última mensagem"
        }
    },
    chat="5511999999999@s.whatsapp.net",
    archive=True  # True para arquivar, False para desarquivar
)

response = client.chat.archive_chat(instance_id, config, instance_token)
```

#### Mark Chat as Unread

```python
from evolutionapi.models.chat import UnreadChat

config = UnreadChat(
    last_message={
        "key": {
            "remoteJid": "5511999999999@s.whatsapp.net",
            "fromMe": False,
            "id": "message_id",
            "participant": "5511999999999@s.whatsapp.net"
        },
        "message": {
            "conversation": "Última mensagem"
        }
    },
    chat="5511999999999@s.whatsapp.net"
)

response = client.chat.unread_chat(instance_id, config, instance_token)
```

#### Get Chat Profile Picture

```python
from evolutionapi.models.chat import ProfilePicture

config = ProfilePicture(
    number="5511999999999"
)

response = client.chat.get_chat_profile_picture(instance_id, config, instance_token)
```

#### Download Media Message

```python
from evolutionapi.models.chat import MediaMessage

config = MediaMessage(
    message={
        "key": {
            "remoteJid": "5511999999999@s.whatsapp.net",
            "fromMe": False,
            "id": "message_id",
            "participant": "5511999999999@s.whatsapp.net"
        },
        "message": {
            "imageMessage": {
                "jpegThumbnail": "base64_da_imagem"
            }
        }
    },
    convert_to_mp4=True  # opcional, para converter vídeos para MP4
)

response = client.chat.download_media_message(instance_id, config, instance_token)
```

#### Update Message

```python
from evolutionapi.models.chat import UpdateMessage

config = UpdateMessage(
    number="5511999999999",
    key={
        "remoteJid": "5511999999999@s.whatsapp.net",
        "fromMe": False,
        "id": "message_id",
        "participant": "5511999999999@s.whatsapp.net"
    },
    text="Mensagem atualizada"
)

response = client.chat.update_message(instance_id, config, instance_token)
```

#### Set Presence

```python
from evolutionapi.models.chat import Presence

config = Presence(
    number="5511999999999",
    delay=1000,  # delay em ms
    presence="composing"  # "composing", "recording", "paused"
)

response = client.chat.set_presence(instance_id, config, instance_token)
```

### Calls

#### Simulate Call

```python
from evolutionapi.models.call import FakeCall

# Chamada de voz
config = FakeCall(
    number="5511999999999",
    isVideo=False,
    callDuration=30  # duração em segundos
)

# Chamada de vídeo
config = FakeCall(
    number="5511999999999",
    isVideo=True,
    callDuration=30  # duração em segundos
)

response = client.calls.fake_call(instance_id, config, instance_token)
```

### Labels

#### Manage Labels

```python
from evolutionapi.models.label import HandleLabel

# Adicionar etiqueta
config = HandleLabel(
    number="5511999999999",
    label_id="label_id",
    action="add"
)

# Remover etiqueta
config = HandleLabel(
    number="5511999999999",
    label_id="label_id",
    action="remove"
)

response = client.label.handle_label(instance_id, config, instance_token)
```

## WebSocket

The Evolution API client supports WebSocket connection to receive real-time events. Here's a guide on how to use it:

### Prerequisites

Before using WebSocket, you need to:

1. Enable WebSocket in your Evolution API by setting the environment variable:

```bash
WEBSOCKET_ENABLED=true
```

2. Configure WebSocket events for your instance using the WebSocket service:

```python
from evolutionapi.models.websocket import WebSocketConfig

# Configure WebSocket events
config = WebSocketConfig(
    enabled=True,
    events=[
        "APPLICATION_STARTUP",
        "QRCODE_UPDATED",
        "MESSAGES_SET",
        "MESSAGES_UPSERT",
        "MESSAGES_UPDATE",
        "MESSAGES_DELETE",
        "SEND_MESSAGE",
        "CONTACTS_SET",
        "CONTACTS_UPSERT",
        "CONTACTS_UPDATE",
        "PRESENCE_UPDATE",
        "CHATS_SET",
        "CHATS_UPSERT",
        "CHATS_UPDATE",
        "CHATS_DELETE",
        "GROUPS_UPSERT",
        "GROUP_UPDATE",
        "GROUP_PARTICIPANTS_UPDATE",
        "CONNECTION_UPDATE",
        "LABELS_EDIT",
        "LABELS_ASSOCIATION",
        "CALL",
        "TYPEBOT_START",
        "TYPEBOT_CHANGE_STATUS"
    ]
)

# Set WebSocket configuration
response = client.websocket.set_websocket(instance_id, config, instance_token)

# Get current WebSocket configuration
websocket_info = client.websocket.find_websocket(instance_id, instance_token)
print(f"WebSocket enabled: {websocket_info.enabled}")
print(f"Configured events: {websocket_info.events}")
```

### Basic Configuration

There are two ways to create a WebSocket manager:

1. Using the client's helper method (recommended):

```python
# Create WebSocket manager using the client
websocket = client.create_websocket(
    instance_id="test",
    api_token="your_api_token",
    max_retries=5,        # Maximum number of reconnection attempts
    retry_delay=1.0       # Initial delay between attempts in seconds
)
```

2. Creating the manager directly:

```python
from evolutionapi.client import EvolutionClient
import logging

# Logging configuration
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Initialize client
client = EvolutionClient(
    base_url="http://localhost:8081",
    api_token="your-api-token"
)

# Create WebSocket manager
websocket = client.create_websocket(
    instance_id="test",
    api_token="your_api_token",
    max_retries=5,
    retry_delay=1.0
)
```

### Registering Event Handlers

You can register handlers for different types of events:

```python
def handle_message(data):
    print(f"New message received: {data}")

def handle_qrcode(data):
    print(f"QR Code updated: {data}")

# Registering handlers
websocket.on("messages.upsert", handle_message)
websocket.on("qrcode.updated", handle_qrcode)
```

### Available Events

The available events are:

#### Instance Events

- `application.startup`: Triggered when the application starts
- `instance.create`: Triggered when a new instance is created
- `instance.delete`: Triggered when an instance is deleted
- `remove.instance`: Triggered when an instance is removed
- `logout.instance`: Triggered when an instance logs out

#### Connection and QR Code Events

- `qrcode.updated`: Triggered when the QR Code is updated
- `connection.update`: Triggered when connection status changes
- `status.instance`: Triggered when instance status changes
- `creds.update`: Triggered when credentials are updated

#### Message Events

- `messages.set`: Triggered when messages are set
- `messages.upsert`: Triggered when new messages are received
- `messages.edited`: Triggered when messages are edited
- `messages.update`: Triggered when messages are updated
- `messages.delete`: Triggered when messages are deleted
- `send.message`: Triggered when a message is sent
- `messaging-history.set`: Triggered when messaging history is set

#### Contact Events

- `contacts.set`: Triggered when contacts are set
- `contacts.upsert`: Triggered when new contacts are added
- `contacts.update`: Triggered when contacts are updated

#### Chat Events

- `chats.set`: Triggered when chats are set
- `chats.update`: Triggered when chats are updated
- `chats.upsert`: Triggered when new chats are added
- `chats.delete`: Triggered when chats are deleted

#### Group Events

- `groups.upsert`: Triggered when groups are created/updated
- `groups.update`: Triggered when groups are updated
- `group-participants.update`: Triggered when group participants are updated

#### Presence Events

- `presence.update`: Triggered when presence status is updated

#### Call Events

- `call`: Triggered when there's a call

#### Typebot Events

- `typebot.start`: Triggered when a typebot starts
- `typebot.change-status`: Triggered when typebot status changes

#### Label Events

- `labels.edit`: Triggered when labels are edited
- `labels.association`: Triggered when labels are associated/disassociated

### Example with Specific Events

```python
def handle_messages(data):
    logger.info(f"New message: {data}")

def handle_contacts(data):
    logger.info(f"Contacts updated: {data}")

def handle_groups(data):
    logger.info(f"Groups updated: {data}")

def handle_presence(data):
    logger.info(f"Presence status: {data}")

# Registering handlers for different events
websocket.on("messages.upsert", handle_messages)
websocket.on("contacts.upsert", handle_contacts)
websocket.on("groups.upsert", handle_groups)
websocket.on("presence.update", handle_presence)
```

### Complete Example

```python
from evolutionapi.client import EvolutionClient
from evolutionapi.models.websocket import WebSocketConfig
import logging
import time

# Logging configuration
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

def handle_message(data):
    logger.info(f"New message received: {data}")

def handle_qrcode(data):
    logger.info(f"QR Code updated: {data}")

def handle_connection(data):
    logger.info(f"Connection status: {data}")

def main():
    # Initialize client
    client = EvolutionClient(
        base_url="http://localhost:8081",
        api_token="your-api-token"
    )

    # Configure WebSocket
    websocket_config = WebSocketConfig(
        enabled=True,
        events=[
            "MESSAGES_UPSERT",
            "QRCODE_UPDATED",
            "CONNECTION_UPDATE"
        ]
    )

    # Set WebSocket configuration
    client.websocket.set_websocket("instance_id", websocket_config, "instance_token")

    # Create WebSocket manager
    websocket = client.create_websocket(
        instance_id="instance_id",
        api_token="your_api_token",
        max_retries=5,
        retry_delay=1.0
    )

    # Register handlers
    websocket.on("messages.upsert", handle_message)
    websocket.on("qrcode.updated", handle_qrcode)
    websocket.on("connection.update", handle_connection)

    try:
        # Connect to WebSocket
        websocket.connect()
        logger.info("Connected to WebSocket. Waiting for events...")

        # Keep the program running
        while True:
            time.sleep(1)

    except KeyboardInterrupt:
        logger.info("Closing connection...")
        websocket.disconnect()
    except Exception as e:
        logger.error(f"Error: {e}")
        websocket.disconnect()

if __name__ == "__main__":
    main()
```

### Additional Features

#### Automatic Reconnection

The WebSocket Manager has automatic reconnection with exponential backoff:

```python
websocket = client.create_websocket(
    instance_id="test",
    api_token="your_api_token",
    max_retries=5,        # Maximum number of reconnection attempts
    retry_delay=1.0       # Initial delay between attempts in seconds
)
```

#### Logging

The WebSocket Manager uses Python's logging system. You can adjust the log level as needed:

```python
# For more details
logging.getLogger("evolutionapi.services.websocket").setLevel(logging.DEBUG)
```

### Error Handling

The WebSocket Manager has robust error handling:

- Automatic reconnection on disconnection
- Detailed error logs
- Invalid event handling
- Data validation

### Usage Tips

1. Always use try/except when connecting to WebSocket
2. Implement handlers for all events you need to monitor
3. Use logging for debugging and monitoring
4. Consider implementing a heartbeat mechanism if needed
5. Keep your API token secure and don't expose it in logs

## Contributing

This project uses Python >= 3.8 and uv:

### Setup Development Environment

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh

git clone https://github.com/EvolutionAPI/evolution-client-python.git
cd evolution-client-python

uv sync
```

### Code Quality

This project uses [ruff](https://docs.astral.sh/ruff/) for linting and formatting:

```bash
# Format code
uv run ruff format

# Check linting
uv run ruff check

# Fix auto-fixable issues
uv run ruff check --fix
```

### Building and Publishing

```bash
# Build the package
uv build

# Publish to PyPI (requires credentials)
uv publish

# Or use the provided script
./publish.sh
```

### Adding Dependencies

```bash
# Add a runtime dependency
uv add package-name

# Add a development dependency
uv add --dev package-name
```

## License

MIT
