diff --git a/public/webhook-clickup/index.php b/public/webhook-clickup/index.php index 07cad04..687a372 100644 --- a/public/webhook-clickup/index.php +++ b/public/webhook-clickup/index.php @@ -1,167 +1,176 @@ ClickUp bot # Copyright (C) 2023 Valerio Bozzolan, contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . /** * Receive ClickUp webhooks * * Documentation: * https://clickup.com/api/developer-portal/webhooktaskpayloads/ */ // default response code http_response_code( 500 ); require realpath( __DIR__ ) . '/../autoload.php'; $response = file_get_contents('php://input'); $response_data = json_decode( $response ); $event = $response_data->event ?? null; $clickup_task_id = $response_data->task_id ?? null; $webhook_id = $_SERVER['HTTP_X_SIGNATURE'] ?? null; // find history items (and the first one) $history_items = $response_data->history_items ?? []; $history_item_first = null; foreach( $history_items as $history_item_first ) { break; } // check incoming request if( CLICKUP_VALIDATE_WEBHOOK ) { // TODO //if( $webhook_id !== CLICKUP_REGISTERED_WEBHOOK_ID ) { // throw new Exception( sprintf( "unknown webhook ID: %s", $webhook_id ) ); //} $checkorigintoken = $_GET['checkorigintoken'] ?? null; if( $checkorigintoken !== CLICKUP_WEBHOOK_CHECKORIGINTOKEN ) { echo "Invalid check origin token\n"; http_response_code( 400 ); exit; } } $cache = ClickUpPhabricatorCache::instance(); switch( $event ) { case 'taskCreated': - // get more ClickUp info (title and description etc.) and remember in cache - $clickup_task_details = ClickUpAPI::queryTaskData( $clickup_task_id ); - - // TODO: move in the QueryTaskData some validation on these - $clickup_task_title = $clickup_task_details->name; - $clickup_task_descr = $clickup_task_details->description; - $clickup_task_folder = $clickup_task_details->folder; - $clickup_task_folder_id = $clickup_task_folder->id; - $clickup_task_folder_name = $clickup_task_folder->name; - - if( !$clickup_task_title ) { - throw new Exception( "missing title in new ClickUp Task" ); - } + // check if we already know about it + $phab_task_id = $cache->getClickupTaskPhabricatorID( $clickup_task_id ); + if( $phab_task_id ) { + // probably Phabricator already created it from the Phabricator webhook + error_log( "skip new ClickUp Task $clickup_task_id: already imported in Phab as $phab_task_id" ); + } else { + // let's create the Task - // prepare description for new Task in Phabricator - $phab_task_description = $clickup_task_descr; - $phab_task_description .= "\n\n"; - $phab_task_description .= "> https://app.clickup.com/t/$clickup_task_id"; + // get more ClickUp info (title and description etc.) and remember in cache + $clickup_task_details = ClickUpAPI::queryTaskData( $clickup_task_id ); - $phab_task_args = []; + // TODO: move in the QueryTaskData some validation on these + $clickup_task_title = $clickup_task_details->name; + $clickup_task_descr = $clickup_task_details->description; + $clickup_task_folder = $clickup_task_details->folder; + $clickup_task_folder_id = $clickup_task_folder->id; + $clickup_task_folder_name = $clickup_task_folder->name; - // try to assign to the right project - $phab_task_project = $cache->getPhabricatorTagIDFromClickupFolderID( $clickup_task_folder_id ); - if( $phab_task_project ) { - // assign to the right project - $phab_task_args['projects.add'] = [ $phab_task_project ]; - } else { - // or just give an hint - $clickup_task_title = "[$clickup_task_folder_name] $clickup_task_title"; - } + if( !$clickup_task_title ) { + throw new Exception( "missing title in new ClickUp Task" ); + } - $phab_task_args['title'] = $clickup_task_title; - $phab_task_args['description'] = $phab_task_description; + // prepare description for new Task in Phabricator + $phab_task_description = $clickup_task_descr; + $phab_task_description .= "\n\n"; + $phab_task_description .= "> https://app.clickup.com/t/$clickup_task_id"; - // create Task in Phabricator - $phab_task_data = PhabricatorAPI::createTask( $phab_task_args ); - $phab_task_phid = $phab_task_data['object']['phid']; // TASK-PHID-00000000 - $phab_task_id = $phab_task_data['object']['id']; // 123 for T123 + $phab_task_args = []; - // associate the Phabricator Task to the Conduit Task in the cache - $cache->newlock()->registerClickupPhabricatorTaskID( $clickup_task_id, $phab_task_id )->save(); + // try to assign to the right project + $phab_task_project = $cache->getPhabricatorTagIDFromClickupFolderID( $clickup_task_folder_id ); + if( $phab_task_project ) { + // assign to the right project + $phab_task_args['projects.add'] = [ $phab_task_project ]; + } else { + // or just give an hint + $clickup_task_title = "[$clickup_task_folder_name] $clickup_task_title"; + } + + $phab_task_args['title'] = $clickup_task_title; + $phab_task_args['description'] = $phab_task_description; + + // create Task in Phabricator + $phab_task_data = PhabricatorAPI::createTask( $phab_task_args ); + $phab_task_phid = $phab_task_data['object']['phid']; // TASK-PHID-00000000 + $phab_task_id = $phab_task_data['object']['id']; // 123 for T123 - // update Click-Up adding the Phabricator URL - $phab_task_url = PHABRICATOR_URL . "T{$phab_task_id}"; - ClickUpAPI::putTaskData( $clickup_task_id, [], [ - 'description' => "$clickup_task_descr\n\n$phab_task_url", - ] ); + // associate the Phabricator Task to the Conduit Task in the cache + $cache->newlock()->registerClickupPhabricatorTaskID( $clickup_task_id, $phab_task_id )->save(); + + // update Click-Up adding the Phabricator URL + $phab_task_url = PHABRICATOR_URL . "T{$phab_task_id}"; + ClickUpAPI::putTaskData( $clickup_task_id, [], [ + 'description' => "$clickup_task_descr\n\n$phab_task_url", + ] ); + } break; case 'taskDeleted': // mark as invalid in Phabricator $phab_task_id = $cache->getClickupTaskPhabricatorID( $clickup_task_id ); if( $phab_task_id ) { PhabricatorAPI::editTask( $phab_task_id, [ 'status' => 'invalid', ] ); } // drop from cache $cache->removeClickupTask( $clickup_task_id ); break; case 'taskCommentPosted': // post the comment on Phabricator too $phab_task_id = $cache->getClickupTaskPhabricatorID( $clickup_task_id ); if( $phab_task_id && $history_item_first ) { $clickup_comment_author_name = $history_item_first->user->username; $clickup_comment_author_id = $history_item_first->user->id; $clickup_task_description = $history_item_first->comment->text_content; $phab_comment = "From **{$clickup_comment_author_name}**:\n\n" . $clickup_task_description; $phab_task_data = PhabricatorAPI::editTask( $phab_task_id, [ 'comment' => $phab_comment, ] ); } break; case 'taskStatusUpdated': $phab_task_id = $cache->getClickupTaskPhabricatorID( $clickup_task_id ); if( $phab_task_id && $history_item_first ) { $clickup_task_status = $history_item_first->data->status_type; $phab_task_status_new = ClickUpAPI::taskStatusTypeToPhabricator( $clickup_task_status ); if( $phab_task_status_new === 'resolved' || $phab_task_status_new === 'open' ) { PhabricatorAPI::editTask( $phab_task_id, [ 'status' => $phab_task_status_new, ] ); } else { error_log( "do nothing for status: $phab_task_status_new" ); } } break; default: echo "Method not supported.\n"; error_log( "unsupported ClickUp webhoook: $event" ); } // success http_response_code( 200 );