Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,46 @@
# DataScience
Curso DataScience
# Bitrix24 Open Channel Auto Bot (Local App)

Pequeno app local em PHP para registrar um bot que aparece na listagem de bots do Open Channel e responde automaticamente:
- Ao iniciar uma conversa
- Quando o usuário envia uma mensagem

## Estrutura
- `public/index.php`: UI de configuração
- `public/save-config.php`: salva mensagens padrão
- `public/install.php`: endpoint de instalação (chama imbot.bot.add/imbot.register)
- `public/events.php`: webhooks de eventos do bot
- `src/*`: utilitários e integrações
- `data/config.json`: mensagens padrão
- `data/state.json`: estado (ex.: BOT_ID)

## Requisitos
- PHP 8.0+
- Extensão cURL habilitada
- Servir o diretório `public` (ex.: `php -S 0.0.0.0:8080 -t public`)

## Passos de instalação no Bitrix24
1. Inicie o servidor local expondo o `public` (ou use um túnel HTTPS, ex.: ngrok)
```bash
php -S 0.0.0.0:8080 -t public
```
2. Tenha uma URL pública HTTPS, por exemplo via ngrok:
```bash
ngrok http http://localhost:8080
```
3. Em Bitrix24, crie um aplicativo local (Marketplace → Meus Aplicativos → Adicionar Aplicativo):
- URL de instalação: `https://SEU_DOMINIO/install.php`
- Nome: "OpenChannel Auto Bot"
- Permissões: ImBot, ImOpenLines
4. Instale o app e autorize. O endpoint de instalação registrará o bot com `OPENLINE=Y` e apontará os eventos para `https://SEU_DOMINIO/events.php`.
5. Em Contact Center / Open Channels, adicione o bot à fila/roteamento conforme necessário. Ele aparecerá na lista de bots disponíveis.

## Teste
- Inicie um chat no Open Channel; o bot deve enviar a mensagem de boas-vindas (configurável)
- Envie mensagens; o bot responde com a mensagem padrão

## Configuração
Acesse `https://SEU_DOMINIO/` para editar as mensagens padrão.

## Notas
- Em algumas contas a API antiga `imbot.register` pode ser necessária; o código tenta ambas.
- Para remover o bot, exclua-o no Bitrix24; o webhook `ONIMBOTDELETE` limpa o `data/state.json`.
1 change: 1 addition & 0 deletions data/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{\n "default_on_start": "Olá! Somos o suporte, já vamos te atender.",\n "default_on_message": "Recebemos sua mensagem e responderemos em breve."\n}\n
55 changes: 55 additions & 0 deletions public/events.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
require_once __DIR__ . '/../src/bootstrap.php';

use App\Bitrix;
use App\Config;
use App\Logger;

header('Content-Type: application/json; charset=utf-8');

try {
$req = Bitrix::readRequest();
Logger::log(['incoming' => $req]);

$event = $req['event'] ?? '';
$auth = $req['auth'] ?? $req;
$config = Config::load();
$state = Config::loadState();
$botId = (int)($state['bot_id'] ?? 0);

// Normalize payload fields across events
$params = $req['params'] ?? $req['data'] ?? $req;

if ($event === 'ONIMBOTJOINCHAT' || $event === 'ONIMBOTWELCOME') {
// Chat started. DIALOG_ID provided via params['DIALOG_ID'] or ['dialog']['id']
$dialogId = $params['DIALOG_ID'] ?? ($params['dialog']['id'] ?? null);
if ($dialogId && $botId) {
Bitrix::sendBotMessage($auth, $botId, (string)$dialogId, (string)($config['default_on_start'] ?? 'Olá!'));
}
echo json_encode(['ok' => true]);
return;
}

if ($event === 'ONIMBOTMESSAGEADD') {
// User sent message
$dialogId = $params['DIALOG_ID'] ?? ($params['message']['chat_id'] ?? $params['message']['dialog_id'] ?? null);
if ($dialogId && $botId) {
Bitrix::sendBotMessage($auth, $botId, (string)$dialogId, (string)($config['default_on_message'] ?? 'Obrigado pela mensagem!'));
}
echo json_encode(['ok' => true]);
return;
}

if ($event === 'ONIMBOTDELETE') {
// Clear state
Config::saveState([]);
echo json_encode(['ok' => true]);
return;
}

echo json_encode(['ok' => true, 'ignored' => $event]);
} catch (Throwable $e) {
http_response_code(500);
Logger::log(['error' => $e->getMessage()]);
echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
}
50 changes: 50 additions & 0 deletions public/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
require_once __DIR__ . '/../src/bootstrap.php';

use App\Config;

$config = Config::load();
?>
<!doctype html>
<html lang="pt-br">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Bitrix24 Open Channel Bot - Config</title>
<style>body{font-family:system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";max-width:760px;margin:40px auto;padding:0 16px;line-height:1.5}label{display:block;margin-top:16px}textarea{width:100%;min-height:80px}button{margin-top:16px;padding:10px 14px}code{background:#f6f8fa;padding:2px 6px;border-radius:4px}</style>
</head>
<body>
<h1>Configuração do Bot de Open Channel</h1>
<p>Defina as respostas padrão que o bot enviará ao iniciar um chat e ao receber mensagens.</p>
<form id="config-form">
<label>Mensagem ao iniciar conversa
<textarea name="default_on_start"><?php echo htmlspecialchars($config['default_on_start'] ?? '', ENT_QUOTES, 'UTF-8'); ?></textarea>
</label>
<label>Mensagem ao receber mensagem
<textarea name="default_on_message"><?php echo htmlspecialchars($config['default_on_message'] ?? '', ENT_QUOTES, 'UTF-8'); ?></textarea>
</label>
<button type="submit">Salvar</button>
</form>
<hr/>
<h2>Registro do Bot</h2>
<p>Use o endpoint <code>/install.php</code> como URL de instalação no Bitrix24, e os webhooks de eventos apontam para <code>/events.php</code>.</p>
<script>
const form = document.getElementById('config-form');
form.addEventListener('submit', async (e)=>{
e.preventDefault();
const fd = new FormData(form);
const payload = {
default_on_start: fd.get('default_on_start'),
default_on_message: fd.get('default_on_message')
};
const res = await fetch('/save-config.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
});
const data = await res.json();
alert(data.message || 'Salvo');
});
</script>
</body>
</html>
26 changes: 26 additions & 0 deletions public/install.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
require_once __DIR__ . '/../src/bootstrap.php';

use App\Bitrix;
use App\Config;
use App\Logger;

header('Content-Type: application/json; charset=utf-8');

try {
$req = Bitrix::readRequest();
// For Bitrix24 local app installation, we receive an auth array with domain/access_token.
$auth = $req['auth'] ?? $req;
if (!is_array($auth)) {
throw new RuntimeException('Auth ausente');
}

$baseUrl = Config::baseUrlFromGlobals();
$result = Bitrix::registerBot($auth, $baseUrl);
Logger::log(['event' => 'install', 'result' => $result]);
echo json_encode(['ok' => true, 'result' => $result]);
} catch (Throwable $e) {
http_response_code(500);
Logger::log(['error' => $e->getMessage()]);
echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
}
24 changes: 24 additions & 0 deletions public/save-config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
require_once __DIR__ . '/../src/bootstrap.php';

use App\Config;
use App\Logger;

$input = file_get_contents('php://input') ?: '';
$data = json_decode($input, true);
if (!is_array($data)) {
http_response_code(400);
echo json_encode(['ok' => false, 'message' => 'JSON inválido']);
exit;
}

$config = [
'default_on_start' => trim((string)($data['default_on_start'] ?? '')),
'default_on_message' => trim((string)($data['default_on_message'] ?? '')),
];

Config::save($config);
Logger::log(['event' => 'config_saved', 'config' => $config]);

header('Content-Type: application/json');
echo json_encode(['ok' => true, 'message' => 'Configurações salvas']);
112 changes: 112 additions & 0 deletions src/Bitrix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);

namespace App;

final class Bitrix
{
public static function readRequest(): array
{
$contentType = $_SERVER['CONTENT_TYPE'] ?? $_SERVER['HTTP_CONTENT_TYPE'] ?? '';
$raw = file_get_contents('php://input') ?: '';
$decoded = [];
if (stripos($contentType, 'application/json') !== false && $raw !== '') {
$decoded = json_decode($raw, true) ?: [];
}
if (!is_array($decoded) || !$decoded) {
// Fallback to form-urlencoded or query
$decoded = $_REQUEST ?: [];
}
return is_array($decoded) ? $decoded : [];
}

public static function call(array $auth, string $method, array $params = []): array
{
$token = $auth['access_token'] ?? $auth['auth'] ?? $auth['AUTH_ID'] ?? null;
$clientEndpoint = $auth['client_endpoint'] ?? null; // e.g., https://example.bitrix24.com/rest/
$domain = $auth['domain'] ?? null; // e.g., example.bitrix24.com

if (!$token || (!$clientEndpoint && !$domain)) {
throw new \RuntimeException('Auth inválido: domain/client_endpoint ou token ausentes');
}

// Build REST base URL depending on what is provided
if ($clientEndpoint) {
$base = rtrim($clientEndpoint, '/'); // already contains /rest
$url = $base . '/' . $method . '.json';
} else {
// Fallback to domain
$host = (strpos((string)$domain, 'http') === 0) ? rtrim((string)$domain, '/') : ('https://' . $domain);
$url = rtrim($host, '/') . '/rest/' . $method . '.json';
}

$postFields = $params;
$postFields['auth'] = $token;

$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postFields),
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
CURLOPT_TIMEOUT => 15,
]);
$resp = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
curl_close($ch);
if ($errno) {
throw new \RuntimeException('Erro HTTP: ' . $error);
}
$data = json_decode($resp ?: 'null', true);
return is_array($data) ? $data : ['raw' => $resp];
}

public static function registerBot(array $auth, string $baseUrl, array $overrides = []): array
{
$state = Config::loadState();
$code = $state['bot_code'] ?? ('openline_autobot_' . substr(md5(($overrides['seed'] ?? '') . microtime(true)), 0, 8));
$eventsUrl = rtrim($baseUrl, '/') . '/events.php';

$params = [
'CODE' => $code,
'TYPE' => 'B',
'OPENLINE' => 'Y',
'EVENT_MESSAGE_ADD' => $eventsUrl,
'EVENT_WELCOME_MESSAGE' => $eventsUrl,
'EVENT_BOT_DELETE' => $eventsUrl,
'FIELDS' => [
'NAME' => $overrides['NAME'] ?? 'OpenChannel Auto Bot',
'COLOR' => $overrides['COLOR'] ?? 'AQUA',
],
];

$result = self::call($auth, 'imbot.bot.add', $params);
if (isset($result['error'])) {
// Try alternative legacy method name if available
try {
$result = self::call($auth, 'imbot.register', $params);
} catch (\Throwable $e) {
// keep original
}
}

if (!empty($result['result'])) {
$botId = (int)$result['result'];
$state['bot_id'] = $botId;
$state['bot_code'] = $code;
Config::saveState($state);
}
return $result;
}

public static function sendBotMessage(array $auth, int $botId, string $dialogId, string $message): array
{
$params = [
'BOT_ID' => $botId,
'DIALOG_ID' => $dialogId,
'MESSAGE' => $message,
];
return self::call($auth, 'imbot.message.add', $params);
}
}
Loading