diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index f6445d8..7e50840 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -3,34 +3,42 @@ "public_html": { "ajax.php": { "type": "-", - "size": 1208, + "size": 1340, "lmtime": 0, - "modified": false + "modified": true }, "api.php": { "type": "-", - "size": 2446, + "size": 3758, "lmtime": 0, - "modified": false + "modified": true }, "autoload": {}, "ceidg.php": { "type": "-", - "size": 3862, + "size": 3950, "lmtime": 0, - "modified": false + "modified": true + }, + ".claude": { + "settings.local.json": { + "type": "-", + "size": 72, + "lmtime": 1770583517578, + "modified": false + } }, "config.php": { "type": "-", - "size": 355, - "lmtime": 0, + "size": 729, + "lmtime": 1770583610342, "modified": false }, "cron.php": { "type": "-", - "size": 1777, + "size": 2093, "lmtime": 0, - "modified": false + "modified": true }, ".htaccess": { "type": "-", @@ -40,12 +48,18 @@ }, "index.php": { "type": "-", - "size": 2372, + "size": 2464, "lmtime": 0, - "modified": false + "modified": true }, "layout": {}, "libraries": {}, + "REFACTORING_PLAN.md": { + "type": "-", + "size": 3576, + "lmtime": 0, + "modified": false + }, "robots.txt": { "type": "-", "size": 25, @@ -53,7 +67,59 @@ "modified": false }, "temp": {}, - "templates": {}, + "templates": { + "tasks": { + "filtr_save_form.php": { + "type": "-", + "size": 775, + "lmtime": 0, + "modified": false + }, + "main_view_by_ajax.php": { + "type": "-", + "size": 284, + "lmtime": 0, + "modified": false + }, + "main_view.php": { + "type": "-", + "size": 42341, + "lmtime": 1770583657535, + "modified": false + }, + "task_edit.php": { + "type": "-", + "size": 10202, + "lmtime": 0, + "modified": false + }, + "task_popup.php": { + "type": "-", + "size": 11071, + "lmtime": 0, + "modified": false + }, + "task_single.php": { + "type": "-", + "size": 2893, + "lmtime": 0, + "modified": false + }, + "work-time.php": { + "type": "-", + "size": 12396, + "lmtime": 0, + "modified": false + } + } + }, + "tests": {}, + "tmp_debug_mail_import.php": { + "type": "-", + "size": 1238, + "lmtime": 0, + "modified": false + }, "upload": {} } }, diff --git a/autoload/Domain/Tasks/MailToTaskImporter.php b/autoload/Domain/Tasks/MailToTaskImporter.php index 1478eb0..0a34310 100644 --- a/autoload/Domain/Tasks/MailToTaskImporter.php +++ b/autoload/Domain/Tasks/MailToTaskImporter.php @@ -88,8 +88,34 @@ class MailToTaskImporter try { $content = $this -> extractMessageContent( $imap, $message_no ); - $task_name = trim( $subject ) !== '' ? trim( $subject ) : '(bez tematu)'; - $task_text = $this -> prepareImportedTaskText( $content['text'] ); + + // Sprawdź czy użyć AI do parsowania + global $settings; + $use_ai = isset( $settings['openai_parse_emails'] ) && $settings['openai_parse_emails'] === true; + $api_key = isset( $settings['openai_api_key'] ) ? trim( (string)$settings['openai_api_key'] ) : ''; + + if ( $use_ai && $api_key !== '' ) + { + $model = isset( $settings['openai_model'] ) ? trim( (string)$settings['openai_model'] ) : 'gpt-3.5-turbo'; + $ai_result = $this -> parseWithAI( $api_key, $model, $subject, $content['text'] ); + if ( $ai_result !== null ) + { + $task_name = $ai_result['task_name']; + $task_text = $ai_result['task_text']; + } + else + { + // Fallback do normalnego parsowania jeśli AI nie zadziała + $task_name = trim( $subject ) !== '' ? trim( $subject ) : '(bez tematu)'; + $task_text = $this -> prepareImportedTaskText( $content['text'] ); + } + } + else + { + // Normalne parsowanie + $task_name = trim( $subject ) !== '' ? trim( $subject ) : '(bez tematu)'; + $task_text = $this -> prepareImportedTaskText( $content['text'] ); + } $client_id = $this -> resolveClientIdBySenderDomain( $sender ); $task_id = \factory\Tasks::task_save( @@ -648,4 +674,96 @@ class MailToTaskImporter $value = trim( $value, '<>' ); return strtolower( $value ); } + + private function parseWithAI( $api_key, $model, $subject, $raw_content ) + { + $subject = trim( (string)$subject ); + $raw_content = trim( (string)$raw_content ); + $model = trim( (string)$model ); + + if ( $model === '' ) + $model = 'gpt-3.5-turbo'; + + // Przygotuj treść do analizy (usuń HTML jesli jest) + if ( preg_match( '/<\s*[a-z][^>]*>/i', $raw_content ) ) + { + $raw_content = $this -> htmlToText( $raw_content ); + $raw_content = $this -> cleanBodyText( $raw_content ); + } + + $prompt = "Jesteś asystentem do przetwarzania emaili na zadania w systemie CRM. " . + "Na podstawie poniższego tematu i treści emaila, wygeneruj:\n" . + "1. Zwięzły, konkretny temat zadania (max 100 znaków)\n" . + "2. Czytelny opis zadania zawierający najważniejsze informacje\n\n" . + "Temat emaila: " . $subject . "\n\n" . + "Treść emaila:\n" . mb_substr( $raw_content, 0, 2000 ) . "\n\n" . + "Odpowiedz TYLKO w formacie JSON bez żadnych dodatkowych wyjaśnień:\n" . + '{"task_name": "temat zadania", "task_text": "opis zadania"}'; + + $payload = [ + 'model' => $model, + 'messages' => [ + [ + 'role' => 'system', + 'content' => 'Jesteś asystentem do przetwarzania emaili. Odpowiadasz TYLKO w formacie JSON.' + ], + [ + 'role' => 'user', + 'content' => $prompt + ] + ], + 'temperature' => 0.3, + 'max_tokens' => 500 + ]; + + $ch = curl_init( 'https://api.openai.com/v1/chat/completions' ); + curl_setopt_array( $ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode( $payload ), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . $api_key + ], + CURLOPT_TIMEOUT => 30 + ] ); + + $response = curl_exec( $ch ); + $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); + curl_close( $ch ); + + if ( $http_code !== 200 || !$response ) + return null; + + $data = json_decode( $response, true ); + if ( !isset( $data['choices'][0]['message']['content'] ) ) + return null; + + $content = trim( $data['choices'][0]['message']['content'] ); + + // Usuń markdown code block jeśli jest + $content = preg_replace( '/```json\s*|\s*```/', '', $content ); + $content = trim( $content ); + + $parsed = json_decode( $content, true ); + if ( !is_array( $parsed ) || !isset( $parsed['task_name'] ) || !isset( $parsed['task_text'] ) ) + return null; + + $task_name = trim( (string)$parsed['task_name'] ); + $task_text = trim( (string)$parsed['task_text'] ); + + if ( $task_name === '' ) + $task_name = '(bez tematu)'; + + if ( $task_text === '' ) + $task_text = '(brak treści)'; + + // Formatuj treść zadania z nl2br + $task_text = nl2br( $task_text ); + + return [ + 'task_name' => $task_name, + 'task_text' => $task_text + ]; + } } diff --git a/config.php b/config.php index bb91068..8937d0d 100644 --- a/config.php +++ b/config.php @@ -18,3 +18,8 @@ $imap_tasks['password'] = 'ProjectPro2025!'; // Auto-start timer when opening task popup (true/false) $settings['tasks_auto_start_timer'] = false; + +// OpenAI ChatGPT API configuration for email task parsing +$settings['openai_api_key'] = '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'; // Wklej tutaj swój klucz API OpenAI +$settings['openai_parse_emails'] = true; // true = użyj AI do parsowania emaili, false = normalne parsowanie +$settings['openai_model'] = 'gpt-3.5-turbo'; // Model: gpt-3.5-turbo (najtańszy), gpt-4o-mini, gpt-4o, itp.