В общем, разобрался самостоятельно, вот что вышло, может, кому-то пригодится:
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
import json
class WsConsumer(WebsocketConsumer):
def connect(self):
self.accept()
self.user_id = self.scope['url_route']['kwargs']['user_id'] # ID пользователя приходит как часть url
async_to_sync(self.channel_layer.group_add)( # Добавляем пользователя в собственную группу
str(self.user_id), # Таким образом, сообщения будут во всех табах
self.channel_name
)
def disconnect(self, close_code):
pass
def send_message(self, event): # Функция отправки должна быть внутри consumer'а
message = event['message']
message['type'] = 'message'
self.send(json.dumps(message))
def send_to_group(group_name, message): # А эта функция за пределами класса
from channels.layers import get_channel_layer # и может быть вызвана откуда угодно в рамках проекта
from asgiref.sync import async_to_sync
message = {'message': message}
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(group_name, {"type": "send.message", # значение type должно совпадать с именем функции отправки внутри класса, только с точкой вместо подчеркивания. Такая логика.
"message": message})