OverviewFilesCommitsIssuesDocsReleasesPeers

holepunchto-deps / @holepunchto / keet-core / lib / core / contract / chat.js

1 lines · javascript ref:
const e=require("b4a"),t=require("./registration"),{getMemberForDeviceKey:i,isMemberActive:s}=require("./query"),{KeetContractError:a}=require("./errors"),{insertMessageFiles:r,insertLinkPreviewFiles:o,deleteMessageFiles:n,deleteFileRecord:l,updateImageFile:d}=require("./avatars"),{contract:{hyperschema:{getEnum:c}}}=require("../../../schema/spec"),{chatStats:w,fileStats:h}=require("./stats"),{trackActivity:g}=require("./hooks"),p=c("@keet/room-type");async function m({message:e,header:t},{db:o,blobs:n,writer:l}){let[d,c,h,g,m,k]=await Promise.all([i(o,l.key),o.get("@keet/threads",{id:0}),e.parent?o.get("@keet/threads-by-parent",{oplog:e.parent}):null,e.parent?o.get("@keet/chat-by-message-id",{oplog:e.parent}):null,e.replyTo?o.get("@keet/chat-by-message-id",{oplog:e.replyTo}):null,o.get("@keet/system-digest")]);if(!s(d))throw a.INACTIVE_MEMBER();if(d.status.isMuted)throw a.WRITER_IS_MUTED();const y=k.settings.roomType===p.Broadcast||k.settings.isAppendingDisabled;if(!d.status.isModerator&&y)throw a.MODERATORS_ONLY();if(e.parent&&!g)throw a.PARENT_MSG_INVALID();if(e.replyTo&&!m)throw a.REPLYTO_MSG_INVALID();if(e.parent||(h=c),e.event?.callMissed){const e=await o.get("@keet/chat",{id:{thread:h.id,index:h.count-1}});if(e&&e.event.callMissed)throw a.NO_DUPLICATE_CALL_EVENTS()}if(k.settings.isAttachmentsDisabled&&A(e))throw a.FILE_ATTACHMENTS_DISABLED();if(k.settings.isLinksDisabled&&N(e))throw a.LINKS_DISABLED();const E=c.stats.files.clock;e.files=await r({primaryThread:c,clock:E,message:e,blobs:n,db:o,oplog:l});const f={id:{thread:h.id,index:h.count},oplog:l,clock:c.stats.chat.clock,timestamp:t.timestamp,message:e,status:{isEdited:!1,isPinned:!1,isDeleted:!1},event:e.event};return await o.insert("@keet/chat",f),await w(o,c,f),{writer:l,writerMember:d}}async function k({header:t,oplog:o,message:l},{db:d,blobs:c,writer:h}){const[g,p,m,k,y,E]=await Promise.all([i(d,h.key),i(d,o.key),d.get("@keet/chat-by-message-id",{oplog:o}),d.get("@keet/threads",{id:0}),l.replyTo?d.get("@keet/chat-by-message-id",{oplog:l.replyTo}):null,d.get("@keet/system-digest")]);if(!s(g))throw a.INACTIVE_MEMBER();if(!m)throw a.TARGET_MSG_INVALID();if(!p)throw a.TARGET_MEMBER_NOT_FOUND();if(!e.equals(g.key,p.key))throw a.SELF_ONLY();if(m.status.isDeleted)throw a.TARGET_MSG_IS_DELETED();if(l.replyTo&&!y)throw a.REPLYTO_MSG_INVALID();if(E.settings.isAttachmentsDisabled&&A(l))throw a.FILE_ATTACHMENTS_DISABLED();if(E.settings.isLinksDisabled&&N(l))throw a.LINKS_DISABLED();const f=[];m.message?.files?.length&&(f.push(...m.message.files),m.message.files=[]),l.files=[],await n({primaryThread:k,message:m,db:d,blobs:c});const u=k.stats.files.clock;await r({primaryThread:k,clock:u,message:l,blobs:c,db:d,oplog:h});const b={...m,message:{...m.message,text:l.text,files:f,display:l.display},status:{...m.status,isEdited:!0},editedAt:t.timestamp};return await d.insert("@keet/chat",b),await w(d,k,b,m),{writer:h,writerMember:g}}async function y({oplog:t},{db:r,blobs:o,writer:l}){const[d,c,h,g]=await Promise.all([i(r,l.key),i(r,t.key),r.get("@keet/chat-by-message-id",{oplog:t}),r.get("@keet/threads",{id:0})]);if(!s(d))throw a.INACTIVE_MEMBER();if(!h)throw a.TARGET_MSG_INVALID();if(!d.status.isModerator&&!e.equals(d.key,c.key))throw a.MODERATORS_OR_SELF_ONLY();await n({primaryThread:g,message:h,db:r,blobs:o});const p={...h,message:null,status:{...h.status,isDeleted:!0,isPinned:!1}};await r.insert("@keet/chat",p),await w(r,g,p,h);const m=await r.get("@keet/chat-label-digests",{id:h.id});return m?(await r.insert("@keet/chat-label-digests",{...m,isDeleted:!0}),{writer:l,writerMember:d}):{writer:l,writerMember:d}}async function E({oplog:e,isPinned:t},{db:r,writer:o}){const[n,l,d]=await Promise.all([i(r,o.key),r.get("@keet/chat-by-message-id",{oplog:e}),r.get("@keet/threads",{id:0})]);if(!s(n))throw a.INACTIVE_MEMBER();if(!l)throw a.TARGET_MSG_INVALID();if(!n.status.isModerator)throw a.MODERATORS_ONLY();const c={...l,status:{...l.status,isPinned:t}};return await r.insert("@keet/chat",c),await w(r,d,c,l),{writer:o,writerMember:n}}async function f({header:t,oplog:r,index:o,preview:n,displayType:l},{db:c,writer:g}){const[p,m,k,y]=await Promise.all([i(c,g.key),i(c,r.key),c.get("@keet/chat-by-message-id",{oplog:r}),c.get("@keet/threads",{id:0})]);if(!s(p))throw a.INACTIVE_MEMBER();if(!k)throw a.TARGET_MSG_INVALID();if(!m)throw a.TARGET_MEMBER_NOT_FOUND();if(!e.equals(p.key,m.key))throw a.SELF_ONLY();const E=l?M(k,l,o):k.message?.files,f=l?0:o;if(!E||E.length<=f)throw a.FILE_REFERENCE_INVALID();const u=await c.get("@keet/files",{id:E[f]});if(!u)throw a.FILE_RECORD_INVALID();if(u.file.preview){const e={...k,status:{...k.status,isEdited:!0},editedAt:t.timestamp};await Promise.all([c.insert("@keet/chat",e),w(c,y,e,k)])}return u.file.preview=await d(c,null,n),await Promise.all([c.insert("@keet/files",u),h(c,y,u,u)]),{writer:g,writerMember:p}}async function u({header:t,oplog:r,index:o,displayType:n},{db:l,writer:d}){const[c,g,p,m]=await Promise.all([i(l,d.key),i(l,r.key),l.get("@keet/chat-by-message-id",{oplog:r}),l.get("@keet/threads",{id:0})]);if(!s(c))throw a.INACTIVE_MEMBER();if(!p)throw a.TARGET_MSG_INVALID();if(!e.equals(c.key,g.key))throw a.SELF_ONLY();const k=n?M(p,n,o):p.message?.files,y=n?0:o;if(!k||k.length<=y)throw a.FILE_REFERENCE_INVALID();const E=await l.get("@keet/files",{id:k[y]});if(!E)throw a.FILE_RECORD_INVALID();const f={...p,status:{...p.status,isEdited:!0},editedAt:t.timestamp};return E.file.preview=null,await Promise.all([l.insert("@keet/chat",f),l.insert("@keet/files",E),w(l,m,f,p),h(l,m,E,E)]),{writer:d,writerMember:c}}async function b({header:t,oplog:r,displayType:n,index:d,preview:c},{db:h,blobs:g,writer:p}){const[m,k,y,E]=await Promise.all([i(h,p.key),i(h,r.key),h.get("@keet/chat-by-message-id",{oplog:r}),h.get("@keet/threads",{id:0})]);if(!s(m))throw a.INACTIVE_MEMBER();if(!y)throw a.TARGET_MSG_INVALID();if(!e.equals(m.key,k.key))throw a.SELF_ONLY();const f=L(n),u=f?y.message?.display?.[f]:null;if(!u||u.length<=d)throw a.LINK_REFERENCE_INVALID();const b=u[d];b.preview?.files?.length&&await l({primaryThread:E,files:b.preview.files,db:h,blobs:g});const _={...b,preview:c},I=E.stats.files.clock;await o({primaryThread:E,clock:I,links:[_],blobs:g,db:h,oplog:p});const T={...y,message:{...y.message,display:{...y.message.display,[f]:u.with(d,_)}}};return b.preview&&(T.status={...y.status,isEdited:!0},T.editedAt=t.timestamp),await Promise.all([h.insert("@keet/chat",T),w(h,E,T,y)]),{writer:p,writerMember:m}}async function _({header:t,oplog:r,displayType:o,index:n},{db:d,blobs:c,writer:h}){const[g,p,m,k]=await Promise.all([i(d,h.key),i(d,r.key),d.get("@keet/chat-by-message-id",{oplog:r}),d.get("@keet/threads",{id:0})]);if(!s(g))throw a.INACTIVE_MEMBER();if(!m)throw a.TARGET_MSG_INVALID();if(!e.equals(g.key,p.key))throw a.SELF_ONLY();const y=L(o),E=y?m.message?.display?.[y]:null;if(!E||E.length<=n)throw a.LINK_REFERENCE_INVALID();const f=E[n];f.preview?.files?.length&&await l({primaryThread:k,files:f.preview.files,db:d,blobs:c});const u={...m,message:{...m.message,display:{...m.message.display,[y]:E.with(n,{...f,preview:null})}},status:{...m.status,isEdited:!0},editedAt:t.timestamp};return await Promise.all([d.insert("@keet/chat",u),w(d,k,u,m)]),{writer:h,writerMember:g}}async function I({length:e},{db:t,writer:a}){const[r,o,n]=await Promise.all([t.get("@keet/system-digest"),i(t,a.key),t.get("@keet/threads",{id:0})]);if(s(o)&&!o.status.isMuted&&r.settings.roomType===p.DirectMessage)return await t.insert("@keet/read-lengths",{memberKey:o.key,length:Math.min(e,n.count)}),{writer:a,writerMember:o}}async function T({header:e},{db:t,writer:r}){const[o,n]=await Promise.all([t.get("@keet/system-digest"),i(t,r.key)]);if(!s(n))throw a.INACTIVE_MEMBER();if(o.settings.roomType!==p.DirectMessage)throw a.INVALID_ROOM_TYPE();await t.insert("@keet/typing-indicators",{memberKey:n.key,timestamp:e.timestamp})}function M(e,t,i){const s=e.message?.display;switch(t){case 1:return s?.httpLink?.[i]?.preview?.files;case 2:return s?.pearLink?.[i]?.preview?.files}}function L(e){switch(e){case 1:return"httpLink";case 2:return"pearLink"}}function A(e){return e?.files?.length>0||e?.display?.httpLink?.some(e=>e.preview)||e?.display?.pearLink?.some(e=>e.preview)}function N(e){return e.display?.httpLink?.length>0||e?.display?.pearLink?.length>0}module.exports={addEvent:async function(e,t,i,s,a){const r=await e.get("@keet/threads",{id:0}),o={id:{thread:0,index:r.count},oplog:t,clock:r.stats.chat.clock,timestamp:s,event:i,status:{isEdited:!1,isPinned:!1,isDeleted:!1}};await e.insert("@keet/chat",o),await w(e,r,o)},[t]:function(e){e.add("@keet/add-chat-message",g(m)),e.add("@keet/update-chat-message",g(k)),e.add("@keet/remove-chat-message",g(y)),e.add("@keet/pin-chat-message",g(E)),e.add("@keet/add-file-preview",g(f)),e.add("@keet/remove-file-preview",g(u)),e.add("@keet/add-link-preview",g(b)),e.add("@keet/remove-link-preview",g(_)),e.add("@keet/update-read-length",g(I)),e.add("@keet/update-typing-indicator",T)}};