{"id":33074051,"url":"https://github.com/YSaxon/boltworks","last_synced_at":"2025-11-19T04:01:40.854Z","repository":{"id":135255875,"uuid":"497060172","full_name":"YSaxon/boltworks","owner":"YSaxon","description":"Easier more interesting Slackbots","archived":false,"fork":false,"pushed_at":"2024-05-21T05:47:02.000Z","size":1097,"stargazers_count":1,"open_issues_count":11,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-25T08:47:31.871Z","etag":null,"topics":["bolt","python","slack","slack-bolt","slack-bot","slackbot"],"latest_commit_sha":null,"homepage":"https://ysaxon.github.io/boltworks/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/YSaxon.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.md","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-27T16:10:38.000Z","updated_at":"2023-04-20T19:58:10.000Z","dependencies_parsed_at":"2024-01-03T04:56:23.059Z","dependency_job_id":"3dc7a625-28ee-4466-b16a-3279c61e8b78","html_url":"https://github.com/YSaxon/boltworks","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/YSaxon/boltworks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YSaxon%2Fboltworks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YSaxon%2Fboltworks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YSaxon%2Fboltworks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YSaxon%2Fboltworks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/YSaxon","download_url":"https://codeload.github.com/YSaxon/boltworks/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YSaxon%2Fboltworks/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285181688,"owners_count":27128334,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-19T02:00:05.673Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bolt","python","slack","slack-bolt","slack-bot","slackbot"],"created_at":"2025-11-14T10:00:28.637Z","updated_at":"2025-11-19T04:01:40.848Z","avatar_url":"https://github.com/YSaxon.png","language":"Python","funding_links":[],"categories":[":hammer_and_wrench: \u0026nbsp; Libraries and SDKs"],"sub_categories":["Python"],"readme":"# Boltworks\n\n\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/boltworks)\n[![PyPI version](https://badge.fury.io/py/boltworks.svg)](https://badge.fury.io/py/boltworks)\n[![codecov](https://codecov.io/gh/YSaxon/boltworks/branch/master/graph/badge.svg?token=MYK47OLRPF)](https://codecov.io/gh/YSaxon/boltworks)\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/ysaxon/boltworks/dev.yml)\n![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)\n\n\nA collection of various extensions for Slack's bolt library to help you more easily make better slackbots.\n\n[docs](https://ysaxon.github.io/boltworks/)\n\nThe main features are:\n* A fast and flexible way of posting nested information in a dynamically expandable GUI format\n* Easy CLI parsing using the ArgParse library (or an automagic function parser that determines what params you need)\n* Easy callbacks on buttons and other GUI elements\n\n\n\n## Getting Started\n\n```\npip install boltworks\n```\n\nFollow the instructions at https://github.com/slackapi/bolt-python to begin setting up a Slackbot. Note that BoltWorks is not presently designed for async use, but any of the non-async handlers should work. For testing purposes, socket mode tends to be the easiest. All the rest of the demos will assume you've already instantiated a slack `app`.\n\n\n## NodeTreeUI - dynamic nested information formatter\n\n**[See main article: docs/treenode.md](docs/treenode.md)**\n\n[(also see the TreeNodeUI class in docs)](https://ysaxon.github.io/boltworks/api/#boltworks.gui.treenodeui.TreeNodeUI)\n\nThis module allows you to display complex nested information neatly, in a user-clickable, expanding and contracting view.\nThe TreeNodeUI class handles all the logic of formatting these trees and responding to clicks.\n\n![weather_demo](https://user-images.githubusercontent.com/11711101/226973190-cdd88994-848a-493a-816c-5ff86e8e8a78.gif)\n\n## Easy CLIs with the @argparse_command decorator\n\n[argparse_command in docs](https://ysaxon.github.io/boltworks/api/#boltworks.cli.argparse_decorator)\n\nThis allows you to use Python's argparse library to process complex command line flags and options in Slack Commands. As usual, a --help flag will be generated for you. And if your method is type hinted, you can use Automagic mode to create a parser automagically.\n\nAll Slack parameters will be passed through to your method; you can use the 'args' catchall, and/or individual arguments like 'respond' or 'context' etc\nAll other parameters will be parsed from the command string when the command is run.\n\n#### The explicit way\n\n\n```\n# example taken from the argparser docs: https://docs.python.org/3/howto/argparse.html#conflicting-options\n\nparser = argparse.ArgumentParser(description=\"calculate X to the power of Y\")\ngroup = parser.add_mutually_exclusive_group()\ngroup.add_argument(\"-v\", \"--verbose\", action=\"store_true\")\ngroup.add_argument(\"-q\", \"--quiet\", action=\"store_true\")\nparser.add_argument(\"x\", type=int, help=\"the base\")\nparser.add_argument(\"y\", type=int, help=\"the exponent\")\n@app.command(\"/exponent\")\n@argparse_command(parser)\ndef power_calculator(respond,x,y,verbose,quiet):\n    answer = x**y\n    if quiet:\n        respond(text=answer)\n    elif verbose:\n        respond(text=f\"{x} to the power {y} equals {answer}\")\n    else:\n        respond(text=f\"{x}^{y} == {answer}\")\n                    \n          \n ```\n \n#### The automagic way\n\nIf you set automagic on, then providing an ArgParser is optional. Any type-hinted arguments not handled by an ArgParser you pass will be automagically added to an argparser with the appropriate names and types set. Lists, Optionals, Literals, default arguments, and primitive types all are supported.\n\n```\nfrom boltworks import argparse_command\n\n@app.command(re.compile(\"/exponent\"))\n@argparse_command(automagic=True)\ndef power_calculator(respond, x: int, y:int, mode:Optional[Literal[\"q\",\"v\"]]=None):\n    answer = x**y\n    if mode==\"q\":\n        respond(text=answer)\n    elif mode==\"v\":\n        respond(text=f\"{x} to the power {y} equals {answer}\")\n    else:\n        respond(text=f\"{x}^{y} == {answer}\")\n```\n\n\n## ActionCallbacks - easy callback serialization\n\n[ActionCallbacks in docs](https://ysaxon.github.io/boltworks/api/#boltworks.cli.argparse_decorator)\n\nThis class allows you to easily serialize a method as a callback for a Slack UI element such as a button.\nThese callbacks can themselves post UI elements with more callbacks for more complicated logic, and you can always use the `partial` class to inject some arguments into the callback method at the time you are creating the callback, as in the below example.  \n\n```\nDISK_CACHE_DIR=\"~/.diskcache\"\nfrom boltworks import ActionCallbacks,DiskCacheKVSTore\nfrom diskcache import Cache\n\ndisk_cache=DiskCacheKVStore(Cache(directory=DISK_CACHE_DIR))\ncallbacks=ActionCallbacks(app,disk_cache.using_serializer(dill))\n\ndef get_elapsed_time(args:Args, start:datetime):\n    diff = datetime.now() - start\n    formatted_diff = f\"{diff.seconds//3600:02d}:{(diff.seconds%3600)//60:02d}:{diff.seconds%60:02d}\"\n    args.respond(f\"time elapsed: {formatted_diff}\")\n    \ndef start_timer(args:Args):\n  now=datetime.now()\n  get_elapsed_button=callbacks.get_button_register_callback(\"get elapsed time\",partial(get_elapsed_time,start=now))\n  timer_started_message=\"Timer started at \"+now.strftime(\"%A, %B %d, %Y %I:%M:%S %p\")\n  block=slack_sdk.models.blocks.SectionBlock(text=timer_started_message,accessory=get_elapsed_button)\n  args.say(blocks=[block])\n\nstart_timer_button=callbacks.get_button_register_callback(\"start a timer\",start_timer)\ntimer_start_block=slack_sdk.models.blocks.SectionBlock(text=\"click here to start a timer\",accessory=start_timer_button)\napp.client.chat_postMessage(blocks=[timer_start_block],channel=CHANNEL_ID)\n```\n\n## ThreadCallbacks\n\nSimiliar to ActionCallbacks, this class allows you to register a message's `ts` (timestamp used by slack as a message id), so that your callback will be called any time a message is posted to that Thread.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYSaxon%2Fboltworks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FYSaxon%2Fboltworks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYSaxon%2Fboltworks/lists"}