diff --git a/README.md b/README.md
index 69cbc7e..d15048d 100644
--- a/README.md
+++ b/README.md
@@ -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`.
diff --git a/data/config.json b/data/config.json
new file mode 100644
index 0000000..2efb61e
--- /dev/null
+++ b/data/config.json
@@ -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
\ No newline at end of file
diff --git a/public/events.php b/public/events.php
new file mode 100644
index 0000000..ca0f592
--- /dev/null
+++ b/public/events.php
@@ -0,0 +1,55 @@
+ $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()]);
+}
diff --git a/public/index.php b/public/index.php
new file mode 100644
index 0000000..9fce82f
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+ Bitrix24 Open Channel Bot - Config
+
+
+
+ Configuração do Bot de Open Channel
+ Defina as respostas padrão que o bot enviará ao iniciar um chat e ao receber mensagens.
+
+
+ Registro do Bot
+ Use o endpoint /install.php
como URL de instalação no Bitrix24, e os webhooks de eventos apontam para /events.php
.
+
+
+
diff --git a/public/install.php b/public/install.php
new file mode 100644
index 0000000..b4ca91f
--- /dev/null
+++ b/public/install.php
@@ -0,0 +1,26 @@
+ '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()]);
+}
diff --git a/public/save-config.php b/public/save-config.php
new file mode 100644
index 0000000..32ee937
--- /dev/null
+++ b/public/save-config.php
@@ -0,0 +1,24 @@
+ 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']);
diff --git a/src/Bitrix.php b/src/Bitrix.php
new file mode 100644
index 0000000..6c77367
--- /dev/null
+++ b/src/Bitrix.php
@@ -0,0 +1,112 @@
+ 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);
+ }
+}
diff --git a/src/Config.php b/src/Config.php
new file mode 100644
index 0000000..e00f3f2
--- /dev/null
+++ b/src/Config.php
@@ -0,0 +1,78 @@
+ 'Olá! Somos o suporte, já vamos te atender.',
+ 'default_on_message' => 'Recebemos sua mensagem e responderemos em breve.'
+ ];
+ }
+
+ public static function save(array $config): void
+ {
+ if (!is_dir(DATA_DIR)) {
+ mkdir(DATA_DIR, 0775, true);
+ }
+ file_put_contents(self::configPath(), json_encode($config, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
+ }
+
+ public static function loadState(): array
+ {
+ $path = self::statePath();
+ if (is_file($path)) {
+ $json = file_get_contents($path);
+ $data = json_decode($json ?: '[]', true);
+ if (is_array($data)) {
+ return $data;
+ }
+ }
+ return [];
+ }
+
+ public static function saveState(array $state): void
+ {
+ if (!is_dir(DATA_DIR)) {
+ mkdir(DATA_DIR, 0775, true);
+ }
+ file_put_contents(self::statePath(), json_encode($state, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
+ }
+
+ public static function configPath(): string
+ {
+ return DATA_DIR . '/config.json';
+ }
+
+ public static function statePath(): string
+ {
+ return DATA_DIR . '/state.json';
+ }
+
+ public static function baseUrlFromGlobals(): string
+ {
+ $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
+ || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https')
+ || (($_SERVER['REQUEST_SCHEME'] ?? '') === 'https');
+ $scheme = $isHttps ? 'https' : 'http';
+ $host = $_SERVER['HTTP_HOST'] ?? ($_SERVER['SERVER_NAME'] ?? 'localhost');
+ $script = $_SERVER['SCRIPT_NAME'] ?? '/';
+ $dir = rtrim(str_replace('\\', '/', dirname($script)), '/');
+ if ($dir === '' || $dir === '.') {
+ $dir = '/';
+ }
+ $base = $scheme . '://' . $host . ($dir === '/' ? '' : $dir);
+ return $base;
+ }
+}
diff --git a/src/Logger.php b/src/Logger.php
new file mode 100644
index 0000000..315d89a
--- /dev/null
+++ b/src/Logger.php
@@ -0,0 +1,17 @@
+