{"id":18011276,"url":"https://github.com/johnnyjayjay/slash","last_synced_at":"2025-03-26T15:32:40.356Z","repository":{"id":44531769,"uuid":"388858192","full_name":"JohnnyJayJay/slash","owner":"JohnnyJayJay","description":"A small Clojure library for handling Discord interactions (slash commands and components) ","archived":false,"fork":false,"pushed_at":"2024-07-06T22:02:50.000Z","size":98,"stargazers_count":10,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-22T02:02:46.331Z","etag":null,"topics":["clojure","discord","discord-api","hacktoberfest","slash-commands"],"latest_commit_sha":null,"homepage":"","language":"Clojure","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/JohnnyJayJay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-23T16:13:52.000Z","updated_at":"2024-11-27T12:16:54.000Z","dependencies_parsed_at":"2024-07-06T23:29:14.281Z","dependency_job_id":null,"html_url":"https://github.com/JohnnyJayJay/slash","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnnyJayJay%2Fslash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnnyJayJay%2Fslash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnnyJayJay%2Fslash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnnyJayJay%2Fslash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JohnnyJayJay","download_url":"https://codeload.github.com/JohnnyJayJay/slash/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245681539,"owners_count":20655215,"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","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":["clojure","discord","discord-api","hacktoberfest","slash-commands"],"created_at":"2024-10-30T03:08:49.011Z","updated_at":"2025-03-26T15:32:39.999Z","avatar_url":"https://github.com/JohnnyJayJay.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# slash\n\nA small Clojure library designed to handle and route Discord interactions, both for gateway events and incoming webhooks. \n\nslash is environment-agnostic, extensible through middleware and works directly with Clojure data (no JSON parsing/printing included).\n\n[![Clojars Project](https://img.shields.io/clojars/v/com.github.johnnyjayjay/slash.svg)](https://clojars.org/com.github.johnnyjayjay/slash)\n\n**slash is currently in a Proof-of-Concept-phase and more features are to be added.**\\\nSuch features include:\n - Add more middleware: argument validation, permission checks, ...\n\n## Command Structure Definition\n\nslash provides utilities to define slash commands in `slash.command.structure`.\n\nOnce you are familiar with [how slash commands are structured](https://discord.com/developers/docs/interactions/application-commands), the functions should be self-explanatory.\n\nExamples:\n\n``` clojure\n(require '[slash.command.structure :refer :all])\n\n(def input-option (option \"input\" \"Your input\" :string :required true))\n\n(def echo-command\n  (command\n   \"echo\"\n   \"Echoes your input\"\n   :options\n   [input-option]))\n\n(def fun-commands\n  (command\n   \"fun\"\n   \"Fun commands\"\n   :options\n   [(sub-command\n     \"reverse\"\n     \"Reverse the input\"\n     :options\n     [input-option\n      (option \"words\" \"Reverse words instead of characters?\" :boolean)])\n    (sub-command\n     \"mock\"\n     \"Spongebob-mock the input\"\n     :options\n     [input-option])]))\n```\n\n## Component Structure Definition\n\nslash also provides similar utilities to create [message components](https://discord.com/developers/docs/interactions/message-components).\n\nExamples:\n\n``` clojure\n(require '[slash.component.structure :refer :all])\n\n(def my-components\n  [(action-row\n    (button :danger \"unsubscribe\" :label \"Turn notifications off\")\n    (button :success \"subcribe\" :label \"Turn notifications on\"))\n   (action-row\n    (select-menu\n     \"language\"\n     [(select-option \"English\" \"EN\" :emoji {:name \"🇬🇧\"})\n      (select-option \"French\" \"FR\" :emoji {:name \"🇫🇷\"})\n      (select-option \"Spanish\" \"ES\" :emoji {:name \"🇪🇸\"})]\n     :placeholder \"Language\"))])\n```\n\n## Routing \n\nYou can use slash to handle interaction events based on their type.\n\n``` clojure\n(slash.core/route-interaction handler-map interaction-event)\n```\n\n`handler-map` is a map containing handlers for the different types of interactions that may occur. E.g. \n\n``` clojure\n{:ping ping-handler\n :application-command command-handler\n :message-component component-handler}\n```\n\nYou can find default handler maps for both gateway and webhook environments in `slash.gateway`/`slash.webhook` respectively.\n\n### Commands\n\nslash offers further routing middleware and utilities specifically for slash commands. The API is heavily inspired by [compojure](https://github.com/weavejester/compojure). \n\nSimple, single-command example:\n\n``` clojure\n(require '[slash.command :as cmd] \n         '[slash.response :as rsp :refer [channel-message ephemeral]]) ; The response namespace provides utility functions to create interaction responses\n\n(cmd/defhandler echo-handler\n  [\"echo\"] ; Command path\n  _interaction ; Interaction binding - whatever you put here will be bound to the entire interaction\n  [input] ; Command options - can be either a vector or a custom binding (symbol, map destructuring, ...)\n  (channel-message {:content input}))\n```\n\nYou can now use `echo-handler` as a command handler to call with a command interaction event and it will return the response if it is an `echo` command or `nil` if it's not.\n\nAn example with multiple (sub-)commands:\n\n``` clojure\n(require '[clojure.string :as str])\n\n(cmd/defhandler reverse-handler\n  [\"reverse\"]\n  _\n  [input words]\n  (channel-message\n   {:content (if words\n               (-\u003e\u003e #\"\\s+\" (str/split input) reverse (str/join \" \"))\n               (str/reverse input))}))\n\n(cmd/defhandler mock-handler\n  [\"mock\"]\n  _\n  [input]\n  (channel-message\n   {:content (-\u003e\u003e input\n                  (str/lower-case)\n                  (map #(cond-\u003e % (rand-nth [true false]) Character/toUpperCase))\n                  str/join)}))\n                  \n(cmd/defhandler unknown-handler\n  [unknown] ; Placeholders can be used in paths too\n  {{{user-id :id} :user} :member} ; Using the interaction binding to get the user who ran the command\n  _ ; no options\n  (-\u003e (channel-message {:content (str \"I don't know the command `\" unknown \"`, \u003c@\" user-id \"\u003e.\")})\n      ephemeral))\n      \n(cmd/defpaths command-paths\n  (cmd/group [\"fun\"] ; common prefix for all following commands\n    reverse-handler \n    mock-hander\n    unknown-handler))\n```\n\nSimilar to the previous example, `command-paths` can now be used as a command handler. It will call each of its nested handlers with the interaction and stop once a handler is found that does not return `nil`.\n\n### Autocomplete \n\nYou can also use the command routing facilities to provide autocomplete for your commands.\n\n``` clojure\n;; Will produce autocompletion for command `/foo bar` on option `baz`, using the partial value of `baz` in the process\n(cmd/defhandler foo-bar-autocompleter\n  [\"foo\" \"bar\"]\n  {{:keys [focused-option]} :data}\n  [baz]\n  (case focused-option \n    :baz (rsp/autocomplete-result (map (partial str baz) [1 2 3]))))\n```\n\n### Full Webhook Example \n\nFor this example, I use the ring webserver specification.\n\nUsing [ring-json](https://github.com/ring-clojure/ring-json) and [ring-discord-auth](https://github.com/JohnnyJayJay/ring-discord-auth) we can create a ring handler for accepting outgoing webhooks.\n\n``` clojure\n(require '[slash.webhook :refer [webhook-defaults]]\n         '[ring-discord-auth.ring :refer [wrap-authenticate]]\n         '[ring.middleware.json :refer [wrap-json-body wrap-json-response]])\n\n(def ring-handler\n  (-\u003e (partial slash.core/route-interaction\n               (assoc webhook-defaults :application-command command-paths))\n      wrap-json-response\n      (wrap-json-body {:keyword? true})\n      (wrap-authenticate \"application public key\")))\n```\n\n### Full Gateway Example\n\nFor this example, I use [discljord](https://github.com/IGJoshua/discljord).\n\nYou also see the use of the `wrap-response-return` middleware for the interaction handler, which allows you to simply return the interaction\nresponses from your handlers and let the middleware respond via REST. You only need to provide a callback that specifies how to respond to the interaction (as I'm using discljord here, I used its functions for this purpose).\n\n``` clojure\n(require '[discljord.messaging :as rest]\n         '[discljord.connections :as gateway]\n         '[discljord.events :as events]\n         '[clojure.core.async :as a]\n         '[slash.gateway :refer [gateway-defaults wrap-response-return]])\n\n(let [rest-conn (rest/start-connection! \"bot token\")\n      event-channel (a/chan 100)\n      gateway-conn (gateway/connect-bot! \"bot token\" event-channel :intents #{})\n      event-handler (-\u003e slash.core/route-interaction\n                        (partial (assoc gateway-defaults :application-command command-paths))\n                        (wrap-response-return (fn [id token {:keys [type data]}]\n                                                (rest/create-interaction-response! rest-conn id token type :data data))))]\n  (events/message-pump! event-channel (partial events/dispatch-handlers {:interaction-create [#(event-handler %2)]})))\n```\nThis is a very quick and dirty example. More in-depth documentation and tutorials will follow soon.\n\n## clj-kondo support for macros\n\nYou can find a clj-kondo config that gets rid of \"unresolved symbol\" warnings in [.clj-kondo/](./.clj-kondo). Just copy [the hooks](./.clj-kondo/hooks) to your clj-kondo config folder (preserving the directory structure, of course!) and add this to your `config.edn`:\n\n``` clojure\n{:hooks {:analyze-call {slash.command/handler hooks.slash/handler\n                        slash.command/defhandler hooks.slash/defhandler\n                        slash.command/group hooks.slash/group\n                        slash.command/defpaths hooks.slash/defpaths}}}\n```\n\n## License\n\nCopyright © 2021-2023 JohnnyJayJay\n\nLicensed under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnnyjayjay%2Fslash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnnyjayjay%2Fslash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnnyjayjay%2Fslash/lists"}