const e = require("hypercore-crypto"),
t = require("signal-promise"),
i = require("safety-catch"),
{ Task: o } = require("../task"),
{ restoreAvatar: s } = require("../../contract/avatars"),
{
contract: {
hyperschema: { getEnum: r },
},
} = require("../../../../schema/spec"),
{
getWriterKey: a,
createContactRequestOperation: n,
generateRoomCapabilities: m,
generatePairingCapabilities: h,
} = require("../../invitations"),
l = r("@keet/room-type")
module.exports = class extends o {
constructor(e) {
super(e)
const t = this.description.sendContactRequest
;((this._roomKey = t.roomKey),
(this._memberKey = t.memberKey),
(this._message = t.message),
(this._contactInfo = t.contactInfo),
(this._member = null),
(this._contact = null),
(this._writerInfo = null),
(this._contactRoomInfo = null),
(this._privateContactRoom = null),
(this._selfMember = null),
(this._targetMember = null),
(this._roomMailbox = null),
(this._memberMailbox = null),
(this._memberMailboxDiscoveryKey = null),
(this._core = null),
(this._dmRoom = null),
(this._memberRoom = null),
(this._privateProfile = null),
(this._wakeupSession = null),
(this._watcher = null),
(this._privateProfileWatcher = null))
}
async _close() {
;(this._dmRoom &&
(this._watcher && this._dmRoom.db.unwatch(this._watcher),
this.keet.internalReleaseRoom(this._dmRoom)),
this._memberRoom && this.keet.internalReleaseRoom(this._memberRoom),
this._privateProfile &&
(this._privateProfileWatcher &&
this._privateProfile.db.unwatch(this._privateProfileWatcher),
this.keet.internalReleaseRoom(this._privateProfile)),
this._wakeupSession && this._wakeupSession.destroy())
}
async _createRoom() {
const t = e.keyPair(),
i = e.randomBytes(32),
o = a(t.publicKey),
s = m(o, i),
r = { identity: this._identityInfo, privateProfile: this._privateProfileInfo },
n = this.keet.internalCreateDeviceUpdate(r, o)
return (
(this._roomMailbox = s.mailboxKeyPair.publicKey),
(this._dmRoom = await this.keet.internalCreateRoom({
settings: { roomType: l.DirectMessage },
initialMembers: [this._selfMember, this._targetMember],
bootstrapDevice: n,
keyPair: t,
seed: i,
})),
this._message &&
(await this._dmRoom.dispatch("@keet/add-chat-message", {
message: { text: this._message },
})),
!0
)
}
async _appendContactRequest() {
if (!(await this.keet.internalVerifyOptimisticCore(this._core)) || this.closing) return
const e = {
message: this._message,
sender: this._selfMember,
mailbox: this._roomMailbox,
keys: { key: this._dmRoom.key, encryptionKey: this._dmRoom.encryptionKey },
},
t = n(this._memberMailbox, this._identityInfo, e),
i = {
memberKey: this._memberKey,
mailbox: this._memberMailbox,
roomKey: this._dmRoom.key,
status: { isBlocked: !1 },
}
return (
await Promise.all([
this._core.append(t),
this._privateProfile.dispatch("@keet-private-id/add-contact", { contact: i }),
]),
!0
)
}
async _loadRequestWriter() {
if (
((this._writerInfo = await this.local.get("@local/writers", {
mailbox: this._memberMailbox,
})),
this._writerInfo)
)
return
const t = e.keyPair(),
i = a(t.publicKey)
this._writerInfo = { mailbox: this._memberMailbox, keyPair: t, key: i }
const o = await this.keet.internalEnterLocalTransaction()
try {
;(await o.insert("@local/writers", this._writerInfo), await o.flush())
} finally {
await this.keet.internalExitLocalTransaction()
}
}
_getQueryRange() {
return {
gte: { contact: this._targetMember.attestation.memberKey },
lte: { contact: this._targetMember.attestation.memberKey },
}
}
async _load() {
if (
(this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
(this._identityInfo = await this.local.get("@local/identity")),
this.closing)
)
return !1
const e = this._identityInfo.privateProfileKey
if (
((this._privateProfileInfo = await this.local.get("@local/rooms", { roomKey: e })),
this.closing)
)
return !1
if (
((this._privateProfile = this.keet.internalOpenExistingRoom(e)),
await this._privateProfile.ready(),
this.closing)
)
return !1
let t = !1
return (
(t = this._contactInfo ? await this._useContactInfo() : await this._useRoomKey()),
!(
!this._targetMember?.profile?.displayName ||
(await this._loadRequestWriter(),
this.closing ||
((this._core = this.store.get({ keyPair: this._writerInfo.keyPair })),
await this._core.ready(),
this.closing ||
((this._privateContactRoom = await this._privateProfile.db.findOne(
"@keet-private-id/rooms-by-contact",
this._getQueryRange(),
)),
(!t || this._privateContactRoom) && (await this._waitForMember(), 1))))
)
)
}
async _useContactInfo() {
const e = await this._privateProfile.db.get("@keet-private-id/contacts", {
memberKey: this._memberKey,
})
if (
((this._contact = e),
(this._memberMailbox = this._contactInfo.profile.mailbox),
(this._member = null),
(this._memberProofs = null),
(this._memberPairingCapabilities = h(this._memberMailbox)),
(this._targetMember = {
profile: this._contactInfo.profile,
attestation: this._contactInfo.attestation,
}),
e?.roomKey &&
(this._contactRoomInfo = await this._privateProfile.db.get("@keet-private-id/rooms", {
roomKey: this._contact.roomKey,
})),
this.closing || this._contactRoomInfo?.status.isActive)
)
return !1
const t = this._privateProfileInfo.selfMember
return (
(this._selfMember = {
profile: t.profile,
attestation: { memberKey: t.key, proof: t.proofs.profileAttestation },
}),
!0
)
}
async _useRoomKey() {
if (
((this._memberRoom = this.keet.internalOpenExistingRoom(this._roomKey)),
await this._memberRoom.ready(),
this.closing)
)
return !1
const [e, t, i] = await Promise.all([
this._privateProfile.db.get("@keet-private-id/contacts", { memberKey: this._memberKey }),
this._memberRoom.db.get("@keet/members", { key: this._memberKey }),
this._memberRoom.db.get("@keet/member-proofs", { key: this._memberKey }),
])
if (
((this._contact = e),
(this._member = t),
(this._memberProofs = i),
(this._memberMailbox = this._member.profile.mailbox),
(this._memberPairingCapabilities = h(this._memberMailbox)),
this.closing || !t || !i)
)
return !1
if (
((this._targetMember = {
profile: this._member.profile,
attestation: { memberKey: this._member.key, proof: this._memberProofs.profileAttestation },
}),
e?.roomKey &&
(this._contactRoomInfo = await this._privateProfile.db.get("@keet-private-id/rooms", {
roomKey: this._contact.roomKey,
})),
this.closing || this._contactRoomInfo?.status.isActive)
)
return !1
const o = this._privateProfileInfo.selfMember
return (
(this._selfMember = {
profile: o.profile,
attestation: { memberKey: o.key, proof: o.proofs.profileAttestation },
}),
this._targetMember.profile.avatar &&
(await s(this._memberRoom.blobs, this._targetMember.profile.avatar)),
!0
)
}
async _waitForMember() {
if (!this._dmRoom) {
const e = this._privateContactRoom?.roomKey || this._contact.roomKey
this._dmRoom = this.keet.internalOpenExistingRoom(e)
}
this.swarmWorker.startActiveSwarming(this._memberPairingCapabilities.swarmTopic)
const e = (e) => {
this._wakeupSession.announce(e, [{ key: this._core.key, length: this._core.length }])
}
this._wakeupSession = this.keet.protomuxWakeup.session(this._memberMailbox, {
discoveryKey: this._memberPairingCapabilities.wakeupTopic,
onpeeractive: e,
})
for (const t of this._wakeupSession.peers) t.active && e(t)
;(this.keet.blindPeering.addCoreBackground(this._core, {
target: this._memberMailbox,
referrer: this._memberMailbox,
priority: 1,
}),
(this._signal = new t()),
(this._watcher = async () => {
try {
const e = this._targetMember.attestation.memberKey
;(await this._dmRoom.db.findOne("@keet/devices-by-member-key", {
gte: { memberKey: e },
lte: { memberKey: e },
})) && this._signal.notify()
} catch (e) {
i(e)
}
}),
this._dmRoom.db.watch(this._watcher),
this._watcher(),
(this._privateProfileWatcher = async () => {
try {
const e = await await this._privateProfile.db.get("@keet-private-id/rooms", {
roomKey: this._dmRoom.key,
})
e && !1 === e.status.isActive && this._signal.notify()
} catch (e) {
i(e)
}
}),
this._privateProfile.db.watch(this._privateProfileWatcher),
this._privateProfileWatcher(),
await this._signal.wait(),
this.swarmWorker.stopActiveSwarming(this._memberPairingCapabilities.swarmTopic))
}
async _run() {
const e = await this._load()
if (this.closing || !e) return
const t = await this._createRoom()
if (this.closing || !t) return
const i = await this._appendContactRequest()
!this.closing && i && (await this._waitForMember())
}
}