Update.
This commit is contained in:
parent
4c601e8333
commit
4ae846cf8b
43
src/snek/service/chat.py
Normal file
43
src/snek/service/chat.py
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
|
||||
from snek.system.service import BaseService
|
||||
|
||||
|
||||
class ChatService(BaseService):
|
||||
|
||||
async def send(self,user_uid, channel_uid, message):
|
||||
channel_message = await self.services.channel_message.create(
|
||||
user_uid,
|
||||
channel_uid,
|
||||
message
|
||||
)
|
||||
channel_message_uid = channel_message["uid"]
|
||||
|
||||
user = await self.services.user.get(uid=user_uid)
|
||||
async for channel_member in self.services.channel_member.find(
|
||||
channel_uid=channel_message["channel_uid"],
|
||||
is_banned=False,
|
||||
is_muted=False,
|
||||
deleted_at=None,
|
||||
):
|
||||
model = await self.new()
|
||||
model["object_uid"] = channel_message_uid
|
||||
model["object_type"] = "channel_message"
|
||||
model["user_uid"] = channel_member["user_uid"]
|
||||
model["message"] = (
|
||||
f"New message from {user['nick']} in {channel_member['label']}."
|
||||
)
|
||||
if not await self.services.channel_member.save(model):
|
||||
raise Exception(f"Failed to create notification: {model.errors}.")
|
||||
|
||||
sent_to_count = await self.services.socket.broadcast(channel_uid, dict(
|
||||
message=message,
|
||||
user_uid=user_uid,
|
||||
channel_uid=channel_uid,
|
||||
created_at=channel_message["created_at"],
|
||||
updated_at=None,
|
||||
uid=channel_message['uid'],
|
||||
user_nick=user['nick']
|
||||
))
|
||||
return sent_to_count
|
33
src/snek/service/socket.py
Normal file
33
src/snek/service/socket.py
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
|
||||
|
||||
from snek.system.service import BaseService
|
||||
|
||||
|
||||
class SocketService(BaseService):
|
||||
|
||||
def __init__(self, app):
|
||||
super().__init__(app)
|
||||
self.sockets = set()
|
||||
self.subscriptions = {}
|
||||
|
||||
async def add(self, ws):
|
||||
self.sockets.add(ws)
|
||||
|
||||
async def subscribe(self, ws, channel_uid):
|
||||
if not channel_uid in self.subscriptions:
|
||||
self.subscriptions[channel_uid] = set()
|
||||
self.subscriptions[channel_uid].add(ws)
|
||||
|
||||
async def broadcast(self, channel_uid, message):
|
||||
print("BROADCAT!",message)
|
||||
count = 0
|
||||
for ws in self.subscriptions.get(channel_uid,[]):
|
||||
await ws.send_json(message)
|
||||
count += 1
|
||||
return count
|
||||
async def delete(self, ws):
|
||||
try:
|
||||
self.sockets.remove(ws)
|
||||
except IndexError:
|
||||
pass
|
23
src/snek/static/message-list-manager.js
Normal file
23
src/snek/static/message-list-manager.js
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
class MessageListManagerElement extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.attachShadow({mode:'open'})
|
||||
this.container = document.createElement("div")
|
||||
this.shadowRoot.appendChild(this.container)
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
let channels = await app.rpc.getChannels()
|
||||
const me = this
|
||||
channels.forEach(channel=>{
|
||||
const messageList = document.createElement("message-list")
|
||||
messageList.setAttribute("channel",channel.uid)
|
||||
me.container.appendChild(messageList)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define("message-list-manager",MessageListManagerElement)
|
86
src/snek/static/message-list.js
Normal file
86
src/snek/static/message-list.js
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
|
||||
class MessageListElement extends HTMLElement {
|
||||
|
||||
static get observedAttributes() {
|
||||
return ["messages"];
|
||||
}
|
||||
messages = []
|
||||
room = null
|
||||
url = null
|
||||
container = null
|
||||
constructor() {
|
||||
super()
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.component = document.createElement('div')
|
||||
this.shadowRoot.appendChild(this.component )
|
||||
}
|
||||
createElement(message){
|
||||
const element = document.createElement("div")
|
||||
|
||||
element.classList.add("message")
|
||||
const avatar = document.createElement("div")
|
||||
avatar.classList.add("avatar")
|
||||
avatar.innerText = message.user_nick[0]
|
||||
const messageContent = document.createElement("div")
|
||||
messageContent.classList.add("message-content")
|
||||
const author = document.createElement("div")
|
||||
author.classList.add("author")
|
||||
author.textContent = message.user_nick
|
||||
const text = document.createElement("div")
|
||||
text.classList.add("text")
|
||||
text.textContent = message.message
|
||||
const time = document.createElement("div")
|
||||
time.classList.add("time")
|
||||
time.textContent = message.created_at
|
||||
messageContent.appendChild(author)
|
||||
messageContent.appendChild(text)
|
||||
messageContent.appendChild(time)
|
||||
element.appendChild(avatar)
|
||||
element.appendChild(messageContent)
|
||||
|
||||
|
||||
|
||||
message.element = element
|
||||
|
||||
return element
|
||||
}
|
||||
addMessage(message){
|
||||
|
||||
const obj = new models.Message(
|
||||
message.uid,
|
||||
message.channel_uid,
|
||||
message.user_uid,
|
||||
message.user_nick,
|
||||
message.message,
|
||||
message.created_at,
|
||||
message.updated_at
|
||||
)
|
||||
const element = this.createElement(obj)
|
||||
this.messages.push(obj)
|
||||
this.container.appendChild(element)
|
||||
return obj
|
||||
}
|
||||
connectedCallback() {
|
||||
const link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = '/base.css'
|
||||
this.component.appendChild(link)
|
||||
this.container = document.createElement('div')
|
||||
this.container.classList.add("chat-messages")
|
||||
this.component.appendChild(this.container)
|
||||
|
||||
this.messages = []
|
||||
this.channel_uid = this.getAttribute("channel")
|
||||
const me = this
|
||||
app.addEventListener(this.channel_uid, (data) => {
|
||||
console.info("WIIIIIIIIIIIIIIIIIIIIIIII")
|
||||
me.addMessage(data)
|
||||
})
|
||||
this.dispatchEvent(new CustomEvent("rendered", {detail:this,bubbles:true}))
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('message-list', MessageListElement);
|
22
src/snek/static/models.js
Normal file
22
src/snek/static/models.js
Normal file
@ -0,0 +1,22 @@
|
||||
class MessageModel {
|
||||
message = null
|
||||
user_uid = null
|
||||
channel_uid = null
|
||||
created_at = null
|
||||
updated_at = null
|
||||
element = null
|
||||
constructor(uid, channel_uid,user_uid,user_nick, message,created_at, updated_at){
|
||||
this.uid = uid
|
||||
this.message = message
|
||||
this.user_uid = user_uid
|
||||
this.user_nick = user_nick
|
||||
this.channel_uid = channel_uid
|
||||
this.created_at = created_at
|
||||
this.updated_at = updated_at
|
||||
}
|
||||
}
|
||||
|
||||
const models = {
|
||||
Message: MessageModel
|
||||
|
||||
}
|
75
src/snek/view/rpc.py
Normal file
75
src/snek/view/rpc.py
Normal file
@ -0,0 +1,75 @@
|
||||
from aiohttp import web
|
||||
from snek.system.view import BaseView
|
||||
|
||||
|
||||
class RPCView(BaseView):
|
||||
|
||||
login_required = True
|
||||
|
||||
class RPCApi:
|
||||
def __init__(self,view, ws):
|
||||
self.view = view
|
||||
self.app = self.view.app
|
||||
self.services = self.app.services
|
||||
self.user_uid = self.view.session.get("uid")
|
||||
self.ws = ws
|
||||
|
||||
|
||||
async def get_channels(self):
|
||||
channels = []
|
||||
async for subscription in self.services.channel_member.find(user_uid=self.user_uid,is_banned=False):
|
||||
channels.append(dict(
|
||||
name=subscription["label"],
|
||||
uid=subscription["channel_uid"],
|
||||
is_moderator=subscription["is_moderator"],
|
||||
is_read_only=subscription["is_read_only"]
|
||||
))
|
||||
return channels
|
||||
|
||||
async def send_message(self, room, message):
|
||||
await self.services.chat.send(self.user_uid,room,message)
|
||||
return True
|
||||
|
||||
|
||||
async def echo(self,*args):
|
||||
return args
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async def __call__(self, data):
|
||||
call_id = data.get("callId")
|
||||
method_name = data.get("method")
|
||||
args = data.get("args")
|
||||
if hasattr(super(),method_name) or not hasattr(self,method_name):
|
||||
return await self.ws.send_json({"callId":call_id,"data":"Not allowed"})
|
||||
|
||||
method = getattr(self,method_name.replace(".","_"),None)
|
||||
result = await method(*args)
|
||||
await self.ws.send_json({"callId":call_id,"data":result})
|
||||
|
||||
|
||||
async def call_ping(self,callId,*args):
|
||||
return {"pong": args}
|
||||
|
||||
|
||||
async def get(self):
|
||||
|
||||
|
||||
ws = web.WebSocketResponse()
|
||||
await ws.prepare(self.request)
|
||||
await self.services.socket.add(ws)
|
||||
async for subscription in self.services.channel_member.find(user_uid=self.session.get("uid"),deleted_at=None,is_banned=False):
|
||||
await self.services.socket.subscribe(ws,subscription["channel_uid"])
|
||||
print("Subscribed for: ", subscription["label"],flush=True)
|
||||
rpc = RPCView.RPCApi(self,ws)
|
||||
async for msg in ws:
|
||||
if msg.type == web.WSMsgType.TEXT:
|
||||
await rpc(msg.json())
|
||||
elif msg.type == web.WSMsgType.ERROR:
|
||||
print(f"WebSocket exception {ws.exception()}")
|
||||
|
||||
await self.services.socket.delete(ws)
|
||||
print("WebSocket connection closed")
|
||||
return ws
|
Loading…
Reference in New Issue
Block a user