@openvertice/bot-sdk 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -2
- package/dist/src/audio.d.ts +32 -0
- package/dist/src/audio.d.ts.map +1 -0
- package/dist/src/audio.js +47 -0
- package/dist/src/audio.js.map +1 -0
- package/dist/src/cliente.d.ts +213 -0
- package/dist/src/cliente.d.ts.map +1 -0
- package/dist/src/cliente.js +381 -0
- package/dist/src/cliente.js.map +1 -0
- package/dist/src/gateway.d.ts +76 -0
- package/dist/src/gateway.d.ts.map +1 -0
- package/dist/src/gateway.js +299 -0
- package/dist/src/gateway.js.map +1 -0
- package/dist/src/http.d.ts +119 -0
- package/dist/src/http.d.ts.map +1 -0
- package/dist/src/http.js +189 -0
- package/dist/src/http.js.map +1 -0
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +16 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/tipos.d.ts +243 -0
- package/dist/src/tipos.d.ts.map +1 -0
- package/dist/src/tipos.js +5 -0
- package/dist/src/tipos.js.map +1 -0
- package/dist/src/voz-wrtc.d.ts +20 -0
- package/dist/src/voz-wrtc.d.ts.map +1 -0
- package/dist/src/voz-wrtc.js +68 -0
- package/dist/src/voz-wrtc.js.map +1 -0
- package/dist/src/voz.d.ts +150 -0
- package/dist/src/voz.d.ts.map +1 -0
- package/dist/src/voz.js +311 -0
- package/dist/src/voz.js.map +1 -0
- package/dist/src/wav.d.ts +29 -0
- package/dist/src/wav.d.ts.map +1 -0
- package/dist/src/wav.js +54 -0
- package/dist/src/wav.js.map +1 -0
- package/package.json +58 -3
- package/src/audio.ts +88 -0
- package/src/cliente.ts +472 -0
- package/src/gateway.ts +354 -0
- package/src/http.ts +249 -0
- package/src/index.ts +41 -0
- package/src/tipos.ts +220 -0
- package/src/voz-wrtc.ts +100 -0
- package/src/voz.ts +407 -0
- package/src/wav.ts +71 -0
- package/src/wrtc.d.ts +4 -0
package/src/tipos.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Types of the Vértice contract (server/bots/README.md, version 2026-10).
|
|
2
|
+
// They are the boundary: if the server changes a field, it changes here too.
|
|
3
|
+
|
|
4
|
+
/** Contract version this SDK implements. */
|
|
5
|
+
export const CONTRATO = "2026-10";
|
|
6
|
+
|
|
7
|
+
/** Gateway event families a bot may subscribe to. */
|
|
8
|
+
export type Intencao = "MESSAGES" | "MEMBERS" | "ROOMS" | "PRESENCE";
|
|
9
|
+
|
|
10
|
+
/** A text channel as the server grants it to this bot. */
|
|
11
|
+
export interface CanalDeTexto {
|
|
12
|
+
/** Stable channel code used in every API call (for example `"MURAL"`). */
|
|
13
|
+
code: string;
|
|
14
|
+
/** Name people see ("Música"); falls back to `code` if the room is gone. */
|
|
15
|
+
nome: string;
|
|
16
|
+
/** Whether the bot may read what is posted here. */
|
|
17
|
+
escutar: boolean;
|
|
18
|
+
/** Whether the bot may post here. */
|
|
19
|
+
falar: boolean;
|
|
20
|
+
/** Command prefix of the channel; `null` = the bot receives everything it can hear. */
|
|
21
|
+
prefixo: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A voice channel the bot is allowed to join. */
|
|
25
|
+
export interface CanalDeVoz {
|
|
26
|
+
code: string;
|
|
27
|
+
nome: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** STUN/TURN server handed to the WebRTC peer connection. */
|
|
31
|
+
export interface ServidorIce {
|
|
32
|
+
/** One URL or a list of them (`stun:`, `turn:`). */
|
|
33
|
+
urls: string | string[];
|
|
34
|
+
username?: string;
|
|
35
|
+
credential?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Rate limits the server enforces for this bot. */
|
|
39
|
+
export interface Limites {
|
|
40
|
+
/** Messages per minute. */
|
|
41
|
+
mensagensPorMinuto: number;
|
|
42
|
+
/** `identify` attempts per minute. */
|
|
43
|
+
identifyPorMinuto: number;
|
|
44
|
+
/** Session lifetime in seconds. */
|
|
45
|
+
sessaoSegundos: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Response of `GET /api/bots/gateway`: everything this bot is allowed to do. */
|
|
49
|
+
export interface Descoberta {
|
|
50
|
+
/** Contract version the server speaks; compare it with `CONTRATO`. */
|
|
51
|
+
contrato: string;
|
|
52
|
+
/** The identity the server assigned to the bot. */
|
|
53
|
+
bot: { id: string; usuario: string; exibicao: string };
|
|
54
|
+
/** Where to connect and how often to send heartbeats. */
|
|
55
|
+
gateway: { versao: number; url: string; heartbeatMs: number };
|
|
56
|
+
/** Channels the panel granted, split by kind. */
|
|
57
|
+
canais: { texto: CanalDeTexto[]; voz: CanalDeVoz[] };
|
|
58
|
+
/** Event families the panel granted. */
|
|
59
|
+
intents: Intencao[];
|
|
60
|
+
/** ICE servers to use for voice. */
|
|
61
|
+
iceServers: ServidorIce[];
|
|
62
|
+
/** Effective rate limits. */
|
|
63
|
+
limites: Limites;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Response of `POST /api/bots/session`: a short-lived gateway credential. */
|
|
67
|
+
export interface Sessao {
|
|
68
|
+
/** The session token, sent on `identify` and on message requests. */
|
|
69
|
+
sessao: string;
|
|
70
|
+
/** Expiry as a Unix timestamp in milliseconds. */
|
|
71
|
+
expiraEm: number;
|
|
72
|
+
/** Gateway endpoint and cadence to use with this session. */
|
|
73
|
+
gateway: { versao: number; url: string; heartbeatMs: number };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A file attached to a message. */
|
|
77
|
+
export interface AnexoDaMensagem {
|
|
78
|
+
/** Original file name, as uploaded. */
|
|
79
|
+
nome: string;
|
|
80
|
+
/** MIME type. */
|
|
81
|
+
mime: string;
|
|
82
|
+
/** Size in bytes. */
|
|
83
|
+
tamanho: number;
|
|
84
|
+
/** Absolute URL to download the file. */
|
|
85
|
+
url: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Link preview the server resolved for a message. */
|
|
89
|
+
export interface PreviaDeLink {
|
|
90
|
+
/** URL the preview was built from. */
|
|
91
|
+
url: string;
|
|
92
|
+
/** Page title, when the server could read one. */
|
|
93
|
+
titulo: string | null;
|
|
94
|
+
/** Page description, when available. */
|
|
95
|
+
descricao: string | null;
|
|
96
|
+
/** Preview image URL, when available. */
|
|
97
|
+
imagem: string | null;
|
|
98
|
+
/** Site name (host) shown next to the preview. */
|
|
99
|
+
site: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** A message, as delivered by `message.created` and returned by the send routes. */
|
|
103
|
+
export interface Mensagem {
|
|
104
|
+
/** Message id, unique inside the channel. */
|
|
105
|
+
id: string;
|
|
106
|
+
/** Code of the channel the message belongs to. */
|
|
107
|
+
sala?: string;
|
|
108
|
+
/** Display name of the author. */
|
|
109
|
+
autor: string;
|
|
110
|
+
/** Author id, used to tell the bot's own messages apart. */
|
|
111
|
+
autorId?: string | null;
|
|
112
|
+
/** `true` when the author is a bot — this one included. */
|
|
113
|
+
autorBot: boolean;
|
|
114
|
+
/** Kind of payload the message carries. */
|
|
115
|
+
tipo?: "texto" | "arquivo" | "voz";
|
|
116
|
+
/** Text content; `null` for attachments without a caption. */
|
|
117
|
+
texto: string | null;
|
|
118
|
+
/** Creation time as a Unix timestamp in milliseconds. */
|
|
119
|
+
criadaEm: number;
|
|
120
|
+
/** When the message was pinned, if it was. */
|
|
121
|
+
fixadaEm?: number | null;
|
|
122
|
+
/** Who pinned the message. */
|
|
123
|
+
fixadaPor?: string | null;
|
|
124
|
+
/** Attachment, when `tipo` is `arquivo` or `voz`. */
|
|
125
|
+
arquivo?: AnexoDaMensagem | null;
|
|
126
|
+
/** Resolved link preview, when the message contains a link. */
|
|
127
|
+
previa?: PreviaDeLink | null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The payload of `voice.signal` — the raw WebRTC `signal`, untranslated.
|
|
132
|
+
*
|
|
133
|
+
* The SDP travels under two names because both sides of the protocol already
|
|
134
|
+
* existed: `sdp` is the bot contract's name (server/bots/README.md) and
|
|
135
|
+
* `description.sdp` is what the browser client reads
|
|
136
|
+
* (`client/src/types/signaling.ts`). Senders fill in both; receivers accept
|
|
137
|
+
* either one.
|
|
138
|
+
*/
|
|
139
|
+
export interface SinalDeVoz {
|
|
140
|
+
/** `"offer"`, `"answer"` or `"candidate"`. */
|
|
141
|
+
type?: string;
|
|
142
|
+
/** Session description, in the bot contract's name. */
|
|
143
|
+
sdp?: string;
|
|
144
|
+
/** Session description, in the browser client's shape. */
|
|
145
|
+
description?: { type?: string; sdp?: string };
|
|
146
|
+
/** ICE candidate, when `type` is `"candidate"`. */
|
|
147
|
+
candidate?: unknown;
|
|
148
|
+
[chave: string]: unknown;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** A command announced on `identify`, used by the client's autocomplete. */
|
|
152
|
+
export interface ComandoAnunciado {
|
|
153
|
+
/** Command name, without the channel prefix (e.g. `"tocar"`). */
|
|
154
|
+
nome: string;
|
|
155
|
+
/** Short, human-readable summary shown next to the suggestion. */
|
|
156
|
+
descricao?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Someone else in the voice room. */
|
|
160
|
+
export interface PeerDaVoz {
|
|
161
|
+
/** Peer id inside the room. */
|
|
162
|
+
id: string;
|
|
163
|
+
/** Display name. */
|
|
164
|
+
nome: string;
|
|
165
|
+
/** `true` when the peer is a bot. */
|
|
166
|
+
bot?: boolean;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Events dispatched by the gateway (op 0), keyed by event name, with the shape of their `d`. */
|
|
170
|
+
export interface Eventos {
|
|
171
|
+
/** Sent once per connection, right after `identify` (or `resume`) succeeds. */
|
|
172
|
+
ready: {
|
|
173
|
+
/** The bot's identity on the server. */
|
|
174
|
+
bot: { id: string; usuario: string; exibicao: string };
|
|
175
|
+
/** Text channels with full settings; voice channels as bare codes. */
|
|
176
|
+
canais: { texto: CanalDeTexto[]; voz: string[] };
|
|
177
|
+
/** Event families the server accepted. */
|
|
178
|
+
intents: Intencao[];
|
|
179
|
+
/** Channels where the read subscription took effect (they exist and grant access). */
|
|
180
|
+
ouvindo: string[];
|
|
181
|
+
/** Commands this process announced, for the client's autocomplete. */
|
|
182
|
+
comandos: ComandoAnunciado[];
|
|
183
|
+
/** ICE servers to use for voice. */
|
|
184
|
+
iceServers: ServidorIce[];
|
|
185
|
+
/** Id of the session this connection is using. */
|
|
186
|
+
sessao: string;
|
|
187
|
+
};
|
|
188
|
+
/** A message was posted in a channel the bot listens to. */
|
|
189
|
+
"message.created": Mensagem;
|
|
190
|
+
/**
|
|
191
|
+
* Someone called the bot with `@login`/`@display`.
|
|
192
|
+
*
|
|
193
|
+
* Arrives even in channels with a prefix (where `message.created` would not)
|
|
194
|
+
* and, in channels without a prefix, alongside it.
|
|
195
|
+
*/
|
|
196
|
+
mention: Mensagem;
|
|
197
|
+
/** A message was deleted; `sala` is the channel code it belonged to. */
|
|
198
|
+
"message.deleted": { id: string; sala: string };
|
|
199
|
+
/** An operation failed; `codigo` tells channel errors from voice errors. */
|
|
200
|
+
error: { codigo: string; mensagem: string };
|
|
201
|
+
/** The bot is now in the voice room. */
|
|
202
|
+
"voice.joined": {
|
|
203
|
+
/** Channel code. */
|
|
204
|
+
code: string;
|
|
205
|
+
/** Channel name. */
|
|
206
|
+
nome: string;
|
|
207
|
+
/** The bot's id in the room: it settles the crossed offer (glare). */
|
|
208
|
+
eu: string;
|
|
209
|
+
/** Peers already in the room. */
|
|
210
|
+
peers: PeerDaVoz[];
|
|
211
|
+
};
|
|
212
|
+
/** Another peer joined the voice room. */
|
|
213
|
+
"voice.peer.joined": PeerDaVoz;
|
|
214
|
+
/** Another peer left the voice room. */
|
|
215
|
+
"voice.peer.left": { id: string };
|
|
216
|
+
/** The bot left (or was removed from) the voice room. */
|
|
217
|
+
"voice.left": { code: string; motivo: string };
|
|
218
|
+
/** WebRTC signaling addressed to this bot by peer `de`. */
|
|
219
|
+
"voice.signal": { de: string; signal: SinalDeVoz };
|
|
220
|
+
}
|
package/src/voz-wrtc.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Voice transport for Node: `@roamhq/wrtc` (an optional dependency).
|
|
2
|
+
//
|
|
3
|
+
// It is loaded only when someone actually uses voice — a text-only bot never
|
|
4
|
+
// downloads a native binary. The Node 24 spike (ABI 137) is documented in the
|
|
5
|
+
// README.
|
|
6
|
+
import type { ConexaoDeVoz, FonteDeAudio, TransporteDeVoz } from "./voz.js";
|
|
7
|
+
import type { ServidorIce } from "./tipos.js";
|
|
8
|
+
|
|
9
|
+
/** The pieces of `@roamhq/wrtc` this SDK touches. */
|
|
10
|
+
interface ModuloWrtc {
|
|
11
|
+
RTCPeerConnection: new (config: unknown) => ConexaoDeVoz;
|
|
12
|
+
nonstandard: { RTCAudioSource: new () => FonteDeAudio & {
|
|
13
|
+
/** Pushes one PCM block into the source. */
|
|
14
|
+
onData(dados: {
|
|
15
|
+
/** Interleaved signed 16-bit samples. */
|
|
16
|
+
samples: Int16Array;
|
|
17
|
+
/** Sample rate in Hz (48 kHz for wrtc). */
|
|
18
|
+
sampleRate: number;
|
|
19
|
+
/** Bits per sample (16). */
|
|
20
|
+
bitsPerSample: number;
|
|
21
|
+
/** Number of channels (1 = mono). */
|
|
22
|
+
channelCount: number;
|
|
23
|
+
/** Frames in this block (480 at 48 kHz, i.e. 10 ms). */
|
|
24
|
+
numberOfFrames: number;
|
|
25
|
+
}): void;
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Module cache: the native addon is imported at most once per process. */
|
|
30
|
+
let carregado: ModuloWrtc | null = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Imports `@roamhq/wrtc` on first use.
|
|
34
|
+
*
|
|
35
|
+
* The import is dynamic on purpose: a bot that only handles text must not pay
|
|
36
|
+
* for (or fail because of) a native dependency it never uses.
|
|
37
|
+
*
|
|
38
|
+
* @returns The wrtc module, memoised after the first call.
|
|
39
|
+
* @throws {Error} With install instructions when the optional dependency is missing.
|
|
40
|
+
*/
|
|
41
|
+
async function carregarWrtc(): Promise<ModuloWrtc> {
|
|
42
|
+
if (carregado) return carregado;
|
|
43
|
+
try {
|
|
44
|
+
const modulo = (await import("@roamhq/wrtc")) as unknown as
|
|
45
|
+
| ModuloWrtc
|
|
46
|
+
| { default: ModuloWrtc };
|
|
47
|
+
carregado = "RTCPeerConnection" in modulo ? modulo : modulo.default;
|
|
48
|
+
return carregado;
|
|
49
|
+
} catch (erro) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
"A voz do bot precisa de `@roamhq/wrtc`. Instale com `npm i @roamhq/wrtc` " +
|
|
52
|
+
`no projeto do bot (o texto funciona sem ele). Detalhe: ${
|
|
53
|
+
erro instanceof Error ? erro.message : String(erro)
|
|
54
|
+
}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates the Node voice transport backed by `@roamhq/wrtc`.
|
|
61
|
+
*
|
|
62
|
+
* The returned transport mirrors the browser's `RTCPeerConnection` API, so the
|
|
63
|
+
* mesh code in voz.ts does not need to know which runtime it is running on.
|
|
64
|
+
* Every connection it opens is tracked, and `fechar()` closes them all.
|
|
65
|
+
*
|
|
66
|
+
* @returns A transport that creates peer connections and audio sources.
|
|
67
|
+
* @throws {Error} When `@roamhq/wrtc` is not installed.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* const transporte = await criarTransporteWrtc();
|
|
72
|
+
* const voz = new Voz(bot, { transporte });
|
|
73
|
+
* await voz.entrar("PATIO");
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export async function criarTransporteWrtc(): Promise<TransporteDeVoz> {
|
|
77
|
+
const wrtc = await carregarWrtc();
|
|
78
|
+
const abertas = new Set<ConexaoDeVoz>();
|
|
79
|
+
return {
|
|
80
|
+
/** Opens a peer connection configured with the server-provided ICE servers. */
|
|
81
|
+
criarConexao({ iceServers }: { iceServers: ServidorIce[] }) {
|
|
82
|
+
const conexao = new wrtc.RTCPeerConnection({
|
|
83
|
+
iceServers: iceServers.map((servidor) => ({
|
|
84
|
+
urls: servidor.urls,
|
|
85
|
+
...(servidor.username ? { username: servidor.username } : {}),
|
|
86
|
+
...(servidor.credential ? { credential: servidor.credential } : {}),
|
|
87
|
+
})),
|
|
88
|
+
});
|
|
89
|
+
abertas.add(conexao);
|
|
90
|
+
return conexao;
|
|
91
|
+
},
|
|
92
|
+
/** Creates the PCM source that feeds the published audio track. */
|
|
93
|
+
criarFonteDeAudio: () => new wrtc.nonstandard.RTCAudioSource(),
|
|
94
|
+
/** Closes every connection this transport opened. */
|
|
95
|
+
fechar() {
|
|
96
|
+
for (const conexao of abertas) conexao.close();
|
|
97
|
+
abertas.clear();
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|