|
@@ -0,0 +1,262 @@
|
|
|
|
|
+from odoo import models, fields, api
|
|
|
|
|
+import logging
|
|
|
|
|
+from datetime import datetime
|
|
|
|
|
+
|
|
|
|
|
+_logger = logging.getLogger(__name__)
|
|
|
|
|
+
|
|
|
|
|
+class WWGroup(models.Model):
|
|
|
|
|
+ _name = 'ww.group'
|
|
|
|
|
+ _description = 'Grupo de WhatsApp Web'
|
|
|
|
|
+
|
|
|
|
|
+ name = fields.Char(string='Nombre del Grupo', required=True)
|
|
|
|
|
+ whatsapp_web_id = fields.Char(string='ID WhatsApp Web', index=True, help='ID único del grupo en WhatsApp Web')
|
|
|
|
|
+ whatsapp_account_id = fields.Many2one('whatsapp.account', string='Cuenta de WhatsApp', required=True)
|
|
|
|
|
+ channel_id = fields.Many2one('discuss.channel', string='Canal de Discusión', readonly=True)
|
|
|
|
|
+ contact_ids = fields.Many2many(
|
|
|
|
|
+ comodel_name='res.partner',
|
|
|
|
|
+ relation='ww_group_contact_rel',
|
|
|
|
|
+ column1='group_id',
|
|
|
|
|
+ column2='contact_id',
|
|
|
|
|
+ string='Contactos',
|
|
|
|
|
+ readonly=True,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ def _process_messages(self, messages_data):
|
|
|
|
|
+ """Process WhatsApp messages and create them in the channel"""
|
|
|
|
|
+ self.ensure_one()
|
|
|
|
|
+
|
|
|
|
|
+ if not messages_data or not self.channel_id:
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ # Get existing message IDs to avoid duplicates
|
|
|
|
|
+ existing_ids = set(self.channel_id.message_ids.mapped('message_id'))
|
|
|
|
|
+
|
|
|
|
|
+ # Prepare bulk create values
|
|
|
|
|
+ message_vals_list = []
|
|
|
|
|
+ for msg_data in messages_data:
|
|
|
|
|
+ msg_id = msg_data.get('id', {}).get('_serialized')
|
|
|
|
|
+
|
|
|
|
|
+ # Skip if message already exists
|
|
|
|
|
+ if msg_id in existing_ids:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ # Get author partner
|
|
|
|
|
+ author_whatsapp_id = msg_data.get('author')
|
|
|
|
|
+ author = self.env['res.partner'].search([
|
|
|
|
|
+ ('whatsapp_web_id', '=', author_whatsapp_id)
|
|
|
|
|
+ ], limit=1) if author_whatsapp_id else False
|
|
|
|
|
+
|
|
|
|
|
+ # Get quoted message author if exists
|
|
|
|
|
+ quoted_author = False
|
|
|
|
|
+ if msg_data.get('hasQuotedMsg') and msg_data.get('quotedParticipant'):
|
|
|
|
|
+ quoted_author = self.env['res.partner'].search([
|
|
|
|
|
+ ('whatsapp_web_id', '=', msg_data['quotedParticipant'])
|
|
|
|
|
+ ], limit=1)
|
|
|
|
|
+
|
|
|
|
|
+ # Convert timestamp to datetime
|
|
|
|
|
+ timestamp = datetime.fromtimestamp(msg_data.get('timestamp', 0))
|
|
|
|
|
+
|
|
|
|
|
+ # Prepare message body with author and content
|
|
|
|
|
+ author_name = author.name if author else "Desconocido"
|
|
|
|
|
+ message_body = f"{msg_data.get('body', '')}"
|
|
|
|
|
+
|
|
|
|
|
+ # Add quoted message if exists
|
|
|
|
|
+ if msg_data.get('hasQuotedMsg') and msg_data.get('quotedMsg', {}).get('body'):
|
|
|
|
|
+ quoted_author_name = quoted_author.name if quoted_author else "Desconocido"
|
|
|
|
|
+ message_body += f"\n\n<blockquote><strong>{quoted_author_name}:</strong> {msg_data['quotedMsg']['body']}</blockquote>"
|
|
|
|
|
+
|
|
|
|
|
+ message_vals = {
|
|
|
|
|
+ 'model': 'discuss.channel',
|
|
|
|
|
+ 'res_id': self.channel_id.id,
|
|
|
|
|
+ 'message_type': 'comment',
|
|
|
|
|
+ 'subtype_id': self.env.ref('mail.mt_comment').id,
|
|
|
|
|
+ 'body': message_body,
|
|
|
|
|
+ 'date': timestamp,
|
|
|
|
|
+ 'author_id': author.id if author else self.env.user.partner_id.id,
|
|
|
|
|
+ 'message_id': msg_id,
|
|
|
|
|
+ }
|
|
|
|
|
+ message_vals_list.append(message_vals)
|
|
|
|
|
+
|
|
|
|
|
+ # Bulk create messages
|
|
|
|
|
+ if message_vals_list:
|
|
|
|
|
+ self.env['mail.message'].create(message_vals_list)
|
|
|
|
|
+
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ def _create_discussion_channel(self):
|
|
|
|
|
+ """Create a discussion channel for the WhatsApp group"""
|
|
|
|
|
+ self.ensure_one()
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ # Verificar si ya existe un canal para este grupo
|
|
|
|
|
+ if self.channel_id:
|
|
|
|
|
+ return self.channel_id
|
|
|
|
|
+
|
|
|
|
|
+ # Create channel name with WhatsApp prefix
|
|
|
|
|
+ channel_name = f"📱 {self.name}"
|
|
|
|
|
+
|
|
|
|
|
+ # Verificar que hay contactos
|
|
|
|
|
+ if not self.contact_ids:
|
|
|
|
|
+ _logger.warning(f"No hay contactos para crear el canal del grupo {self.name}")
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ # Obtener los IDs de los contactos de forma segura
|
|
|
|
|
+ partner_ids = []
|
|
|
|
|
+ for contact in self.contact_ids:
|
|
|
|
|
+ if contact and contact.id:
|
|
|
|
|
+ partner_ids.append(contact.id)
|
|
|
|
|
+
|
|
|
|
|
+ if not partner_ids:
|
|
|
|
|
+ _logger.warning(f"No se encontraron IDs válidos de contactos para el grupo {self.name}")
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ # Create the channel using channel_create
|
|
|
|
|
+ channel = self.env['discuss.channel'].channel_create(
|
|
|
|
|
+ name=channel_name,
|
|
|
|
|
+ group_id=self.env.user.groups_id[0].id, # Usar el primer grupo del usuario actual
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # Add members to the channel
|
|
|
|
|
+ channel.add_members(partner_ids=partner_ids)
|
|
|
|
|
+
|
|
|
|
|
+ # Link the channel to the group
|
|
|
|
|
+ self.write({'channel_id': channel.id})
|
|
|
|
|
+ return channel
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ _logger.error(f"Error al crear el canal para el grupo {self.name}: {str(e)}")
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ def _update_discussion_channel(self):
|
|
|
|
|
+ """Update the discussion channel members"""
|
|
|
|
|
+ self.ensure_one()
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ # Si no existe el canal, intentar crearlo
|
|
|
|
|
+ if not self.channel_id:
|
|
|
|
|
+ return self._create_discussion_channel()
|
|
|
|
|
+
|
|
|
|
|
+ # Verificar que el canal aún existe
|
|
|
|
|
+ channel = self.env['discuss.channel'].browse(self.channel_id.id)
|
|
|
|
|
+ if not channel.exists():
|
|
|
|
|
+ _logger.warning(f"El canal para el grupo {self.name} ya no existe, creando uno nuevo")
|
|
|
|
|
+ self.write({'channel_id': False})
|
|
|
|
|
+ return self._create_discussion_channel()
|
|
|
|
|
+
|
|
|
|
|
+ # Obtener los IDs de los contactos de forma segura
|
|
|
|
|
+ partner_ids = []
|
|
|
|
|
+ for contact in self.contact_ids:
|
|
|
|
|
+ if contact and contact.id:
|
|
|
|
|
+ partner_ids.append(contact.id)
|
|
|
|
|
+
|
|
|
|
|
+ if not partner_ids:
|
|
|
|
|
+ _logger.warning(f"No hay contactos válidos para actualizar el canal del grupo {self.name}")
|
|
|
|
|
+ return channel
|
|
|
|
|
+
|
|
|
|
|
+ # Update channel members using add_members
|
|
|
|
|
+ channel.add_members(partner_ids=partner_ids)
|
|
|
|
|
+ return channel
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ _logger.error(f"Error al actualizar el canal para el grupo {self.name}: {str(e)}")
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ @api.model
|
|
|
|
|
+ def sync_ww_contacts_groups(self):
|
|
|
|
|
+ """
|
|
|
|
|
+ Sincroniza los contactos y grupos de WhatsApp Web.
|
|
|
|
|
+ Solo sincroniza contactos que están dentro de grupos y valida que no se dupliquen,
|
|
|
|
|
+ verificando los últimos 10 dígitos del campo mobile.
|
|
|
|
|
+ """
|
|
|
|
|
+ accounts = self.env['whatsapp.account'].search([])
|
|
|
|
|
+
|
|
|
|
|
+ for account in accounts:
|
|
|
|
|
+ try:
|
|
|
|
|
+ # Obtener grupos usando el método de la cuenta
|
|
|
|
|
+ groups_data = account.get_groups()
|
|
|
|
|
+ if not groups_data:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ # Procesar cada grupo
|
|
|
|
|
+ for group_data in groups_data:
|
|
|
|
|
+ group_id = group_data.get('id').get('_serialized')
|
|
|
|
|
+ group_name = group_data.get('name', 'Sin nombre')
|
|
|
|
|
+
|
|
|
|
|
+ # Buscar o crear grupo
|
|
|
|
|
+ group = self.search([
|
|
|
|
|
+ ('whatsapp_web_id', '=', group_id),
|
|
|
|
|
+ ('whatsapp_account_id', '=', account.id)
|
|
|
|
|
+ ], limit=1)
|
|
|
|
|
+
|
|
|
|
|
+ if not group:
|
|
|
|
|
+ group = self.create({
|
|
|
|
|
+ 'name': group_name,
|
|
|
|
|
+ 'whatsapp_web_id': group_id,
|
|
|
|
|
+ 'whatsapp_account_id': account.id
|
|
|
|
|
+ })
|
|
|
|
|
+ # Create discussion channel for new group
|
|
|
|
|
+ group._create_discussion_channel()
|
|
|
|
|
+ else:
|
|
|
|
|
+ # Actualizar nombre del grupo si cambió
|
|
|
|
|
+ if group.name != group_name:
|
|
|
|
|
+ group.write({'name': group_name})
|
|
|
|
|
+ # Actualizar nombre del canal si existe
|
|
|
|
|
+ if group.channel_id:
|
|
|
|
|
+ group.channel_id.write({'name': f"📱 {group_name}"})
|
|
|
|
|
+
|
|
|
|
|
+ # Procesar participantes del grupo
|
|
|
|
|
+ participants = group_data.get('members', [])
|
|
|
|
|
+ contact_ids = []
|
|
|
|
|
+
|
|
|
|
|
+ for participant in participants:
|
|
|
|
|
+ whatsapp_web_id = participant.get('id', {}).get('_serialized')
|
|
|
|
|
+ mobile = participant.get('number', '')
|
|
|
|
|
+ is_admin = participant.get('isAdmin', False)
|
|
|
|
|
+ is_super_admin = participant.get('isSuperAdmin', False)
|
|
|
|
|
+
|
|
|
|
|
+ # Derive participant name
|
|
|
|
|
+ participant_name = participant.get('name') or participant.get('pushname') or mobile
|
|
|
|
|
+
|
|
|
|
|
+ # Search for existing contact
|
|
|
|
|
+ contact = self.env['res.partner'].search([
|
|
|
|
|
+ ('whatsapp_web_id', '=', whatsapp_web_id)
|
|
|
|
|
+ ], limit=1)
|
|
|
|
|
+
|
|
|
|
|
+ if not contact and mobile and len(mobile) >= 10:
|
|
|
|
|
+ last_10_digits = mobile[-10:]
|
|
|
|
|
+ contact = self.env['res.partner'].search([
|
|
|
|
|
+ ('mobile', 'like', '%' + last_10_digits)
|
|
|
|
|
+ ], limit=1)
|
|
|
|
|
+
|
|
|
|
|
+ partner_vals = {
|
|
|
|
|
+ 'name': participant_name,
|
|
|
|
|
+ 'mobile': mobile,
|
|
|
|
|
+ 'whatsapp_web_id': whatsapp_web_id,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if contact:
|
|
|
|
|
+ # Update existing contact
|
|
|
|
|
+ contact.write(partner_vals)
|
|
|
|
|
+ else:
|
|
|
|
|
+ # Create new contact
|
|
|
|
|
+ contact = self.env['res.partner'].create(partner_vals)
|
|
|
|
|
+
|
|
|
|
|
+ if contact:
|
|
|
|
|
+ contact_ids.append(contact.id)
|
|
|
|
|
+
|
|
|
|
|
+ # Actualizar contactos del grupo
|
|
|
|
|
+ group.write({'contact_ids': [(6, 0, contact_ids)]})
|
|
|
|
|
+
|
|
|
|
|
+ # Update discussion channel members
|
|
|
|
|
+ group._update_discussion_channel()
|
|
|
|
|
+
|
|
|
|
|
+ # Process messages if available
|
|
|
|
|
+ messages = group_data.get('messages', [])
|
|
|
|
|
+ if messages:
|
|
|
|
|
+ group._process_messages(messages)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ _logger.error("Error en la sincronización de grupos para la cuenta %s: %s", account.name, str(e))
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ return True
|