botql 1.0.0 → 1.0.1

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/Bootstrap.js ADDED
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Bootstrap.js — ponto de entrada do BotQL.
5
+ *
6
+ * Lê um ficheiro .sql (com suporte a IMPORT), cria o interpreter, liga os
7
+ * connectors reais das plataformas usadas via CONNECT, corre ON START e
8
+ * fica pronto a receber mensagens.
9
+ *
10
+ * Uso:
11
+ * node Bootstrap.js caminho/para/bot.sql
12
+ *
13
+ * Uso programático:
14
+ * const { bootstrap } = require('./Bootstrap.js');
15
+ * const bot = await bootstrap('bot.sql');
16
+ * await bot.receiveMessage('+244900000000', 'quero comprar');
17
+ */
18
+
19
+ const path = require('path');
20
+ const { BotQLInterpreter } = require('./botql.js');
21
+ const { ConnectorRegistry, attachConnectors } = require('./Connectors.js');
22
+ const { SQLiteDatabase } = require('./Database.js');
23
+
24
+ /**
25
+ * @param {string} sqlFilePath Caminho do ficheiro .sql principal do bot.
26
+ * @param {object} options
27
+ * @param {string} [options.dbPath] Caminho do ficheiro .sqlite. Sem isto,
28
+ * usa MemoryDatabase (sem persistência), que é o omisso do interpreter.
29
+ * @param {string} [options.connectorsPath] Caminho alternativo para o JSON
30
+ * de connectors. Por omissão usa o Connectors.json ao lado deste ficheiro.
31
+ * @param {boolean} [options.autoStart] Corre ON START automaticamente (omissão: true).
32
+ */
33
+ async function bootstrap(sqlFilePath, options = {}) {
34
+ const interpreterOptions = {};
35
+ if (options.dbPath) {
36
+ interpreterOptions.db = new SQLiteDatabase(options.dbPath);
37
+ }
38
+
39
+ const bot = BotQLInterpreter.fromFile(sqlFilePath, interpreterOptions);
40
+
41
+ const registry = new ConnectorRegistry(
42
+ options.connectorsPath || path.join(__dirname, 'Connectors.json')
43
+ );
44
+ attachConnectors(bot, registry);
45
+
46
+ if (options.autoStart !== false) {
47
+ await bot.start();
48
+ }
49
+
50
+ return bot;
51
+ }
52
+
53
+ // Execução direta: node Bootstrap.js caminho/bot.sql
54
+ if (require.main === module) {
55
+ const sqlFilePath = process.argv[2];
56
+ if (!sqlFilePath) {
57
+ console.error('Uso: node Bootstrap.js caminho/para/bot.sql');
58
+ process.exit(1);
59
+ }
60
+
61
+ bootstrap(sqlFilePath)
62
+ .then((bot) => {
63
+ console.log(`BotQL: bot "${bot.botName || '(sem nome)'}" iniciado.`);
64
+ if (!bot.running) {
65
+ console.log('Aviso: o ficheiro não tem "RUN BOT" — o bot foi carregado mas não marcado como em execução.');
66
+ }
67
+ })
68
+ .catch((err) => {
69
+ console.error(err.message);
70
+ process.exit(1);
71
+ });
72
+ }
73
+
74
+ module.exports = { bootstrap };
@@ -0,0 +1,17 @@
1
+ {
2
+ "TELEGRAM": "./connectors/telegram.js",
3
+ "WHATSAPP": "./connectors/whatsapp.js",
4
+ "DISCORD": "./connectors/discord.js",
5
+ "SLACK": "./connectors/slack.js",
6
+ "MESSENGER": "./connectors/messenger.js",
7
+ "INSTAGRAM": "./connectors/instagram.js",
8
+ "TWITTER": "./connectors/twitter.js",
9
+ "SMS": "./connectors/sms.js",
10
+ "EMAIL": "./connectors/email.js",
11
+ "WEBHOOK": "./connectors/webhook.js",
12
+ "VIBER": "./connectors/viber.js",
13
+ "LINE": "./connectors/line.js",
14
+ "WECHAT": "./connectors/wechat.js",
15
+ "MATRIX": "./connectors/matrix.js",
16
+ "GOOGLECHAT": "./connectors/googlechat.js"
17
+ }
package/botql-mode.js ADDED
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Modo BotQL para CodeMirror 5 (addon/mode/simple.js).
5
+ *
6
+ * Usa exatamente a mesma lista de keywords do Parser.js (KEYWORDS),
7
+ * para nunca desalinhar highlight vs. sintaxe real aceite pelo parser.
8
+ *
9
+ * Uso (depois de incluir codemirror.js, mode/sql/sql.js NÃO é necessário,
10
+ * e addon/mode/simple.js):
11
+ *
12
+ * <script src="codemirror.js"></script>
13
+ * <script src="addon/mode/simple.js"></script>
14
+ * <script src="botql-mode.js"></script>
15
+ *
16
+ * const editor = CodeMirror(document.body, {
17
+ * mode: "botql",
18
+ * theme: "default"
19
+ * });
20
+ */
21
+
22
+ // Mesma lista do Parser.js — SQL real (CREATE, INSERT, UPDATE, ...)
23
+ // e BotQL (ON, WHEN, REPLY, ...) juntas, porque highlight não separa
24
+ // os dois mundos: é uma linguagem só.
25
+ const BOTQL_KEYWORDS = [
26
+ 'CREATE', 'BOT', 'PLATFORM', 'CONNECT', 'TABLE', 'PREVENT', 'DEFAULT',
27
+ 'ON', 'START', 'MESSAGE', 'FROM',
28
+ 'WHEN', 'CONTAINS', 'OR', 'OTHERWISE',
29
+ 'REPLY', 'TO', 'FORWARD', 'PARSE', 'SEND',
30
+ 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'SET', 'WHERE',
31
+ 'IMPORT', 'RUN'
32
+ ];
33
+
34
+ // Palavras especiais que merecem cor própria por serem "mágicas"
35
+ // no interpreter (botql.js), não por serem sintaxe da gramática.
36
+ const BOTQL_BUILTINS = ['Context', 'NOW', 'LAST_INSERT_ID'];
37
+
38
+ // Variáveis implícitas do runtime (client, message, lastMsg, SIGNAL).
39
+ const BOTQL_VARS = ['client', 'message', 'lastMsg', 'SIGNAL'];
40
+
41
+ function defineBotQLMode(CodeMirror) {
42
+ const keywordRegex = new RegExp('^(?:' + BOTQL_KEYWORDS.join('|') + ')\\b');
43
+ const builtinRegex = new RegExp('^(?:' + BOTQL_BUILTINS.join('|') + ')\\b');
44
+ const varRegex = new RegExp('^(?:' + BOTQL_VARS.join('|') + ')\\b');
45
+
46
+ CodeMirror.defineSimpleMode('botql', {
47
+ start: [
48
+ { regex: /--.*/, token: 'comment' },
49
+ { regex: /"(?:[^"\\]|\\.)*"?/, token: 'string' },
50
+ { regex: /'(?:[^'\\]|\\.)*'?/, token: 'string' },
51
+ { regex: /\b\d+(?:\.\d+)?\b/, token: 'number' },
52
+ { regex: keywordRegex, token: 'keyword' },
53
+ { regex: builtinRegex, token: 'builtin' },
54
+ { regex: varRegex, token: 'variable-2' },
55
+ { regex: /[A-Za-z_]\w*(?=\s*\()/, token: 'variable-2' }, // chamadas: NOME(
56
+ { regex: /[{}]/, token: 'bracket' },
57
+ { regex: /[()]/, token: 'bracket' },
58
+ { regex: /[+=,.]/, token: 'operator' },
59
+ { regex: /[A-Za-z_]\w*/, token: 'variable' }
60
+ ],
61
+ meta: {
62
+ lineComment: '--'
63
+ }
64
+ });
65
+ }
66
+
67
+ if (typeof module !== 'undefined' && module.exports) {
68
+ module.exports = { defineBotQLMode, BOTQL_KEYWORDS, BOTQL_BUILTINS, BOTQL_VARS };
69
+ }
70
+ if (typeof window !== 'undefined' && window.CodeMirror) {
71
+ defineBotQLMode(window.CodeMirror);
72
+ }
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * _shared.js — utilidades comuns aos connectors reais.
5
+ *
6
+ * A credencial de um CONNECT em BotQL é sempre uma única string
7
+ * (CONNECT SERVICO TIPO "credencial"). Quando a plataforma precisa de mais
8
+ * do que um valor (ex: WhatsApp precisa de token + phone_number_id),
9
+ * a credencial deve ser escrita como uma string JSON:
10
+ *
11
+ * CONNECT WHATSAPP CLOUD_API "{\"token\":\"...\",\"phoneNumberId\":\"...\"}"
12
+ *
13
+ * parseCredential() trata os dois casos: JSON válido vira objeto,
14
+ * qualquer outra coisa fica disponível em { token: credential }.
15
+ */
16
+ function parseCredential(credential) {
17
+ if (typeof credential !== 'string') return credential || {};
18
+ try {
19
+ const parsed = JSON.parse(credential);
20
+ if (parsed && typeof parsed === 'object') return parsed;
21
+ return { token: credential };
22
+ } catch {
23
+ return { token: credential };
24
+ }
25
+ }
26
+
27
+ // Wrapper fino sobre fetch: lança erro com o corpo da resposta quando o
28
+ // pedido falha, para os erros aparecerem claros nos logs do bot.
29
+ async function httpJson(url, options = {}) {
30
+ const res = await fetch(url, options);
31
+ const raw = await res.text();
32
+ let body;
33
+ try { body = raw ? JSON.parse(raw) : null; } catch { body = raw; }
34
+
35
+ if (!res.ok) {
36
+ const detail = typeof body === 'string' ? body : JSON.stringify(body);
37
+ throw new Error(`BotQL connector: pedido HTTP falhou (${res.status}): ${detail}`);
38
+ }
39
+ return body;
40
+ }
41
+
42
+ module.exports = { parseCredential, httpJson };
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Discord — envia mensagem a um canal usando um bot token.
7
+ * CONNECT DISCORD BOT "bot-token-do-discord"
8
+ * O "target" em REPLY/FORWARD/SEND deve ser o ID do canal.
9
+ */
10
+ module.exports = function discordConnector(credential) {
11
+ const { token } = parseCredential(credential);
12
+
13
+ return {
14
+ async send(channelId, text) {
15
+ return httpJson(`https://discord.com/api/v10/channels/${channelId}/messages`, {
16
+ method: 'POST',
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ Authorization: `Bot ${token}`
20
+ },
21
+ body: JSON.stringify({ content: text })
22
+ });
23
+ }
24
+ };
25
+ };
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Email via SendGrid (sem dependências externas, só a API HTTP).
7
+ * CONNECT EMAIL SENDGRID "{\"apiKey\":\"...\",\"from\":\"bot@dominio.com\"}"
8
+ * O "target" deve ser o endereço de email destinatário.
9
+ */
10
+ module.exports = function emailConnector(credential) {
11
+ const { apiKey, from } = parseCredential(credential);
12
+
13
+ return {
14
+ async send(to, text) {
15
+ return httpJson('https://api.sendgrid.com/v3/mail/send', {
16
+ method: 'POST',
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ Authorization: `Bearer ${apiKey}`
20
+ },
21
+ body: JSON.stringify({
22
+ personalizations: [{ to: [{ email: to }] }],
23
+ from: { email: from },
24
+ subject: 'Mensagem do bot',
25
+ content: [{ type: 'text/plain', value: text }]
26
+ })
27
+ });
28
+ }
29
+ };
30
+ };
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Google Chat — webhook de um espaço (Space). Não precisa de OAuth quando se
7
+ * usa o webhook do próprio espaço.
8
+ * CONNECT GOOGLECHAT WEBHOOK "https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=..."
9
+ * O "target" é ignorado (o webhook já aponta a um espaço fixo).
10
+ */
11
+ module.exports = function googleChatConnector(credential) {
12
+ const { token: url } = parseCredential(credential); // credencial simples = URL do webhook
13
+
14
+ return {
15
+ async send(_target, text) {
16
+ return httpJson(url, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body: JSON.stringify({ text })
20
+ });
21
+ }
22
+ };
23
+ };
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Instagram Messaging API (conta profissional ligada a uma Página, via Graph API).
7
+ * CONNECT INSTAGRAM PAGE "{\"token\":\"...\",\"igUserId\":\"...\"}"
8
+ * O "target" deve ser o IGSID (Instagram-scoped id) do utilizador.
9
+ */
10
+ module.exports = function instagramConnector(credential) {
11
+ const { token, igUserId } = parseCredential(credential);
12
+ const url = `https://graph.facebook.com/v20.0/${igUserId}/messages?access_token=${encodeURIComponent(token)}`;
13
+
14
+ return {
15
+ async send(igsid, text) {
16
+ return httpJson(url, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body: JSON.stringify({
20
+ recipient: { id: igsid },
21
+ message: { text }
22
+ })
23
+ });
24
+ }
25
+ };
26
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * LINE Messaging API.
7
+ * CONNECT LINE BOT "channel-access-token-do-line"
8
+ * O "target" deve ser o userId do LINE.
9
+ */
10
+ module.exports = function lineConnector(credential) {
11
+ const { token } = parseCredential(credential);
12
+
13
+ return {
14
+ async send(userId, text) {
15
+ return httpJson('https://api.line.me/v2/bot/message/push', {
16
+ method: 'POST',
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ Authorization: `Bearer ${token}`
20
+ },
21
+ body: JSON.stringify({
22
+ to: userId,
23
+ messages: [{ type: 'text', text }]
24
+ })
25
+ });
26
+ }
27
+ };
28
+ };
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Matrix — envia mensagens de texto a uma sala usando um access token de bot.
7
+ * CONNECT MATRIX BOT "{\"homeserver\":\"https://matrix.org\",\"accessToken\":\"...\"}"
8
+ * O "target" deve ser o room ID (ex: "!sala:matrix.org").
9
+ */
10
+ module.exports = function matrixConnector(credential) {
11
+ const { homeserver, accessToken } = parseCredential(credential);
12
+
13
+ return {
14
+ async send(roomId, text) {
15
+ const txnId = Date.now();
16
+ const url = `${homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${txnId}`;
17
+ return httpJson(url, {
18
+ method: 'PUT',
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ Authorization: `Bearer ${accessToken}`
22
+ },
23
+ body: JSON.stringify({ msgtype: 'm.text', body: text })
24
+ });
25
+ }
26
+ };
27
+ };
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Facebook Messenger Send API.
7
+ * CONNECT MESSENGER PAGE "token-de-acesso-da-pagina"
8
+ * O "target" deve ser o PSID (page-scoped id) do utilizador.
9
+ */
10
+ module.exports = function messengerConnector(credential) {
11
+ const { token } = parseCredential(credential);
12
+ const url = `https://graph.facebook.com/v20.0/me/messages?access_token=${encodeURIComponent(token)}`;
13
+
14
+ return {
15
+ async send(psid, text) {
16
+ return httpJson(url, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body: JSON.stringify({
20
+ recipient: { id: psid },
21
+ message: { text }
22
+ })
23
+ });
24
+ }
25
+ };
26
+ };
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Slack Web API.
7
+ * CONNECT SLACK BOT "xoxb-token-do-bot"
8
+ * O "target" deve ser o ID do canal ou utilizador (ex: "C0123456").
9
+ */
10
+ module.exports = function slackConnector(credential) {
11
+ const { token } = parseCredential(credential);
12
+
13
+ return {
14
+ async send(channel, text) {
15
+ return httpJson('https://slack.com/api/chat.postMessage', {
16
+ method: 'POST',
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ Authorization: `Bearer ${token}`
20
+ },
21
+ body: JSON.stringify({ channel, text })
22
+ });
23
+ }
24
+ };
25
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * SMS via Twilio.
7
+ * CONNECT SMS TWILIO "{\"accountSid\":\"...\",\"authToken\":\"...\",\"from\":\"+244...\"}"
8
+ * O "target" deve ser o número de telefone destinatário, em formato E.164.
9
+ */
10
+ module.exports = function smsConnector(credential) {
11
+ const { accountSid, authToken, from } = parseCredential(credential);
12
+ const url = `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`;
13
+ const auth = Buffer.from(`${accountSid}:${authToken}`).toString('base64');
14
+
15
+ return {
16
+ async send(to, text) {
17
+ const body = new URLSearchParams({ From: from, To: to, Body: text });
18
+ return httpJson(url, {
19
+ method: 'POST',
20
+ headers: {
21
+ 'Content-Type': 'application/x-www-form-urlencoded',
22
+ Authorization: `Basic ${auth}`
23
+ },
24
+ body: body.toString()
25
+ });
26
+ }
27
+ };
28
+ };
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Telegram Bot API.
7
+ * CONNECT TELEGRAM BOT "123456:ABC-token-do-bot"
8
+ */
9
+ module.exports = function telegramConnector(credential) {
10
+ const { token } = parseCredential(credential);
11
+ const base = `https://api.telegram.org/bot${token}`;
12
+
13
+ return {
14
+ async send(chatId, text) {
15
+ return httpJson(`${base}/sendMessage`, {
16
+ method: 'POST',
17
+ headers: { 'Content-Type': 'application/json' },
18
+ body: JSON.stringify({ chat_id: chatId, text })
19
+ });
20
+ }
21
+ };
22
+ };
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Twitter/X API v2 — Direct Messages.
7
+ * CONNECT TWITTER BEARER "bearer-token-com-permissao-dm"
8
+ * O "target" deve ser o ID numérico do utilizador destinatário.
9
+ */
10
+ module.exports = function twitterConnector(credential) {
11
+ const { token } = parseCredential(credential);
12
+
13
+ return {
14
+ async send(userId, text) {
15
+ return httpJson('https://api.twitter.com/2/dm_conversations/with/' + userId + '/messages', {
16
+ method: 'POST',
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ Authorization: `Bearer ${token}`
20
+ },
21
+ body: JSON.stringify({ text })
22
+ });
23
+ }
24
+ };
25
+ };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Viber REST API.
7
+ * CONNECT VIBER BOT "token-da-conta-viber"
8
+ * O "target" deve ser o "receiver" (id do utilizador Viber).
9
+ */
10
+ module.exports = function viberConnector(credential) {
11
+ const { token } = parseCredential(credential);
12
+
13
+ return {
14
+ async send(receiver, text) {
15
+ return httpJson('https://chatapi.viber.com/pa/send_message', {
16
+ method: 'POST',
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ 'X-Viber-Auth-Token': token
20
+ },
21
+ body: JSON.stringify({
22
+ receiver,
23
+ type: 'text',
24
+ text
25
+ })
26
+ });
27
+ }
28
+ };
29
+ };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * Webhook genérico: entrega a mensagem a qualquer endpoint HTTP próprio.
7
+ * CONNECT WEBHOOK POST "https://meu-servidor.com/receber"
8
+ * ou, com autenticação:
9
+ * CONNECT WEBHOOK POST "{\"url\":\"https://...\",\"secret\":\"...\"}"
10
+ * O "target" é incluído no corpo do pedido, para o servidor decidir o destino.
11
+ */
12
+ module.exports = function webhookConnector(credential) {
13
+ const parsed = parseCredential(credential);
14
+ const url = parsed.url || parsed.token; // token guarda a URL quando é string simples
15
+ const secret = parsed.secret;
16
+
17
+ return {
18
+ async send(target, text) {
19
+ const headers = { 'Content-Type': 'application/json' };
20
+ if (secret) headers['X-BotQL-Secret'] = secret;
21
+
22
+ return httpJson(url, {
23
+ method: 'POST',
24
+ headers,
25
+ body: JSON.stringify({ target, text })
26
+ });
27
+ }
28
+ };
29
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * WeChat (conta de serviço/oficial) — API de mensagens customer service.
7
+ * CONNECT WECHAT OFFICIAL "{\"accessToken\":\"...\"}"
8
+ * O "target" deve ser o OpenID do utilizador WeChat.
9
+ *
10
+ * Nota: o accessToken da WeChat expira periodicamente e normalmente exige
11
+ * um passo prévio de refresh (appId + appSecret -> token). Este connector
12
+ * assume que esse refresh já foi feito fora do bot e que recebe um token válido.
13
+ */
14
+ module.exports = function wechatConnector(credential) {
15
+ const { accessToken } = parseCredential(credential);
16
+ const url = `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${encodeURIComponent(accessToken)}`;
17
+
18
+ return {
19
+ async send(openId, text) {
20
+ return httpJson(url, {
21
+ method: 'POST',
22
+ headers: { 'Content-Type': 'application/json' },
23
+ body: JSON.stringify({
24
+ touser: openId,
25
+ msgtype: 'text',
26
+ text: { content: text }
27
+ })
28
+ });
29
+ }
30
+ };
31
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const { parseCredential, httpJson } = require('./_shared.js');
4
+
5
+ /**
6
+ * WhatsApp Cloud API (Meta).
7
+ * CONNECT WHATSAPP CLOUD_API "{\"token\":\"...\",\"phoneNumberId\":\"...\"}"
8
+ */
9
+ module.exports = function whatsappConnector(credential) {
10
+ const { token, phoneNumberId, apiVersion } = parseCredential(credential);
11
+ const version = apiVersion || 'v20.0';
12
+ const base = `https://graph.facebook.com/${version}/${phoneNumberId}`;
13
+
14
+ return {
15
+ async send(to, text) {
16
+ return httpJson(`${base}/messages`, {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/json',
20
+ Authorization: `Bearer ${token}`
21
+ },
22
+ body: JSON.stringify({
23
+ messaging_product: 'whatsapp',
24
+ to,
25
+ type: 'text',
26
+ text: { body: text }
27
+ })
28
+ });
29
+ }
30
+ };
31
+ };
package/package.json CHANGED
@@ -1,15 +1,21 @@
1
1
  {
2
2
  "name": "botql",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "BotQL is a simple, SQL-inspired rules language for creating bots without writing traditional code.",
5
5
  "main": "botql.js",
6
+ "browser": "botql.browser.js",
6
7
  "files": [
7
8
  "botql.js",
9
+ "botql.browser.js",
10
+ "botql-mode.js",
11
+ "Bootstrap.js",
8
12
  "Parser.js",
9
13
  "Database.js",
10
14
  "RAG.js",
11
15
  "FileSystem.js",
12
16
  "Connectors.js",
17
+ "Connectors.json",
18
+ "connectors/",
13
19
  "README.md",
14
20
  "LICENSE"
15
21
  ],
@@ -31,4 +37,4 @@
31
37
  "url": "https://github.com/adilson889/botql/issues"
32
38
  },
33
39
  "homepage": "https://github.com/adilson889/botql#readme"
34
- }
40
+ }