API_REFERENCE.md 14 KB

API Reference - Gestor de Grupos WhatsApp Web

Índice

Modelos

ww.group

Modelo principal para gestión de grupos de WhatsApp Web.

Campos

Campo Tipo Descripción
name Char Nombre del grupo (requerido)
whatsapp_web_id Char ID único del grupo en WhatsApp Web
whatsapp_account_id Many2one Cuenta de WhatsApp asociada
channel_id Many2one Canal de discusión creado
contact_ids Many2many Contactos miembros del grupo

Métodos

_process_messages(messages_data)

Procesa mensajes de WhatsApp y los crea en el canal de discusión.

Parámetros:

  • messages_data (list): Lista de mensajes de WhatsApp

Retorna: bool - True si se procesó correctamente

Ejemplo:

group = self.env['ww.group'].browse(1)
messages = [
    {
        'id': {'_serialized': '3EB0C767D26A3D1B7B4A'},
        'body': 'Hola grupo!',
        'author': '5215551234567@c.us',
        'timestamp': 1640995200,
        'hasQuotedMsg': False
    }
]
result = group._process_messages(messages)
_create_discussion_channel()

Crea un canal de discusión para el grupo.

Parámetros: Ninguno

Retorna: discuss.channel|False - Canal creado o False si falla

Ejemplo:

group = self.env['ww.group'].browse(1)
channel = group._create_discussion_channel()
if channel:
    print(f"Canal creado: {channel.name}")
_update_discussion_channel()

Actualiza los miembros del canal de discusión.

Parámetros: Ninguno

Retorna: discuss.channel|False - Canal actualizado o False si falla

Ejemplo:

group = self.env['ww.group'].browse(1)
channel = group._update_discussion_channel()
sync_ww_contacts_groups()

Sincroniza todos los grupos y contactos desde WhatsApp Web.

Parámetros: Ninguno (método de modelo)

Retorna: bool - True si se sincronizó correctamente

Ejemplo:

# Sincronización completa
self.env['ww.group'].sync_ww_contacts_groups()

# Sincronización desde un grupo específico
group = self.env['ww.group'].browse(1)
# (Este método es estático, no se llama desde instancia)
send_whatsapp_message(body, attachment=None, wa_template_id=None)

Envía un mensaje WhatsApp al grupo.

Parámetros:

  • body (str): Contenido del mensaje
  • attachment (ir.attachment, opcional): Archivo adjunto
  • wa_template_id (whatsapp.template, opcional): Plantilla WhatsApp

Retorna: whatsapp.message - Mensaje creado y enviado

Ejemplo:

group = self.env['ww.group'].browse(1)
attachment = self.env['ir.attachment'].browse(1)
template = self.env['whatsapp.template'].browse(1)

message = group.send_whatsapp_message(
    body="Mensaje importante para el grupo",
    attachment=attachment,
    wa_template_id=template
)
action_send_whatsapp_message()

Abre el composer de WhatsApp para enviar mensaje al grupo.

Parámetros: Ninguno

Retorna: dict - Acción de ventana para abrir composer

Ejemplo:

group = self.env['ww.group'].browse(1)
action = group.action_send_whatsapp_message()
# Retorna acción para abrir ventana del composer

ww.contact (Extiende res.partner)

Extensión del modelo de contactos para WhatsApp Web.

Campos Adicionales

Campo Tipo Descripción
whatsapp_web_id Char ID único del contacto en WhatsApp Web
group_ids Many2many Grupos donde participa el contacto

Relaciones

  • group_ids: Relación many2many con ww.group
  • channel_ids: Relación con canales de discusión
  • meeting_ids: Relación con eventos de calendario
  • sla_ids: Relación con SLAs de helpdesk

ww.role

Modelo para roles de miembros en grupos.

Campos

Campo Tipo Descripción
name Char Nombre del rol (requerido)
description Text Descripción del rol

ww.group_contact_rel

Modelo de relación entre grupos y contactos con información adicional.

Campos

Campo Tipo Descripción
group_id Many2one Referencia al grupo
contact_id Many2one Referencia al contacto
is_admin Boolean Si es administrador del grupo
is_super_admin Boolean Si es super administrador
role_id Many2one Rol asignado en el grupo

Constraints

  • group_contact_uniq: Constraint único para evitar duplicados (group_id, contact_id)

Ejemplos de Uso

Sincronización de Grupos

# Sincronización completa
self.env['ww.group'].sync_ww_contacts_groups()

# Verificar grupos sincronizados
groups = self.env['ww.group'].search([])
for group in groups:
    print(f"Grupo: {group.name}")
    print(f"Miembros: {len(group.contact_ids)}")
    print(f"Canal: {group.channel_id.name if group.channel_id else 'Sin canal'}")

Creación Manual de Grupo

# Crear grupo manualmente
group = self.env['ww.group'].create({
    'name': 'Mi Grupo Personalizado',
    'whatsapp_web_id': '120363158956331133@g.us',
    'whatsapp_account_id': account.id
})

# Agregar contactos al grupo
contacts = self.env['res.partner'].search([('whatsapp_web_id', '!=', False)])
group.contact_ids = [(6, 0, contacts.ids)]

# Crear canal de discusión
channel = group._create_discussion_channel()

Gestión de Contactos

# Buscar contactos de WhatsApp Web
contacts = self.env['res.partner'].search([
    ('whatsapp_web_id', '!=', False)
])

# Agregar contacto a grupo
group = self.env['ww.group'].browse(1)
contact = self.env['res.partner'].browse(1)

# Crear relación con rol
rel = self.env['ww.group_contact_rel'].create({
    'group_id': group.id,
    'contact_id': contact.id,
    'is_admin': True,
    'role_id': admin_role.id
})

Envío de Mensajes a Grupos

# Envío directo
group = self.env['ww.group'].browse(1)
message = group.send_whatsapp_message("Mensaje importante!")

# Envío con adjunto
attachment = self.env['ir.attachment'].create({
    'name': 'documento.pdf',
    'type': 'binary',
    'datas': base64.b64encode(pdf_content),
    'mimetype': 'application/pdf'
})

message = group.send_whatsapp_message(
    body="Adjunto documento importante",
    attachment=attachment
)

# Envío usando composer
action = group.action_send_whatsapp_message()

Procesamiento de Mensajes

# Procesar mensajes de un grupo
group = self.env['ww.group'].browse(1)
messages_data = [
    {
        'id': {'_serialized': 'msg_id_123'},
        'body': 'Mensaje de prueba',
        'author': '5215551234567@c.us',
        'timestamp': 1640995200,
        'hasQuotedMsg': True,
        'quotedMsg': {
            'body': 'Mensaje citado',
            'author': '5215557654321@c.us'
        }
    }
]

result = group._process_messages(messages_data)

Gestión de Roles

# Crear roles
admin_role = self.env['ww.role'].create({
    'name': 'Administrador',
    'description': 'Administrador del grupo'
})

member_role = self.env['ww.role'].create({
    'name': 'Miembro',
    'description': 'Miembro regular del grupo'
})

# Asignar roles a contactos en grupos
group = self.env['ww.group'].browse(1)
for contact in group.contact_ids:
    rel = self.env['ww.group_contact_rel'].search([
        ('group_id', '=', group.id),
        ('contact_id', '=', contact.id)
    ])
    
    if contact.is_admin:
        rel.role_id = admin_role.id
    else:
        rel.role_id = member_role.id

Consultas Avanzadas

# Grupos con más de 10 miembros
large_groups = self.env['ww.group'].search([
    ('contact_ids', '!=', False)
])
large_groups = [g for g in large_groups if len(g.contact_ids) > 10]

# Contactos administradores
admin_contacts = self.env['res.partner'].search([
    ('group_ids', '!=', False)
])
admin_contacts = [c for c in admin_contacts 
                  if any(rel.is_admin for rel in c.group_ids)]

# Grupos sin canal
groups_without_channel = self.env['ww.group'].search([
    ('channel_id', '=', False)
])

Respuestas de API

Respuesta de getGroups()

[
    {
        "id": {
            "_serialized": "120363158956331133@g.us"
        },
        "name": "Mi Grupo de Trabajo",
        "description": "Grupo para coordinación de proyectos",
        "members": [
            {
                "id": {
                    "_serialized": "5215551234567@c.us"
                },
                "number": "5551234567",
                "name": "Juan Pérez",
                "pushname": "Juan",
                "isAdmin": true,
                "isSuperAdmin": false
            },
            {
                "id": {
                    "_serialized": "5215557654321@c.us"
                },
                "number": "5557654321",
                "name": "María García",
                "pushname": "María",
                "isAdmin": false,
                "isSuperAdmin": false
            }
        ],
        "messages": [
            {
                "id": {
                    "_serialized": "3EB0C767D26A3D1B7B4A"
                },
                "body": "Hola grupo!",
                "author": "5215551234567@c.us",
                "timestamp": 1640995200,
                "hasQuotedMsg": false,
                "quotedParticipant": null,
                "quotedMsg": null
            }
        ]
    }
]

Respuesta de Creación de Canal

# Respuesta exitosa
{
    'id': 123,
    'name': '📱 Mi Grupo de Trabajo',
    'channel_type': 'channel',
    'member_count': 5
}

# Respuesta de error (False)
False

Respuesta de Envío de Mensaje

# Mensaje enviado exitosamente
{
    'id': 456,
    'state': 'sent',
    'msg_uid': '3EB0C767D26A3D1B7B4A_120363158956331133@g.us',
    'body': 'Mensaje importante!',
    'recipient_type': 'group'
}

Manejo de Errores

Excepciones Comunes

ValueError - Grupo sin cuenta

try:
    group.send_whatsapp_message("Mensaje")
except ValueError as e:
    print(f"Error: {e}")  # "Group must have a WhatsApp account configured"

ValidationError - Datos inválidos

from odoo.exceptions import ValidationError

try:
    group = self.env['ww.group'].create({
        'name': '',  # Nombre vacío
        'whatsapp_web_id': 'invalid_id'
    })
except ValidationError as e:
    print(f"Error de validación: {e}")

Logs de Debugging

import logging
_logger = logging.getLogger(__name__)

# Log de sincronización
_logger.info(f"Procesando grupo {group_name}: {len(participants)} participantes encontrados")

# Log de error
_logger.error("Error en la sincronización de grupos para la cuenta %s: %s", 
              account.name, str(e))

# Log de advertencia
_logger.warning(f"Grupo {group_name} no tiene participantes en la respuesta de la API")

Configuración de Cron Jobs

Configuración Básica

<record id="ir_cron_sync_ww_contacts_groups" model="ir.cron">
    <field name="name">Sincronizar Contactos y Grupos WhatsApp Web</field>
    <field name="model_id" ref="model_ww_group"/>
    <field name="state">code</field>
    <field name="code">model.sync_ww_contacts_groups()</field>
    <field name="interval_number">1</field>
    <field name="interval_type">hours</field>
    <field name="active" eval="False"/>
</record>

Configuración Personalizada

# Crear cron job personalizado
cron = self.env['ir.cron'].create({
    'name': 'Sincronización Personalizada',
    'model_id': self.env.ref('whatsapp_web_groups.model_ww_group').id,
    'state': 'code',
    'code': 'model.sync_ww_contacts_groups()',
    'interval_number': 2,
    'interval_type': 'hours',
    'active': True,
    'user_id': self.env.user.id
})

Optimización de Performance

Sincronización en Lotes

# Procesar grupos en lotes para evitar timeouts
def sync_groups_in_batches(self, batch_size=10):
    accounts = self.env['whatsapp.account'].search([])
    
    for account in accounts:
        groups_data = account.get_groups()
        
        # Procesar en lotes
        for i in range(0, len(groups_data), batch_size):
            batch = groups_data[i:i + batch_size]
            self._process_groups_batch(batch, account)
            
            # Commit después de cada lote
            self._cr.commit()

Creación Bulk de Mensajes

def _process_messages_bulk(self, messages_data):
    """Procesar mensajes en bulk para mejor performance"""
    if not messages_data:
        return True
    
    # Preparar valores para creación bulk
    message_vals_list = []
    for msg_data in messages_data:
        message_vals = self._prepare_message_vals(msg_data)
        message_vals_list.append(message_vals)
    
    # Crear todos los mensajes de una vez
    if message_vals_list:
        self.env['mail.message'].create(message_vals_list)
    
    return True

Limpieza y Mantenimiento

Limpiar Grupos Vacíos

def cleanup_empty_groups(self):
    """Eliminar grupos sin contactos"""
    empty_groups = self.env['ww.group'].search([
        ('contact_ids', '=', False)
    ])
    
    if empty_groups:
        _logger.info(f"Eliminando {len(empty_groups)} grupos vacíos")
        empty_groups.unlink()

Limpiar Contactos Huérfanos

def cleanup_orphan_contacts(self):
    """Limpiar contactos sin grupos"""
    orphan_contacts = self.env['res.partner'].search([
        ('whatsapp_web_id', '!=', False),
        ('group_ids', '=', False)
    ])
    
    if orphan_contacts:
        _logger.info(f"Limpiando {len(orphan_contacts)} contactos huérfanos")
        orphan_contacts.write({'whatsapp_web_id': False})

Regenerar Canales Perdidos

def regenerate_missing_channels(self):
    """Regenerar canales que se perdieron"""
    groups_without_channel = self.env['ww.group'].search([
        ('channel_id', '=', False),
        ('contact_ids', '!=', False)
    ])
    
    for group in groups_without_channel:
        try:
            channel = group._create_discussion_channel()
            if channel:
                _logger.info(f"Canal regenerado para grupo {group.name}")
        except Exception as e:
            _logger.error(f"Error regenerando canal para {group.name}: {e}")