Files
public/tier_communications/models/mail_thread.py

254 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
import re
from odoo import models
_logger = logging.getLogger(__name__)
class MailThread(models.AbstractModel):
_inherit = 'mail.thread'
def _get_allowed_message_params(self):
"""Extend allowed params so channel_tag_ids passes through the controller."""
return super()._get_allowed_message_params() | {'channel_tag_ids'}
def _get_message_create_valid_field_names(self):
"""Add channel_tag_ids to the whitelist for _message_create."""
return super()._get_message_create_valid_field_names() | {'channel_tag_ids'}
def assign_mailbox(self, mailbox_id):
"""Assign all messages of this thread to the given tier.mailbox.
Called from the chatter UI when user clicks "Входящие лиды" / "Входящие тикеты".
Removes existing tags for this mailbox first to avoid duplicates.
"""
self.ensure_one()
mailbox = self.env['tier.mailbox'].browse(mailbox_id)
if not mailbox.exists():
return False
# Get all messages of this thread
thread_messages = self.env['mail.message'].search([
('model', '=', self._name),
('res_id', '=', self.id),
])
for message in thread_messages:
# Remove existing tag for this mailbox if any
existing_tag = message.mailbox_tag_ids.filtered(
lambda tag: tag.mailbox_id.id == mailbox_id
)
if existing_tag:
existing_tag.unlink()
# Create new tag
self.env['tier.mailbox.tag'].create({
'mailbox_id': mailbox_id,
'message_id': message.id,
})
print(
f"[tier_communications] assign_mailbox: "
f"thread {self._name}#{self.id} → mailbox '{mailbox.name}' "
f"({len(thread_messages)} messages tagged)"
)
return True
# =========================================================================
# ОТПРАВКА ИСХОДЯЩИХ СООБЩЕНИЙ
# =========================================================================
def message_post(self, *, body='', **kwargs):
"""Override: создаём сообщение в Odoo. Внешняя отправка через канал
выполняется отдельным RPC-вызовом из JS (/tier_communications/send_via_channel).
"""
message = super().message_post(body=body, **kwargs)
if not message:
return message
if not message.channel_tag_ids:
print(
f"[tier_communications] message_post: msg_id={message.id}"
f"канал не выбран, сообщение только в Odoo"
)
return message
# =========================================================================
# ! ОТПРАВКА СООБЩЕНИЯ НА EMAIL
# =========================================================================
def _tier_send_email(self, message):
"""Отправить сообщение на email партнёра треда через стандартный mail.mail."""
recipient_email = None
# 1. Thread has partner_id (business models: sale.order, crm.lead, etc.)
if self._name not in ('mail.thread', 'discuss.channel') and hasattr(self, 'partner_id'):
partner = self.partner_id
if partner and partner.email:
recipient_email = partner.email
# 2. Message has explicit partner_ids
if not recipient_email and message.partner_ids:
for partner in message.partner_ids:
partner_email = partner.email if hasattr(partner, 'email') else None
if partner_email and isinstance(partner_email, str) and '@' in partner_email:
recipient_email = partner_email
break
# 3. Last incoming message in this thread — use its email_from
if not recipient_email and message.model and message.res_id:
last_incoming = self.env['mail.message'].search([
('model', '=', message.model),
('res_id', '=', message.res_id),
('message_type', 'in', ['email', 'comment']),
('email_from', '!=', False),
('author_id', '!=', self.env.user.partner_id.id),
], order='id desc', limit=1)
if last_incoming and last_incoming.email_from:
email_match = re.search(r'<([^>]+)>', last_incoming.email_from)
recipient_email = email_match.group(1) if email_match else last_incoming.email_from
# 4. For discuss.channel — find the other member's email
if not recipient_email and self._name == 'discuss.channel':
other_members = self.channel_member_ids.filtered(
lambda m: m.partner_id and m.partner_id != self.env.user.partner_id
)
for member in other_members:
if member.partner_id.email:
recipient_email = member.partner_id.email
break
if not recipient_email:
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА EMAIL: "
f"msg_id={message.id}У пользователя нет email адреса получателя"
)
return
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА EMAIL: "
f"msg_id={message.id}, email_to={recipient_email}"
)
try:
mail_values = {
'subject': message.subject or 'Сообщение из Odoo',
'body_html': message.body or '',
'email_to': recipient_email,
'email_from': self.env.company.email or self.env.user.email or '',
'auto_delete': True,
}
outgoing_mail = self.env['mail.mail'].sudo().create(mail_values)
outgoing_mail.send()
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА EMAIL: "
f"msg_id={message.id} — SUCCESS, mail.mail id={outgoing_mail.id}"
)
except Exception as send_error:
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА EMAIL: "
f"msg_id={message.id} — ОШИБКА: {send_error}"
)
_logger.exception(
'tier_communications: ошибка отправки email, msg_id=%s', message.id
)
# =========================================================================
# ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX
# =========================================================================
def _tier_send_matrix(self, message):
"""Отправить сообщение в Matrix комнату текущего пользователя."""
current_user = self.env.user
# Matrix fields are defined in tier_communications_matrix module
# If not installed — fields don't exist
if not hasattr(current_user, 'tier_matrix_homeserver'):
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id} — модуль tier_communications_matrix не установлен"
)
return
homeserver_url = current_user.tier_matrix_homeserver
access_token = current_user.tier_matrix_access_token
room_id = current_user.tier_matrix_room_id
if not homeserver_url:
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id}У пользователя нет homeserver URL (tier_matrix_homeserver)"
)
return
if not access_token:
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id}У пользователя нет access token (tier_matrix_access_token)"
)
return
if not room_id:
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id}У пользователя нет room ID (tier_matrix_room_id)"
)
return
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id}, room={room_id}, homeserver={homeserver_url}"
)
if 'tier.channel.matrix.helper' in self.env:
try:
self.env['tier.channel.matrix.helper'].channel_send_message(
message, user=current_user
)
except Exception as send_error:
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id} — ОШИБКА: {send_error}"
)
_logger.exception(
'tier_communications: ошибка отправки matrix, msg_id=%s', message.id
)
else:
print(
f"[tier_communications] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id} — модуль tier_communications_matrix не установлен"
)
# =========================================================================
# ПРИЁМ ВХОДЯЩИХ СООБЩЕНИЙ (fetchmail / стандартный механизм Odoo)
# =========================================================================
def message_process(self, model, message, custom_values=None, save_original=False,
strip_attachments=False, thread_id=None):
"""Override: после стандартной обработки входящего письма — помечаем тегом email."""
result = super().message_process(
model, message,
custom_values=custom_values,
save_original=save_original,
strip_attachments=strip_attachments,
thread_id=thread_id,
)
if result:
incoming_message = self.env['mail.message'].search(
[('model', '=', model), ('res_id', '=', result)],
order='id desc',
limit=1,
)
if incoming_message:
email_tag = self.env['tier.channel.tag'].search(
[('code', '=', 'email')], limit=1
)
if email_tag and not incoming_message.channel_tag_ids:
incoming_message.channel_tag_ids = [(4, email_tag.id)]
print(
f"[tier_communications] message_process: "
f"входящее письмо msg_id={incoming_message.id} помечено тегом 'email'"
)
from .tier_router import TierRouter
TierRouter(self.env).route_incoming(incoming_message)
return result