Skip to content

integrations/clickup: Add ClickUp integration script. - #921

Open
sathwikshetty33 wants to merge 1 commit into
zulip:mainfrom
sathwikshetty33:clickup-integration-script
Open

integrations/clickup: Add ClickUp integration script.#921
sathwikshetty33 wants to merge 1 commit into
zulip:mainfrom
sathwikshetty33:clickup-integration-script

Conversation

@sathwikshetty33

@sathwikshetty33 sathwikshetty33 commented Jul 24, 2026

Copy link
Copy Markdown

This is a replacement PR for #824 .

The original PR used an OAuth approach. The bot was created without a ClickUp token, and the script ran an OAuth flow to generate one. That token, along with the team ID, was then appended to the bot's webhook URL as query parameters, so the key essentially lived in the URL.

The server side has since moved on. It now reads the ClickUp token from the bot's config data, where it is a required option at bot creation, and uses it to call ClickUp's API and turn the entity IDs in webhook payloads into readable names.

That is what rules OAuth out. The config option needs the token when the bot is created, but an OAuth flow can only return a token after the bot and its webhook URL already exist, so there is no point at which OAuth could provide what bot creation asks for.

This PR therefore drops OAuth entirely. Instead, the user generates a personal API token from the ClickUp UI (Settings > ClickUp API > API Token), enters it when creating the bot, and passes the same token to this script to register the webhook. The script now registers a plain webhook URL with nothing appended, since the server takes the team ID straight from the payload and the token from config.

Fixes part of zulip/zulip#26529
CZO: thread

How did you test this PR?
Manually invoked the script and added unit tests.
Testing video

Self-review checklist
  • Self-reviewed the changes for clarity and maintainability
    (variable names, code reuse, readability, etc.).

Communicate decisions, questions, and potential concerns.

  • Explains differences from previous plans (e.g., issue description).
  • Highlights technical choices and bugs encountered.
  • Calls out remaining decisions and concerns.
  • Automated tests verify logic where appropriate.

Individual commits are ready for review (see commit discipline).

  • Each commit is a coherent idea.
  • Commit message(s) explain reasoning and motivation for changes.

Completed manual review and testing of the following:

  • Visual appearance of the changes.
  • Responsiveness and internationalization.
  • Strings and tooltips.
  • End-to-end functionality of buttons, interactions and flows.
  • Corner cases, error conditions, and easily imagined bugs.

@sathwikshetty33
sathwikshetty33 force-pushed the clickup-integration-script branch 3 times, most recently from 1d63b18 to daa1323 Compare July 24, 2026 06:22
@sathwikshetty33

Copy link
Copy Markdown
Author

@0xthedance tagging you for a review.

@sathwikshetty33

Copy link
Copy Markdown
Author

@zulipbot add "buddy review"

@zulipbot zulipbot added the buddy review GSoC buddy review needed. label Jul 24, 2026

@0xthedance 0xthedance left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I've run the script for creating and replacing webhooks in my ClickUp app, and it works.
I've included a few slight suggestions, but feel free to skip any that don't feel clarifying

Comment thread zulip/integrations/clickup/README.md Outdated
Comment thread zulip/integrations/clickup/README.md
Comment thread zulip/integrations/clickup/zulip_clickup.py Outdated
Comment thread zulip/integrations/clickup/zulip_clickup.py Outdated
Comment thread zulip/integrations/clickup/zulip_clickup.py
Comment thread zulip/integrations/clickup/zulip_clickup.py
ClickUp payloads contain only entity IDs, so the server needs a stored
ClickUp token to resolve them to names. Use a personal API token instead
of OAuth: the token must be present in the bot config at bot-creation
time, whereas an OAuth flow can only issue one after the bot and webhook
URL already exist.

Fixes part of zulip/zulip#26529

Co-Authored-By: pieterck <pieterceka123@gmail.com>
@sathwikshetty33
sathwikshetty33 force-pushed the clickup-integration-script branch from daa1323 to b869dbc Compare July 28, 2026 05:12
@sathwikshetty33

Copy link
Copy Markdown
Author

@0xthedance Thanks for the review!

@sathwikshetty33

Copy link
Copy Markdown
Author

@zulipbot remove "buddy review"

@zulipbot zulipbot removed the buddy review GSoC buddy review needed. label Jul 28, 2026
@sathwikshetty33

Copy link
Copy Markdown
Author

@zulipbot add "mentor review"

@zulipbot zulipbot added the mentor review GSoC mentor review needed. label Jul 28, 2026
@sathwikshetty33

Copy link
Copy Markdown
Author

Tagging @sbansal1999 and @prah23 for review.

@sbansal1999 sbansal1999 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sathwikshetty33 for working on this one. I have left some comments on this PR; feel free to create a thread in CZO if you feel any of them needs any discussion.

Comment on lines +59 to +61
if response.status != 200:
print(f"Error : {response.status}")
sys.exit(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This does an abrupt sys.exit call rather than handling the error properly.
  2. Can this branch be deleted given urllib raises HTTPError when the response status is not 2XX?

"folder": ("folderCreated", "folderUpdated", "folderDeleted"),
"space": ("spaceCreated", "spaceUpdated", "spaceDeleted"),
"goal": ("goalCreated", "goalUpdated", "goalDeleted"),
"key result": ("keyResultCreated", "keyResultUpdated", "keyResultDeleted"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the above linked doc it says 'Target' instead of key result.

Can you check if that's the term being used now?

Comment on lines +135 to +140
if (
event_code.strip().isdigit()
and (event := code_to_event_dict.get(int(event_code))) in EVENT_CHOICES
):
if event_code not in exhausted_options:
events = EVENT_CHOICES[event]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can simplify this be using a code_to_events dict, whose key is the event code and the value will be an array with events for that value.

code_to_events = {i + 1: events for i, events in enumerate(EVENT_CHOICES.values())}

Comment on lines +18 to +19
- **Zulip webhook URL** — the URL generated by your Zulip incoming
webhook bot.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add the URL generation doc here; similar to how it's being done in the script's help section.

Comment on lines +129 to +130
user_input: list[str] = re.split(",", input_codes_list)
input_is_valid: bool = len(user_input) > 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we modify the parsing logic to accept inputs like 2,,5 in them (two commas).

So if the user enters 2,,5 we should consider it as 2,5.

selected_events: list[str] = []
input_codes_list: str = input("EVENT CODE(s): ")

if "*" in input_codes_list:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An in check would register all events for an input like 1,2* which actually is an invalid input and should be flagged accordingly.

input_is_valid = False

if not input_is_valid:
print("Please enter a valid set of options and only select each option once")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error text says "only select each option once", but duplicates in the input actually work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mentor review GSoC mentor review needed. size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants