import { AccountNamespace } from './namespaces/account.ts' import { ActiveCampaignNamespace } from './namespaces/activecampaign.ts' import { AnalyticsNamespace } from './namespaces/analytics.ts' import { BillingNamespace } from './namespaces/billing.ts' import { BuilderNamespace } from './namespaces/builder.ts' import { ContextNamespace } from './namespaces/context.ts' import { DeskNamespace } from './namespaces/desk.ts' import { MediaNamespace } from './namespaces/media.ts' import type { SendCommandOptions } from './namespaces/namespace.ts' import { PluginsNamespace } from './namespaces/plugins.ts' import { PortalNamespace } from './namespaces/portal.ts' import { SchedulerNamespace } from './namespaces/scheduler.ts' import { TunnelNamespace } from './namespaces/tunnel.ts' import { WhatsAppNamespace } from './namespaces/whatsapp.ts' import { isClosable } from './sender/closable.ts' import { HttpSender } from './sender/http/httpsender.ts' import type { ConnectionSender, ConnectionSenderConstructor, Sender } from './sender/sender.ts' import type { Message, MessageTypes } from './types/message.ts' import { type BlipDomain, type Domain, type Identity, Node } from './types/node.ts' import { randomId } from './utils/random.ts' export class BlipClient { constructor( public readonly sender: TSender, protected readonly defaultOptions?: SendCommandOptions, ) { this.account = new AccountNamespace(this, defaultOptions) this.builder = new BuilderNamespace(this, defaultOptions) this.desk = new DeskNamespace(this, defaultOptions) this.media = new MediaNamespace(this, defaultOptions) this.whatsapp = new WhatsAppNamespace(this, defaultOptions) this.scheduler = new SchedulerNamespace(this, defaultOptions) this.analytics = new AnalyticsNamespace(this, defaultOptions) this.activecampaign = new ActiveCampaignNamespace(this, defaultOptions) this.portal = new PortalNamespace(this, defaultOptions) this.plugins = new PluginsNamespace(this, defaultOptions) this.context = new ContextNamespace(this, defaultOptions) this.billing = new BillingNamespace(this, defaultOptions) this.tunnel = new TunnelNamespace(this, defaultOptions) } public readonly account!: AccountNamespace public readonly builder!: BuilderNamespace public readonly desk!: DeskNamespace public readonly media!: MediaNamespace public readonly whatsapp!: WhatsAppNamespace public readonly scheduler!: SchedulerNamespace public readonly analytics!: AnalyticsNamespace public readonly activecampaign!: ActiveCampaignNamespace public readonly portal!: PortalNamespace public readonly plugins!: PluginsNamespace public readonly context!: ContextNamespace public readonly billing!: BillingNamespace public readonly tunnel!: TunnelNamespace public as(identityOrBotIdentifier: string | Identity): DelegatedClient { return new DelegatedClient(this.sender, { ownerIdentity: Node.from(identityOrBotIdentifier, 'msging.net').toIdentity(), domain: 'msging.net', }) } public sendMessage(message: Omit, 'id'>): Promise { return this.sender.sendMessage({ id: randomId(), ...message, }) } // TODO: use async disposable? public close(): Promise { return isClosable(this.sender) ? Promise.resolve(this.sender.close()) : Promise.resolve() } public static http(bot: string | Identity, accessKey: string, tenantId?: string): BlipClient public static http(token: string, tenantId?: string): BlipClient public static http(...parameters: Array): BlipClient { return new BlipClient(HttpSender.login.apply(HttpSender, parameters as Parameters)) } public static async createAccountAndConnect( sender: ConnectionSenderConstructor, identity: Identity, password: string, tenantId?: string, ) { const domain = Node.from(identity).domain as BlipDomain const guest = new BlipClient( new sender({ // guests should always be a guid otherwise the server will reject the authentication node: new Node(crypto.randomUUID(), domain), authentication: { scheme: 'guest', }, tenantId, }), ) await guest.account.setAccount( { password, }, { ownerIdentity: identity }, ) await guest.close() return new BlipClient( new sender({ node: identity, authentication: { scheme: 'plain', password, }, tenantId, }), ) } } export class DelegatedClient extends BlipClient { public get delegatedIdentity(): Identity | Domain { if (!this.defaultOptions?.ownerIdentity) { throw new Error('No delegated identity set on this client') } return this.defaultOptions.ownerIdentity } public async incorporate(): Promise> { if (this.defaultOptions?.domain !== 'msging.net') { throw new Error('Incorporation is only allowed for the msging.net domain') } let accessKey: string let tenantId: string try { const application = await this.portal.getApplication(this.delegatedIdentity, { // should not be delegated ownerIdentity: undefined, }) accessKey = application.accessKey tenantId = application.tenantId } catch (err) { throw new Error( 'Failed to get application. Ensure you are connected as someone from blip.ai with application permissions.', { cause: err }, ) } const senderType = this.sender.constructor if (!('login' in senderType) || !senderType.login) { throw new Error('The current sender does not support login.') } const login = senderType.login as (typeof ConnectionSender)['login'] return new BlipClient( login.bind(senderType)(this.delegatedIdentity, accessKey, tenantId) as unknown as TSender, ) } }