from datetime import datetime, timedelta from odoo import models, api, fields from odoo.addons.mail.tools.discuss import Store from odoo.exceptions import ValidationError class DiscussChannel(models.Model): _inherit = "discuss.channel" is_whatsapp_web = fields.Boolean(compute="_compute_is_whatsapp_web") def write(self, vals): """ Override write to debounce 'last_interest_dt' updates. If the channel was updated less than 10 seconds ago, skip updating last_interest_dt. This prevents 'concurrent update' errors during high traffic (e.g. active WhatsApp groups). """ if "last_interest_dt" in vals and len(self) == 1: # Check if we have a recent update if self.last_interest_dt: # Calculate time since last update # Note: last_interest_dt is usually UTC time_since_last = datetime.now() - self.last_interest_dt if time_since_last < timedelta(seconds=10): # Skip updating this field del vals["last_interest_dt"] return super().write(vals) @api.depends("channel_type", "wa_account_id.whatsapp_web_url") def _compute_is_whatsapp_web(self): for record in self: record.is_whatsapp_web = record.channel_type == "whatsapp" and bool( record.wa_account_id.whatsapp_web_url ) def _to_store(self, store: Store): """ Send is_whatsapp_web to the frontend via Store. """ super()._to_store(store) for channel in self: if channel.is_whatsapp_web: store.add(channel, {"is_whatsapp_web": True}) def message_post(self, **kwargs): """ Override message_post to allow sending free text messages in WhatsApp Web channels. Standard Odoo WhatsApp module might block or restrict messages without templates. """ # Check if it's a WhatsApp channel with WhatsApp Web configured if self.channel_type == "whatsapp" and self.wa_account_id.whatsapp_web_url: # We want to use our custom logic for these channels # Extract basic message data body = kwargs.get("body", "") attachment_ids = kwargs.get("attachment_ids", []) # If it's a simple text message or has attachments, we handle it. # Note: We need to ensure we don't break other message_post usages (like system notifications) # System notifications usually have subtype_xmlid='mail.mt_note' or similar, strict check might be needed. # Let's check if we should intervene. # If the user is trying to send a message (comment) if kwargs.get("message_type") == "comment" or not kwargs.get( "message_type" ): # Check for attachments in kwargs (can be list of IDs or list of tuples) # We mainly care about passing them to the mail.message # 1. Create the mail.message manually to bypass potential blocks in super().message_post() # We need to replicate some logic from mail.thread.message_post # However, completely skipping super() is risky for notifications/followers. # Let's try a hybrid approach: # Create the message using mail.message.create() directly, then run necessary side effects? # Or invoke mail.thread's message_post directly if possible? # We can't easily invoke 'grandparent' methods in Odoo new API unless we are careful. # Simplified approach: mimic whatsapp_composer logic email_from = kwargs.get("email_from") if not email_from: email_from = self.env.user.email_formatted # Create mail.message msg_values = { "body": body, "model": self._name, "res_id": self.id, "message_type": "whatsapp_message", # Use whatsapp_message type so our other logic picks it up? Or 'comment'? # Standard WA uses 'whatsapp_message' "email_from": email_from, "partner_ids": [ (4, p.id) for p in self.channel_partner_ids ], # Add channel partners? # 'subtype_id': ... "attachment_ids": attachment_ids, } # Handle author author_id = kwargs.get("author_id") if author_id: msg_values["author_id"] = author_id else: msg_values["author_id"] = self.env.user.partner_id.id # Create the message message = self.env["mail.message"].create(msg_values) # Now create the whatsapp.message to trigger sending (via our overridden _send_message or similar) # Note: whatsapp_message.create() triggers _send_message() if state is outgoing? # In our whatsapp_composer, we called _send_message() explicitly. # Determine recipient (Phone or Group) mobile_number = self.whatsapp_number recipient_type = "phone" if mobile_number and mobile_number.endswith("@g.us"): recipient_type = "group" wa_msg_values = { "mail_message_id": message.id, "wa_account_id": self.wa_account_id.id, "mobile_number": mobile_number, "recipient_type": recipient_type, "wa_template_id": False, "body": body, "state": "outgoing", } wa_msg = self.env["whatsapp.message"].create(wa_msg_values) # Send it wa_msg._send_message() # Ensure the message is linked to the channel (standard mail.message behavior should handle res_id/model) # But Discuss expects the message to be in the channel. return message # Default behavior for other channels or if conditions not met return super(DiscussChannel, self).message_post(**kwargs)