Files
public/tier_communications_matrix/models/tier_channel_matrix.py

172 lines
6.9 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 uuid
import logging
import requests
from odoo import _, models
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class TierChannelMatrixHelper(models.AbstractModel):
"""Helper for Matrix channel operations using per-user credentials from res.users.
Credentials stored on res.users: tier_matrix_homeserver, tier_matrix_access_token, tier_matrix_room_id.
"""
_name = 'tier.channel.matrix.helper'
_description = 'Matrix Channel Helper'
def _get_user_matrix_config(self, user=None):
if not user:
user = self.env.user
return {
'homeserver_url': user.tier_matrix_homeserver,
'access_token': user.tier_matrix_access_token,
'room_id': user.tier_matrix_room_id,
}
def channel_authenticate(self, user=None):
"""Verify Matrix credentials for the given user."""
config = self._get_user_matrix_config(user)
if not all([config['homeserver_url'], config['access_token']]):
raise UserError(_('Matrix credentials not configured for this user.'))
url = f"{config['homeserver_url'].rstrip('/')}/_matrix/client/v3/account/whoami"
headers = {'Authorization': f"Bearer {config['access_token']}"}
try:
resp = requests.get(url, headers=headers, timeout=10)
if resp.status_code == 200:
return True
raise UserError(
_('Matrix authentication failed (HTTP %s): %s') % (resp.status_code, resp.text)
)
except requests.exceptions.RequestException as e:
raise UserError(_('Cannot connect to Matrix homeserver: %s') % str(e))
# =========================================================================
# ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX
# =========================================================================
def channel_send_message(self, message, user=None):
"""Send a mail.message to Matrix using user's credentials."""
if not user:
user = self.env.user
config = self._get_user_matrix_config(user)
if not config['homeserver_url']:
print(
f"[tier_matrix] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id}У пользователя нет homeserver URL (tier_matrix_homeserver)"
)
return
if not config['access_token']:
print(
f"[tier_matrix] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id}У пользователя нет access token (tier_matrix_access_token)"
)
return
if not config['room_id']:
print(
f"[tier_matrix] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id}У пользователя нет room ID (tier_matrix_room_id)"
)
return
txn_id = str(uuid.uuid4())
url = (
f"{config['homeserver_url'].rstrip('/')}/_matrix/client/v3/rooms/"
f"{config['room_id']}/send/m.room.message/{txn_id}"
)
headers = {
'Authorization': f"Bearer {config['access_token']}",
'Content-Type': 'application/json',
}
# Убираем HTML теги для Matrix (plain text)
import re
body_text = re.sub(r'<[^>]+>', '', message.body or '')
payload = {
'msgtype': 'm.text',
'body': body_text,
'format': 'org.matrix.custom.html',
'formatted_body': message.body or '',
}
print(
f"[tier_matrix] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id}, room={config['room_id']}, txn_id={txn_id}"
)
try:
resp = requests.put(url, json=payload, headers=headers, timeout=15)
if resp.status_code not in (200, 201):
print(
f"[tier_matrix] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id} — HTTP {resp.status_code}: {resp.text}"
)
raise UserError(
_('Failed to send Matrix message (HTTP %s): %s') % (resp.status_code, resp.text)
)
event_id = resp.json().get('event_id', '')
print(
f"[tier_matrix] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id} — SUCCESS, event_id={event_id}"
)
except requests.exceptions.RequestException as e:
print(
f"[tier_matrix] ! ОТПРАВКА СООБЩЕНИЯ НА MATRIX: "
f"msg_id={message.id} — ОШИБКА соединения: {e}"
)
raise UserError(_('Matrix send error: %s') % str(e))
# =========================================================================
# ПРИЁМ ВХОДЯЩЕГО СООБЩЕНИЯ ИЗ MATRIX
# =========================================================================
def channel_receive_message(self, payload):
"""Create a mail.message in Odoo from an incoming Matrix event.
Входящее из Matrix → создаём mail.message с тегом 'matrix'.
Это дублирует переписку внутри Odoo.
"""
body = payload.get('content', {}).get('body', '')
sender = payload.get('sender', '')
room_id = payload.get('room_id', '')
print(
f"[tier_matrix] channel_receive_message: "
f"входящее из Matrix, sender={sender}, room={room_id}, body_len={len(body)}"
)
# Ищем пользователя по room_id чтобы привязать к нужному треду
user_with_room = self.env['res.users'].sudo().search(
[('tier_matrix_room_id', '=', room_id)], limit=1
)
vals = {
'body': body,
'message_type': 'email',
'email_from': sender,
'subject': f'Matrix: {sender}',
}
# Если нашли пользователя с этой комнатой — привязываем к его партнёру
if user_with_room and user_with_room.partner_id:
vals['author_id'] = False # внешний отправитель
msg = self.env['mail.message'].sudo().create(vals)
# Помечаем тегом matrix
matrix_tag = self.env['tier.channel.tag'].search([('code', '=', 'matrix')], limit=1)
if matrix_tag:
msg.channel_tag_ids = [(4, matrix_tag.id)]
from odoo.addons.tier_communications.models.tier_router import TierRouter
TierRouter(self.env).route_incoming(msg)
print(
f"[tier_matrix] channel_receive_message: "
f"создано msg_id={msg.id} с тегом matrix"
)
return msg