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