diff --git a/.circleci/config.yml b/.circleci/config.yml index b595bace7..25ed714ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ commands: steps: - run: name: Install OS packages - command: apk add git build-base ruby-dev ruby-etc ruby-json libsodium + command: apk add git build-base ruby-dev ruby-etc ruby-json libsodium-dev - checkout - run: name: "Ruby version" @@ -27,45 +27,50 @@ commands: - ./vendor/bundle jobs: - test_ruby_26: + test_ruby_31: docker: - - image: ruby:2.6-alpine + - image: ruby:3.1-alpine steps: - setup-env - run: name: Run RSpec - command: bundle exec rspec + command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml + - store_test_results: + path: ~/rspec - test_ruby_27: + test_ruby_32: docker: - - image: ruby:2.7-alpine + - image: ruby:3.2-alpine steps: - setup-env - run: name: Run RSpec - command: bundle exec rspec + command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml + - store_test_results: + path: ~/rspec - test_ruby_30: + + test_ruby_33: docker: - - image: ruby:3.0-alpine + - image: ruby:3.3-alpine steps: - setup-env - run: name: Run RSpec - command: bundle exec rspec + command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml rubocop: docker: - - image: ruby:2.6-alpine + - image: ruby:3.3-alpine steps: - setup-env - run: name: Run Rubocop - command: bundle exec rubocop -P + command: bundle exec rubocop yard: docker: - - image: ruby:2.6-alpine + - image: ruby:3.3-alpine steps: - setup-env - attach_workspace: @@ -79,13 +84,22 @@ jobs: - docs pages: - machine: true + docker: + - image: alpine steps: + - run: + name: Install OS packages + command: apk add git openssh-client-default - attach_workspace: at: /tmp/workspace - run: name: Clone docs - command: git clone $CIRCLE_REPOSITORY_URL -b gh-pages . + command: | + mkdir -p ~/.ssh + + echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=' >> ~/.ssh/known_hosts + + git clone $CIRCLE_REPOSITORY_URL -b gh-pages . - add_ssh_keys: fingerprints: - "9a:4c:50:94:23:46:81:74:41:97:87:04:4e:59:4b:4e" @@ -111,9 +125,9 @@ jobs: workflows: test: jobs: - - test_ruby_26 - - test_ruby_27 - - test_ruby_30 + - test_ruby_31 + - test_ruby_32 + - test_ruby_33 - rubocop - yard deploy: diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..d2823c3bf --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,13 @@ +# Which Ruby version to use. You may need to use a more restrictive version, +# e.g. `3.0` +ARG VARIANT=3.3 + +# Pull Microsoft's ruby devcontainer base image +FROM mcr.microsoft.com/devcontainers/ruby:${VARIANT} + +# Install libsodium dependency for voice channel interactions +RUN apt update -yq && apt install -y libsodium-dev + +# Ensure we're running the latest bundler, as what ships with the Ruby image may +# not be current, and bundler will auto-downgrade to match the Gemfile.lock +RUN gem install bundler diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..f9fbeed2b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,29 @@ +{ + "name": "Ruby", + "build": { + "dockerfile": "Dockerfile" + }, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "shopify.ruby-lsp" + ] + } + }, + + // Set the environment variables + // "runArgs": ["--env-file",".env"], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "bash .devcontainer/postcreate.sh", + + // Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} diff --git a/.devcontainer/postcreate.sh b/.devcontainer/postcreate.sh new file mode 100644 index 000000000..01da0d59e --- /dev/null +++ b/.devcontainer/postcreate.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +bundle config set path vendor/bundle +bundle install --jobs=1 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 529140cd0..9bced0f41 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,6 @@ --- name: Bug report about: Report a bug to help us improve the library - --- # Summary diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 0fe231178..fc905754d 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,6 @@ --- name: Feature Request about: Request a new feature, or change an existing one - --- + ### Changed -* The `Bot.game` property can now be set to an arbitrary string. -* Discord mentions are handled in the old way again, after Discord reverted an API change. + +- The `Bot.game` property can now be set to an arbitrary string. +- Discord mentions are handled in the old way again, after Discord reverted an API change. ## [1.4.4] - 2015-12-18 + [1.4.4]: https://github.com/discordrb/discordrb/releases/tag/v1.4.4 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.4.3...v1.4.4) ### Added -* Add `Server.leave_server` as an alias for `delete_server` -* Use the new Discord mention format (mentions array). **Reverted in 1.4.5** -* Discord rate limited API calls are now handled correctly - discordrb will try again after the specified time. -* Debug logging is now handled by a separate `Logger` class + +- Add `Server.leave_server` as an alias for `delete_server` +- Use the new Discord mention format (mentions array). **Reverted in 1.4.5** +- Discord rate limited API calls are now handled correctly - discordrb will try again after the specified time. +- Debug logging is now handled by a separate `Logger` class ### Fixed -* Message timestamps are now parsed correctly. -* The quickadders for awaits (`User.await`, `Channel.await` etc.) now add the correct awaits. + +- Message timestamps are now parsed correctly. +- The quickadders for awaits (`User.await`, `Channel.await` etc.) now add the correct awaits. ## [1.4.3] - 2015-12-11 + [1.4.3]: https://github.com/discordrb/discordrb/releases/tag/v1.4.3 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.4.2...v1.4.3) ### Added -* Added a method `Bot.find_user` analogous to `Bot.find`. + +- Added a method `Bot.find_user` analogous to `Bot.find`. ### Fixed -* Remove a leftover debug line (#23, thanks @VxJasonxV) + +- Remove a leftover debug line (#23, thanks @VxJasonxV) ## [1.4.2] - 2015-12-10 + [1.4.2]: https://github.com/discordrb/discordrb/releases/tag/v1.4.2 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.4.1...v1.4.2) ### Changed -* discordrb will now send a user agent in the format requested by the Discord devs. + +- discordrb will now send a user agent in the format requested by the Discord devs. ## [1.4.1] - 2015-12-07 + [1.4.1]: https://github.com/discordrb/discordrb/releases/tag/v1.4.1 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.4.0...v1.4.1) ### Fixed -* Empty messages will now never be sent -* The command-not-found message in `CommandBot` can now be disabled properly + +- Empty messages will now never be sent +- The command-not-found message in `CommandBot` can now be disabled properly ## [1.4.0] - 2015-12-04 + [1.4.0]: https://github.com/discordrb/discordrb/releases/tag/v1.4.0 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.12...v1.4.0) ### Added -* All methods and classes where the words "colour" or "color" are used now have had aliases added with the respective other spelling. (Internally, everything uses "colour" now). -* discordrb now supports everything on the Discord API comparison, except for voice (see also #22) - * Roles can now be created, edited and deleted and their permissions modified. - * There is now a method to get a channel's message history. - * The bot's user profile can now be edited. - * Servers can now be created, edited and deleted. - * The user can now display a "typing" message in a channel. - * Invites can now be created and deleted, and an `Invite` class was made to represent them. -* Command and event handling is now threaded, with each command/event handler execution in a separate thread. -* Debug messages now specify the current thread's name. -* discordrb now handles created/updated/deleted servers properly with events added to handle them. -* The list of games handled by Discord will now be updated automatically. + +- All methods and classes where the words "colour" or "color" are used now have had aliases added with the respective other spelling. (Internally, everything uses "colour" now). +- discordrb now supports everything on the Discord API comparison, except for voice (see also #22) + - Roles can now be created, edited and deleted and their permissions modified. + - There is now a method to get a channel's message history. + - The bot's user profile can now be edited. + - Servers can now be created, edited and deleted. + - The user can now display a "typing" message in a channel. + - Invites can now be created and deleted, and an `Invite` class was made to represent them. +- Command and event handling is now threaded, with each command/event handler execution in a separate thread. +- Debug messages now specify the current thread's name. +- discordrb now handles created/updated/deleted servers properly with events added to handle them. +- The list of games handled by Discord will now be updated automatically. ### Fixed -* Fixed a bug where command handling would crash if the command didn't exist. + +- Fixed a bug where command handling would crash if the command didn't exist. ## [1.3.12] - 2015-11-30 + [1.3.12]: https://github.com/discordrb/discordrb/releases/tag/v1.3.12 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.11...v1.3.12) ### Added -* Add an attribute `Bot.should_parse_self` (false by default) that prevents the bot from raising an event if it receives a message from itself. -* `User.bot?` and `Message.from_bot?` were implemented to check whether the user is the bot or the message was sent by it. -* Add an event for private messages specifically (`Bot.pm` and `PrivateMessageEvent`) + +- Add an attribute `Bot.should_parse_self` (false by default) that prevents the bot from raising an event if it receives a message from itself. +- `User.bot?` and `Message.from_bot?` were implemented to check whether the user is the bot or the message was sent by it. +- Add an event for private messages specifically (`Bot.pm` and `PrivateMessageEvent`) ### Fixed -* Fix the `MessageEvent` attribute that checks whether the message is from the bot not working at all. + +- Fix the `MessageEvent` attribute that checks whether the message is from the bot not working at all. ## [1.3.11] - 2015-11-29 + [1.3.11]: https://github.com/discordrb/discordrb/releases/tag/v1.3.11 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.10...v1.3.11) ### Added -* Add a user selector (`:bot`) that is usable in the `from:` `MessageEvent` attribute to check whether the message was sent by a bot. + +- Add a user selector (`:bot`) that is usable in the `from:` `MessageEvent` attribute to check whether the message was sent by a bot. ### Fixed -* `Channel.private?` now checks for the server being nil instead of the `is_private` attribute provided by Discord as the latter is unreliable. (wtf) + +- `Channel.private?` now checks for the server being nil instead of the `is_private` attribute provided by Discord as the latter is unreliable. (wtf) ## [1.3.10] - 2015-11-28 + [1.3.10]: https://github.com/discordrb/discordrb/releases/tag/v1.3.10 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.9...v1.3.10) ### Added -* Add a method `Channel.private?` to check for a PM channel -* Add a `MessageEvent` attribute (`:private`) to check whether a message was sent in a PM channel -* Add various aliases to `MessageEvent` attributes -* Allow regexes to check for strings in `MessageEvent` attributes + +- Add a method `Channel.private?` to check for a PM channel +- Add a `MessageEvent` attribute (`:private`) to check whether a message was sent in a PM channel +- Add various aliases to `MessageEvent` attributes +- Allow regexes to check for strings in `MessageEvent` attributes ### Fixed -* The `matches_all` method would break in certain edge cases. This didn't really affect discordrb and I don't think anyone else uses that method (it's pretty useless otherwise). This has been fixed + +- The `matches_all` method would break in certain edge cases. This didn't really affect discordrb and I don't think anyone else uses that method (it's pretty useless otherwise). This has been fixed ## [1.3.9] - 2015-11-27 + [1.3.9]: https://github.com/discordrb/discordrb/releases/tag/v1.3.9 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.8...v1.3.9) ### Added -* Add awaits, a powerful way to add temporary event handlers. -* Add a `Bot.find` method to fuzzy-search for channels. -* Add methods to kick, ban and unban users. + +- Add awaits, a powerful way to add temporary event handlers. +- Add a `Bot.find` method to fuzzy-search for channels. +- Add methods to kick, ban and unban users. ### Fixed -* Permission overrides now work correctly for private channels (i. e. they don't exist at all) -* Users joining and leaving servers are now handled correctly. + +- Permission overrides now work correctly for private channels (i. e. they don't exist at all) +- Users joining and leaving servers are now handled correctly. ## [1.3.8] - 2015-11-12 + [1.3.8]: https://github.com/discordrb/discordrb/releases/tag/v1.3.8 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.7...v1.3.8) ### Added -* Added `Bot.users` and `Bot.servers` readers to get the list of users and servers. + +- Added `Bot.users` and `Bot.servers` readers to get the list of users and servers. ### Fixed -* POST requests to API calls that don't need a payload will now send a `nil` payload instead. This fixes the bot being unable to join any servers and various other latent problems. (#21, thanks @davidkus) + +- POST requests to API calls that don't need a payload will now send a `nil` payload instead. This fixes the bot being unable to join any servers and various other latent problems. (#21, thanks @davidkus) ## [1.3.7] - 2015-11-07 + [1.3.7]: https://github.com/discordrb/discordrb/releases/tag/v1.3.7 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.6...v1.3.7) ### Fixed -* Fix the command bot being included wrong, which caused crashes upon startup. + +- Fix the command bot being included wrong, which caused crashes upon startup. ## [1.3.6] - 2015-11-07 + [1.3.6]: https://github.com/discordrb/discordrb/releases/tag/v1.3.6 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.5...v1.3.6) ### Added -* The bot can now be stopped from the script using the new method `Bot.stop`. + +- The bot can now be stopped from the script using the new method `Bot.stop`. ### Fixed -* Fix some wrong file requires which caused crashes sometimes. + +- Fix some wrong file requires which caused crashes sometimes. ## [1.3.5] - 2015-11-07 + [1.3.5]: https://github.com/discordrb/discordrb/releases/tag/v1.3.5 [View diff for this release.](https://github.com/discordrb/discordrb/compare/v1.3.4...v1.3.5) ### Added -* The bot can now be run asynchronously using `Bot.run(:async)` to do further initialization after the bot was started. + +- The bot can now be run asynchronously using `Bot.run(:async)` to do further initialization after the bot was started. diff --git a/Gemfile b/Gemfile index 520803a5c..e0fbe4dd9 100644 --- a/Gemfile +++ b/Gemfile @@ -5,4 +5,3 @@ source 'https://rubygems.org' # Specify your gem's dependencies in discordrb.gemspec gemspec name: 'discordrb' gemspec name: 'discordrb-webhooks', development_group: 'webhooks' - diff --git a/LICENSE.txt b/LICENSE.txt index 91b1a60d8..ffc78d218 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2020 meew0 +Copyright (c) 2015-2024 meew0 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 527f1f393..2198ba85e 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,23 @@ +# discordrb + [![Gem](https://img.shields.io/gem/v/discordrb.svg)](https://rubygems.org/gems/discordrb) [![Gem](https://img.shields.io/gem/dt/discordrb.svg)](https://rubygems.org/gems/discordrb) [![CircleCI](https://circleci.com/gh/shardlab/discordrb.svg?style=svg)](https://circleci.com/gh/shardlab/discordrb) -[![Inline docs](http://inch-ci.org/github/shardlab/discordrb.svg?branch=main)](https://drb.shardlab.dev/v3.4.0/) +[![Inline docs](https://inch-ci.org/github/shardlab/discordrb.svg?branch=main)](https://drb.shardlab.dev/v3.4.0/) [![Join Discord](https://img.shields.io/badge/discord-join-7289DA.svg)](https://discord.gg/cyK3Hjm) -# discordrb An implementation of the [Discord](https://discord.com/) API using Ruby. ## Quick links to sections -* [Introduction](https://github.com/shardlab/discordrb#introduction) -* [Dependencies](https://github.com/shardlab/discordrb#dependencies) -* [Installation](https://github.com/shardlab/discordrb#installation) -* [Usage](https://github.com/shardlab/discordrb#usage) -* [Webhooks Client](https://github.com/shardlab/discordrb#webhooks-client) -* [Support](https://github.com/shardlab/discordrb#support) -* [Development](https://github.com/shardlab/discordrb#development), [Contributing](https://github.com/shardlab/discordrb#contributing) -* [License](https://github.com/shardlab/discordrb#license) +- [Introduction](https://github.com/shardlab/discordrb#introduction) +- [Dependencies](https://github.com/shardlab/discordrb#dependencies) +- [Installation](https://github.com/shardlab/discordrb#installation) +- [Usage](https://github.com/shardlab/discordrb#usage) +- [Webhooks Client](https://github.com/shardlab/discordrb#webhooks-client) +- [Support](https://github.com/shardlab/discordrb#support) +- [Development](https://github.com/shardlab/discordrb#development), [Contributing](https://github.com/shardlab/discordrb#contributing) +- [License](https://github.com/shardlab/discordrb#license) See also: [Documentation](https://drb.shardlab.dev/v3.4.0/), [Tutorials](https://github.com/shardlab/discordrb/wiki) @@ -43,15 +44,16 @@ If you enjoy using the library, consider getting involved with the community to ## Dependencies -* Ruby >= 2.6 supported +* Ruby >= 3.1 supported * An installed build system for native extensions (on Windows, make sure you download the "Ruby+Devkit" version of [RubyInstaller](https://rubyinstaller.org/downloads/)) ### Voice dependencies This section only applies to you if you want to use voice functionality. -* [libsodium](https://github.com/shardlab/discordrb/wiki/Installing-libsodium) -* A compiled libopus distribution for your system, anywhere the script can find it. See [here](https://github.com/shardlab/discordrb/wiki/Installing-libopus) for installation instructions. -* [FFmpeg](https://www.ffmpeg.org/download.html) installed and in your PATH + +- [libsodium](https://github.com/shardlab/discordrb/wiki/Installing-libsodium) +- A compiled libopus distribution for your system, anywhere the script can find it. See [here](https://github.com/shardlab/discordrb/wiki/Installing-libopus) for installation instructions. +- [FFmpeg](https://www.ffmpeg.org/download.html) installed and in your PATH ## Installation @@ -59,15 +61,19 @@ This section only applies to you if you want to use voice functionality. Using [Bundler](https://bundler.io/#getting-started), you can add discordrb to your Gemfile: - gem 'discordrb' +```ruby +gem 'discordrb' +``` And then install via `bundle install`. -Run the [ping example](https://github.com/shardlab/discordrb/blob/master/examples/ping.rb) to verify that the installation works (make sure to replace the token and client ID in there with your bots'!): +Run the [ping example](https://github.com/shardlab/discordrb/blob/main/examples/ping.rb) to verify that the installation works (make sure to replace the token and client ID in there with your bots'!): To run the bot while using bundler: - bundle exec ruby ping.rb +```sh +bundle exec ruby ping.rb +``` ### With Gem @@ -75,21 +81,27 @@ Alternatively, while Bundler is the recommended option, you can also install dis #### Linux / macOS - gem install discordrb +```sh +gem install discordrb +``` #### Windows > **Make sure you have the DevKit installed! See the [Dependencies](https://github.com/shardlab/discordrb#dependencies) section)** - gem install discordrb --platform=ruby +```sh +gem install discordrb --platform=ruby +``` To run the bot: - ruby ping.rb +```sh +ruby ping.rb +``` ### Installation Troubleshooting -See https://github.com/shardlab/discordrb/wiki/FAQ#installation for a list of common problems and solutions when installing `discordrb`. +See for a list of common problems and solutions when installing `discordrb`. ## Usage @@ -109,7 +121,7 @@ bot.run This bot responds to every "Ping!" with a "Pong!". -See [additional examples here](https://github.com/shardlab/discordrb/tree/master/examples). +See [additional examples here](https://github.com/shardlab/discordrb/tree/main/examples). You can find examples of projects that use discordrb by [searching for the discordrb topic on GitHub](https://github.com/topics/discordrb). @@ -153,7 +165,7 @@ If you need help or have a question, you can: ## Contributing Thank you for your interest in contributing! -Bug reports and pull requests are welcome on GitHub at https://github.com/shardlab/discordrb. +Bug reports and pull requests are welcome on GitHub at . In general, we recommend starting by discussing what you would like to contribute in the [Discord channel](https://discord.gg/cyK3Hjm). There are usually a handful of people working on things for the library, and what you're looking for may already be on the way. diff --git a/discordrb-webhooks.gemspec b/discordrb-webhooks.gemspec index 416643d36..bc162c233 100644 --- a/discordrb-webhooks.gemspec +++ b/discordrb-webhooks.gemspec @@ -20,8 +20,11 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'faraday', '~> 1.8' - spec.add_dependency 'faraday_middleware', '~> 1.1.0' + spec.add_dependency 'faraday', '~> 2.12.0' + spec.add_dependency 'faraday-multipart', '~> 1.0.4' - spec.required_ruby_version = '>= 2.6' + spec.required_ruby_version = '>= 3.1' + spec.metadata = { + 'rubygems_mfa_required' => 'true' + } end diff --git a/discordrb.gemspec b/discordrb.gemspec index bea53443c..d40cd1b0a 100644 --- a/discordrb.gemspec +++ b/discordrb.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |spec| spec.authors = %w[meew0 swarley] spec.email = [''] - spec.summary = 'Discord API for Ruby' + spec.summary = 'Discord API for Ruby.' spec.description = 'A Ruby implementation of the Discord (https://discord.com) API.' spec.homepage = 'https://github.com/shardlab/discordrb' spec.license = 'MIT' @@ -19,28 +19,34 @@ Gem::Specification.new do |spec| spec.bindir = 'exe' spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.metadata = { - 'changelog_uri' => 'https://github.com/shardlab/discordrb/blob/master/CHANGELOG.md' + 'bug_tracker_uri' => 'https://github.com/shardlab/discordrb/issues', + 'changelog_uri' => 'https://github.com/shardlab/discordrb/blob/main/CHANGELOG.md', + 'documentation_uri' => 'https://github.com/shardlab/discordrb/wiki', + 'source_code_uri' => 'https://github.com/shardlab/discordrb', + 'rubygems_mfa_required' => 'true' } spec.require_paths = ['lib'] + spec.add_dependency 'base64', '~> 0.2.0' + spec.add_dependency 'faraday', '~> 2.12.0' + spec.add_dependency 'faraday-multipart', '~> 1.0.4' spec.add_dependency 'ffi', '>= 1.9.24' spec.add_dependency 'opus-ruby' - spec.add_dependency 'faraday', '~> 1.8' - spec.add_dependency 'faraday_middleware', '~> 1.1.0' spec.add_dependency 'websocket-client-simple', '>= 0.3.0' - spec.add_dependency 'discordrb-webhooks', '~> 3.4.2' + spec.add_dependency 'discordrb-webhooks', '~> 4.0.0' - spec.required_ruby_version = '>= 2.6' + spec.required_ruby_version = '>= 3.1' spec.add_development_dependency 'bundler', '>= 1.10', '< 3' spec.add_development_dependency 'rake', '~> 13.0' - spec.add_development_dependency 'redcarpet', '~> 3.5.0' # YARD markdown formatting - spec.add_development_dependency 'rspec', '~> 3.10.0' - spec.add_development_dependency 'rspec-prof', '~> 0.0.7' - spec.add_development_dependency 'rubocop', '~> 1.21.0' + spec.add_development_dependency 'redcarpet', '~> 3.5' # YARD markdown formatting + spec.add_development_dependency 'rspec', '~> 3.0' + spec.add_development_dependency 'rspec_junit_formatter', '~> 0.5' + spec.add_development_dependency 'rspec-prof', '~> 0.0' + spec.add_development_dependency 'rubocop', '~> 1.0' spec.add_development_dependency 'rubocop-performance', '~> 1.0' - spec.add_development_dependency 'rubocop-rake', '~> 0.6.0' - spec.add_development_dependency 'simplecov', '~> 0.21.0' - spec.add_development_dependency 'yard', '~> 0.9.9' + spec.add_development_dependency 'rubocop-rake', '~> 0.6' + spec.add_development_dependency 'simplecov', '~> 0.21' + spec.add_development_dependency 'yard', '~> 0.9' end diff --git a/examples/modals.rb b/examples/modals.rb new file mode 100644 index 000000000..003d07394 --- /dev/null +++ b/examples/modals.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'discordrb' +require 'securerandom' + +bot = Discordrb::Bot.new(token: ENV.fetch('DISCORDRB_TOKEN')) +bot.register_application_command(:modal_test, 'Test out a spiffy modal', server_id: ENV.fetch('DISCORDRB_SERVER_ID')) +bot.register_application_command(:modal_await_test, 'Test out the await style', server_id: ENV.fetch('DISCORDRB_SERVER_ID')) + +bot.application_command :modal_test do |event| + event.show_modal(title: 'Test modal', custom_id: 'test1234') do |modal| + modal.row do |row| + row.text_input( + style: :paragraph, + custom_id: 'input', + label: 'Test input', + required: true, + placeholder: 'Type something to submit.' + ) + end + end +end + +bot.application_command :modal_await_test do |event| + id = SecureRandom.uuid + event.show_modal(title: "I'm waiting for you", custom_id: id) do |modal| + modal.row do |row| + row.text_input( + style: :paragraph, + custom_id: 'input', + label: 'Test input', + required: true, + placeholder: 'Type something to submit.' + ) + end + end + + start_time = Time.now + modal_event = bot.add_await!(Discordrb::Events::ModalSubmitEvent, custom_id: id, timeout: (60 * 10)) + + if event.nil? + modal_event.respond(content: "Time's up!", ephemeral: true) + else + modal_event.respond(content: "Thanks for submitting your modal. I waited #{Time.now - start_time} seconds") + end +end + +bot.modal_submit custom_id: 'test1234' do |event| + event.respond(content: "Thanks for submitting your modal. You sent #{event.value('input').chars.count} characters.") +end + +bot.run diff --git a/examples/ping.rb b/examples/ping.rb index ff4f3f1b0..4e75dad90 100644 --- a/examples/ping.rb +++ b/examples/ping.rb @@ -17,7 +17,7 @@ # Here we output the invite URL to the console so the bot account can be invited to the channel. This only has to be # done once, afterwards, you can remove this part if you want -puts "This bot's invite URL is #{bot.invite_url}." +puts "This bot's invite URL is #{bot.invite_url}" puts 'Click on it to invite it to your server.' # This method call adds an event handler that will be called on any message that exactly contains the string "Ping!". diff --git a/examples/select_menus.rb b/examples/select_menus.rb new file mode 100644 index 000000000..9ced90a3b --- /dev/null +++ b/examples/select_menus.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require 'discordrb' +require 'securerandom' + +bot = Discordrb::Bot.new(token: ENV.fetch('DISCORDRB_TOKEN')) + +bot.message do |event| + if event.message.content == 'TEST' + event.channel.send_message('Examples of different select menus') + + event.channel.send_message( + 'string_select (old select_menu, but alias define to keep legacy)', false, nil, nil, nil, nil, + Discordrb::Components::View.new do |builder| + builder.row do |r| + r.string_select(custom_id: 'string_select', placeholder: 'Test of StringSelect', max_values: 3) do |ss| + ss.option(label: 'Value 1', value: '1', description: 'First value', emoji: { name: '1ī¸âƒŖ' }) + ss.option(label: 'Value 2', value: '2', description: 'Second value', emoji: { name: '2ī¸âƒŖ' }) + ss.option(label: 'Value 3', value: '3', description: 'Third value', emoji: { name: '3ī¸âƒŖ' }) + end + # Same as above with the alias to keep the compatibility with the old method + # r.select_menu(custom_id: 'string_select', placeholder: 'Test of StringSelect', max_values: 3) do |ss| + # ss.option(label: 'Value 1', value: '1', description: 'First value', emoji: { name: '1ī¸âƒŖ' }) + # ss.option(label: 'Value 2', value: '2', description: 'Second value', emoji: { name: '2ī¸âƒŖ' }) + # ss.option(label: 'Value 3', value: '3', description: 'Third value', emoji: { name: '3ī¸âƒŖ' }) + # end + end + end + ) + + event.channel.send_message( + 'user_select', false, nil, nil, nil, nil, + Discordrb::Components::View.new do |builder| + builder.row do |r| + r.user_select(custom_id: 'user_select', placeholder: 'Test of UserSelect', max_values: 3, disabled: true) + end + end + ) + + event.channel.send_message( + 'user_select', false, nil, nil, nil, nil, + Discordrb::Components::View.new do |builder| + builder.row do |r| + r.user_select(custom_id: 'user_select', placeholder: 'Test of UserSelect', max_values: 3) + end + end + ) + + event.channel.send_message( + 'role_select', false, nil, nil, nil, nil, + Discordrb::Components::View.new do |builder| + builder.row do |r| + r.role_select(custom_id: 'role_select', placeholder: 'Test of RoleSelect', max_values: 3) + end + end + ) + + event.channel.send_message( + 'mentionable_select', false, nil, nil, nil, nil, + Discordrb::Components::View.new do |builder| + builder.row do |r| + r.mentionable_select(custom_id: 'mentionable_select', placeholder: 'Test of MentionableSelect', max_values: 3) + end + end + ) + + event.channel.send_message( + 'channel_select', false, nil, nil, nil, nil, + Discordrb::Components::View.new do |builder| + builder.row do |r| + r.channel_select(custom_id: 'channel_select', placeholder: 'Test of ChannelSelect', max_values: 3) + end + end + ) + end +end + +bot.string_select do |event| + # bot.select_menu do |event| # also available with the alias to keep the compatibility with the old method + event.interaction.respond( + content: "**[STRING_SELECT]**\nYou have chosen the values: **#{event.values.join('**, **')}**", + ephemeral: true + ) +end + +bot.user_select do |event| + event.interaction.respond( + content: "**[USER_SELECT]**\nYou have chosen users : **#{event.values.map(&:username).join('**, **')}**", + ephemeral: true + ) +end + +bot.role_select do |event| + event.interaction.respond( + content: "**[ROLE_SELECT]**\nYou have chosen roles : **#{event.values.map(&:name).join('**, **')}**", + ephemeral: true + ) +end + +bot.mentionable_select do |event| + event.interaction.respond( + content: "**[MENTIONABLE_SELECT]**\nYou have chosen mentionables :\n Users: **#{event.values[:users].map(&:username).join('**, **')}**\n Roles: **#{event.values[:roles].map(&:name).join('**, **')}**", + ephemeral: true + ) +end + +bot.channel_select do |event| + event.interaction.respond( + content: "**[CHANNEL_SELECT]**\nYou have chosen channels : **#{event.values.map(&:name).join('**, **')}**", + ephemeral: true + ) +end + +bot.run diff --git a/examples/slash_commands.rb b/examples/slash_commands.rb index c993b7970..001176484 100644 --- a/examples/slash_commands.rb +++ b/examples/slash_commands.rb @@ -2,19 +2,19 @@ require 'discordrb' -bot = Discordrb::Bot.new(token: ENV['SLASH_COMMAND_BOT_TOKEN'], intents: [:server_messages]) +bot = Discordrb::Bot.new(token: ENV.fetch('SLASH_COMMAND_BOT_TOKEN', nil), intents: [:server_messages]) -# We need to register our application comomands separately from the handlers with a special DSL. +# We need to register our application commands separately from the handlers with a special DSL. # This example uses server specific commands so that they appear immediately for testing, # but you can omit the server_id as well to register a global command that can take up to an hour # to appear. # # You may want to have a separate script for registering your commands so you don't need to do this every # time you start your bot. -bot.register_application_command(:example, 'Example commands', server_id: ENV['SLASH_COMMAND_BOT_SERVER_ID']) do |cmd| +bot.register_application_command(:example, 'Example commands', server_id: ENV.fetch('SLASH_COMMAND_BOT_SERVER_ID', nil), name_localizations: { 'de' => 'beispiel' }, description_localizations: { 'de' => 'Beispielbefehle' }) do |cmd| cmd.subcommand_group(:fun, 'Fun things!') do |group| group.subcommand('8ball', 'Shake the magic 8 ball') do |sub| - sub.string('question', 'Ask a question to receive wisdom', required: true) + sub.string('question', 'Ask a question to receive wisdom', required: true, name_localizations: { 'de' => 'frage' }, description_localizations: { 'de' => 'Stell eine Frage um Weisheit zu erlangen' }) end group.subcommand('java', 'What if it was java?') @@ -29,7 +29,7 @@ end end -bot.register_application_command(:spongecase, 'Are you mocking me?', server_id: ENV['SLASH_COMMAND_BOT_SERVER_ID']) do |cmd| +bot.register_application_command(:spongecase, 'Are you mocking me?', server_id: ENV.fetch('SLASH_COMMAND_BOT_SERVER_ID', nil)) do |cmd| cmd.string('message', 'Message to spongecase') cmd.boolean('with_picture', 'Show the mocking sponge?') end diff --git a/examples/voice_send.rb b/examples/voice_send.rb index 037561cf2..6514d1c48 100644 --- a/examples/voice_send.rb +++ b/examples/voice_send.rb @@ -8,7 +8,7 @@ require 'discordrb' -bot = Discordrb::Commands::CommandBot.new token: 'B0T.T0KEN.here', prefix: '!' +bot = Discordrb::Commands::CommandBot.new token: ENV.fetch('DISCORDRB_TOKEN'), prefix: '!' bot.command(:connect) do |event| # The `voice_channel` method returns the voice channel the user is currently in, or `nil` if the user is not in a diff --git a/examples/webhooks.rb b/examples/webhooks.rb new file mode 100644 index 000000000..fa4e5584b --- /dev/null +++ b/examples/webhooks.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'discordrb' +require 'securerandom' + +BASE64_SMALL_PICTURE = 'data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9bpaKtCnYQcchQnSyIijhqFYpQIdQKrTqYXPoFTRqSFBdHwbXg4Mdi1cHFWVcHV0EQ/ABxdHJSdJES/5cUWsR4cNyPd/ced+8Af73MVLNjHFA1y0gl4kImuyoEX9GDfoTRi4DETH1OFJPwHF/38PH1LsazvM/9OcJKzmSATyCeZbphEW8QT29aOud94ggrSgrxOfGYQRckfuS67PIb54LDfp4ZMdKpeeIIsVBoY7mNWdFQiaeIo4qqUb4/47LCeYuzWq6y5j35C0M5bWWZ6zSHkcAiliBCgIwqSijDQoxWjRQTKdqPe/iHHL9ILplcJTByLKACFZLjB/+D392a+ckJNykUBzpfbPtjBAjuAo2abX8f23bjBAg8A1day1+pAzOfpNdaWvQI6NsGLq5bmrwHXO4Ag0+6ZEiOFKDpz+eB9zP6piwwcAt0r7m9Nfdx+gCkqavkDXBwCIwWKHvd491d7b39e6bZ3w/9+3J4GwJBFwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+YLEA0OHzBTh5kAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAK0lEQVRIx+3NQQEAQAQAMC6DjLKJeSX4bQWW1ROXXhwTCAQCgUAgEAgEWz5KaQFiPn2UJwAAAABJRU5ErkJggg==' +CHANNEL_EDIT = ENV.fetch('CHANNEL_EDIT') + +bot = Discordrb::Bot.new(token: ENV.fetch('DISCORDRB_TOKEN')) + +bot.message do |event| + if event.message.content == 'CREATE' + event.channel.send_message('Create webhook in this channel') + + wh = event.channel.create_webhook('Test', nil, 'Creation webhook') + wh.execute(content: '[CREATE]') + end + + if event.message.content == 'EDIT_ONE' + wh = event.channel.webhooks.first + wh.update(name: 'Test edited', reason: 'Edition test one') + + puts wh.inspect + wh.execute(content: '[EDIT ONE]') + end + + if event.message.content == 'EDIT_TWO' + wh = event.channel.webhooks.first + wh.update(avatar: BASE64_SMALL_PICTURE, reason: 'Edition test two') + + puts wh.inspect + wh.execute(content: '[EDIT TWO]') + end + + if event.message.content == 'EDIT_THREE' + wh = event.channel.webhooks.first + wh.update(avatar: nil, channel: event.bot.channel(CHANNEL_EDIT, event.server) || event.channel, reason: 'Edition test three') + + puts wh.inspect + wh.execute(content: '[EDIT THREE]') + end + + if event.message.content == 'DELETE' + wh = (event.bot.channel(CHANNEL_EDIT, event.server) || event.channel).webhooks.first + wh.delete('Delete webhook') + + puts 'Deleted!' + end +end + +bot.run diff --git a/lib/discordrb.rb b/lib/discordrb.rb index 0714e7085..72487d081 100644 --- a/lib/discordrb.rb +++ b/lib/discordrb.rb @@ -10,7 +10,7 @@ module Discordrb Thread.current[:discordrb_name] = 'main' # The default debug logger used by discordrb. - LOGGER = Logger.new(ENV['DISCORDRB_FANCY_LOG']) + LOGGER = Logger.new(ENV.fetch('DISCORDRB_FANCY_LOG', false)) # The Unix timestamp Discord IDs are based on DISCORD_EPOCH = 1_420_070_400_000 @@ -32,7 +32,13 @@ module Discordrb server_message_typing: 1 << 11, direct_messages: 1 << 12, direct_message_reactions: 1 << 13, - direct_message_typing: 1 << 14 + direct_message_typing: 1 << 14, + server_message_content: 1 << 15, + server_scheduled_events: 1 << 16, + auto_moderation_configuration: 1 << 20, + auto_moderation_execution: 1 << 21, + server_message_polls: 1 << 24, + direct_message_polls: 1 << 25 }.freeze # All available intents @@ -40,7 +46,7 @@ module Discordrb # All unprivileged intents # @see https://discord.com/developers/docs/topics/gateway#privileged-intents - UNPRIVILEGED_INTENTS = ALL_INTENTS & ~(INTENTS[:server_members] | INTENTS[:server_presences]) + UNPRIVILEGED_INTENTS = ALL_INTENTS & ~(INTENTS[:server_members] | INTENTS[:server_presences] | INTENTS[:server_message_content]) # No intents NO_INTENTS = 0 @@ -117,29 +123,6 @@ def self.timestamp(time, style = nil) "" end end - - # TODO: This might belong somewhere else. - # Returns one of the "default" discord avatars from the CDN given a discriminator - def self.default_avatar(discrim = 0) - index = discrim.to_i % 5 - "#{Discordrb::API.cdn_url}/embed/avatars/#{index}.png" - end - - # TODO: This might belong somewhere else. - # Make an avatar URL from the user and avatar IDs - def self.avatar_url(user_id, avatar_id, format = nil) - format ||= if avatar_id.start_with?('a_') - 'gif' - else - 'webp' - end - "#{Discordrb::API.cdn_url}/avatars/#{user_id}/#{avatar_id}.#{format}" - end - - # Make a banner URL from server and banner IDs - def self.banner_url(server_id, banner_id, format = 'webp') - "#{cdn_url}/banners/#{server_id}/#{banner_id}.#{format}" - end end # In discordrb, Integer and {String} are monkey-patched to allow for easy resolution of IDs diff --git a/lib/discordrb/api.rb b/lib/discordrb/api.rb deleted file mode 100644 index d2bee19bf..000000000 --- a/lib/discordrb/api.rb +++ /dev/null @@ -1,349 +0,0 @@ -# frozen_string_literal: true - -require 'json' -require 'time' - -require 'discordrb/errors' - -# List of methods representing endpoints in Discord's API -module Discordrb::API - # The base URL of the Discord REST API. - APIBASE = 'https://discord.com/api/v8' - - # The URL of Discord's CDN - CDN_URL = 'https://cdn.discordapp.com' - - module_function - - # @return [String] the currently used API base URL. - def api_base - @api_base || APIBASE - end - - # Sets the API base URL to something. - def api_base=(value) - @api_base = value - end - - # @return [String] the currently used CDN url - def cdn_url - @cdn_url || CDN_URL - end - - # @return [String] the bot name, previously specified using {.bot_name=}. - def bot_name - @bot_name - end - - # Sets the bot name to something. Used in {.user_agent}. For the bot's username, see {Profile#username=}. - def bot_name=(value) - @bot_name = value - end - - # Changes the rate limit tracing behaviour. If rate limit tracing is on, a full backtrace will be logged on every RL - # hit. - # @param value [true, false] whether or not to enable rate limit tracing - def trace=(value) - @trace = value - end - - # Generate a user agent identifying this requester as discordrb. - def user_agent - # This particular string is required by the Discord devs. - required = "DiscordBot (https://github.com/shardlab/discordrb, v#{Discordrb::VERSION})" - @bot_name ||= '' - - "#{required} rest-client/#{RestClient::VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION}p#{RUBY_PATCHLEVEL} discordrb/#{Discordrb::VERSION} #{@bot_name}" - end - - # Resets all rate limit mutexes - def reset_mutexes - @mutexes = {} - @global_mutex = Mutex.new - end - - # Wait a specified amount of time synchronised with the specified mutex. - def sync_wait(time, mutex) - mutex.synchronize { sleep time } - end - - # Wait for a specified mutex to unlock and do nothing with it afterwards. - def mutex_wait(mutex) - mutex.lock - mutex.unlock - end - - # Performs a RestClient request. - # @param type [Symbol] The type of HTTP request to use. - # @param attributes [Array] The attributes for the request. - def raw_request(type, attributes) - RestClient.send(type, *attributes) - rescue RestClient::Forbidden => e - # HACK: for #request, dynamically inject restclient's response into NoPermission - this allows us to rate limit - noprm = Discordrb::Errors::NoPermission.new - noprm.define_singleton_method(:_rc_response) { e.response } - raise noprm, "The bot doesn't have the required permission to do this!" - rescue RestClient::BadGateway - Discordrb::LOGGER.warn('Got a 502 while sending a request! Not a big deal, retrying the request') - retry - end - - # Make an API request, including rate limit handling. - def request(key, major_parameter, type, *attributes) - # Add a custom user agent - attributes.last[:user_agent] = user_agent if attributes.last.is_a? Hash - - # The most recent Discord rate limit requirements require the support of major parameters, where a particular route - # and major parameter combination (*not* the HTTP method) uniquely identifies a RL bucket. - key = [key, major_parameter].freeze - - begin - mutex = @mutexes[key] ||= Mutex.new - - # Lock and unlock, i.e. wait for the mutex to unlock and don't do anything with it afterwards - mutex_wait(mutex) - - # If the global mutex happens to be locked right now, wait for that as well. - mutex_wait(@global_mutex) if @global_mutex.locked? - - response = nil - begin - response = raw_request(type, attributes) - rescue RestClient::Exception => e - response = e.response - - if response.body && !e.is_a?(RestClient::TooManyRequests) - data = JSON.parse(response.body) - err_klass = Discordrb::Errors.error_class_for(data['code'] || 0) - e = err_klass.new(data['message'], data['errors']) - - Discordrb::LOGGER.error(e.full_message) - end - - raise e - rescue Discordrb::Errors::NoPermission => e - if e.respond_to?(:_rc_response) - response = e._rc_response - else - Discordrb::LOGGER.warn("NoPermission doesn't respond_to? _rc_response!") - end - - raise e - ensure - if response - handle_preemptive_rl(response.headers, mutex, key) if response.headers[:x_ratelimit_remaining] == '0' && !mutex.locked? - else - Discordrb::LOGGER.ratelimit('Response was nil before trying to preemptively rate limit!') - end - end - rescue RestClient::TooManyRequests => e - # If the 429 is from the global RL, then we have to use the global mutex instead. - mutex = @global_mutex if e.response.headers[:x_ratelimit_global] == 'true' - - unless mutex.locked? - response = JSON.parse(e.response) - wait_seconds = response['retry_after'].to_i / 1000.0 - Discordrb::LOGGER.ratelimit("Locking RL mutex (key: #{key}) for #{wait_seconds} seconds due to Discord rate limiting") - trace("429 #{key.join(' ')}") - - # Wait the required time synchronized by the mutex (so other incoming requests have to wait) but only do it if - # the mutex isn't locked already so it will only ever wait once - sync_wait(wait_seconds, mutex) - end - - retry - end - - response - end - - # Handles pre-emptive rate limiting by waiting the given mutex by the difference of the Date header to the - # X-Ratelimit-Reset header, thus making sure we don't get 429'd in any subsequent requests. - def handle_preemptive_rl(headers, mutex, key) - Discordrb::LOGGER.ratelimit "RL bucket depletion detected! Date: #{headers[:date]} Reset: #{headers[:x_ratelimit_reset]}" - delta = headers[:x_ratelimit_reset_after].to_f - Discordrb::LOGGER.warn("Locking RL mutex (key: #{key}) for #{delta} seconds pre-emptively") - sync_wait(delta, mutex) - end - - # Perform rate limit tracing. All this method does is log the current backtrace to the console with the `:ratelimit` - # level. - # @param reason [String] the reason to include with the backtrace. - def trace(reason) - unless @trace - Discordrb::LOGGER.debug("trace was called with reason #{reason}, but tracing is not enabled") - return - end - - Discordrb::LOGGER.ratelimit("Trace (#{reason}):") - - caller.each do |str| - Discordrb::LOGGER.ratelimit(" #{str}") - end - end - - # Make an icon URL from server and icon IDs - def icon_url(server_id, icon_id, format = 'webp') - "#{cdn_url}/icons/#{server_id}/#{icon_id}.#{format}" - end - - # Make an icon URL from application and icon IDs - def app_icon_url(app_id, icon_id, format = 'webp') - "#{cdn_url}/app-icons/#{app_id}/#{icon_id}.#{format}" - end - - # Make a widget picture URL from server ID - def widget_url(server_id, style = 'shield') - "#{api_base}/guilds/#{server_id}/widget.png?style=#{style}" - end - - # Make a splash URL from server and splash IDs - def splash_url(server_id, splash_id, format = 'webp') - "#{cdn_url}/splashes/#{server_id}/#{splash_id}.#{format}" - end - - # Make a banner URL from server and banner IDs - def banner_url(server_id, banner_id, format = 'webp') - "#{cdn_url}/banners/#{server_id}/#{banner_id}.#{format}" - end - - # Make an emoji icon URL from emoji ID - def emoji_icon_url(emoji_id, format = 'webp') - "#{cdn_url}/emojis/#{emoji_id}.#{format}" - end - - # Make an asset URL from application and asset IDs - def asset_url(application_id, asset_id, format = 'webp') - "#{cdn_url}/app-assets/#{application_id}/#{asset_id}.#{format}" - end - - # Make an achievement icon URL from application ID, achievement ID, and icon hash - def achievement_icon_url(application_id, achievement_id, icon_hash, format = 'webp') - "#{cdn_url}/app-assets/#{application_id}/achievements/#{achievement_id}/icons/#{icon_hash}.#{format}" - end - - # Login to the server - def login(email, password) - request( - :auth_login, - nil, - :post, - "#{api_base}/auth/login", - email: email, - password: password - ) - end - - # Logout from the server - def logout(token) - request( - :auth_logout, - nil, - :post, - "#{api_base}/auth/logout", - nil, - Authorization: token - ) - end - - # Create an OAuth application - def create_oauth_application(token, name, redirect_uris) - request( - :oauth2_applications, - nil, - :post, - "#{api_base}/oauth2/applications", - { name: name, redirect_uris: redirect_uris }.to_json, - Authorization: token, - content_type: :json - ) - end - - # Change an OAuth application's properties - def update_oauth_application(token, name, redirect_uris, description = '', icon = nil) - request( - :oauth2_applications, - nil, - :put, - "#{api_base}/oauth2/applications", - { name: name, redirect_uris: redirect_uris, description: description, icon: icon }.to_json, - Authorization: token, - content_type: :json - ) - end - - # Get the bot's OAuth application's information - def oauth_application(token) - request( - :oauth2_applications_me, - nil, - :get, - "#{api_base}/oauth2/applications/@me", - Authorization: token - ) - end - - # Acknowledge that a message has been received - # The last acknowledged message will be sent in the ready packet, - # so this is an easy way to catch up on messages - def acknowledge_message(token, channel_id, message_id) - request( - :channels_cid_messages_mid_ack, - nil, # This endpoint is unavailable for bot accounts and thus isn't subject to its rate limit requirements. - :post, - "#{api_base}/channels/#{channel_id}/messages/#{message_id}/ack", - nil, - Authorization: token - ) - end - - # Get the gateway to be used - def gateway(token) - request( - :gateway, - nil, - :get, - "#{api_base}/gateway", - Authorization: token - ) - end - - # Get the gateway to be used, with additional information for sharding and - # session start limits - def gateway_bot(token) - request( - :gateway_bot, - nil, - :get, - "#{api_base}/gateway/bot", - Authorization: token - ) - end - - # Validate a token (this request will fail if the token is invalid) - def validate_token(token) - request( - :auth_login, - nil, - :post, - "#{api_base}/auth/login", - {}.to_json, - Authorization: token, - content_type: :json - ) - end - - # Get a list of available voice regions - def voice_regions(token) - request( - :voice_regions, - nil, - :get, - "#{api_base}/voice/regions", - Authorization: token, - content_type: :json - ) - end -end - -Discordrb::API.reset_mutexes diff --git a/lib/discordrb/api/client.rb b/lib/discordrb/api/client.rb index 4c6a3d4ea..13cb6bf45 100644 --- a/lib/discordrb/api/client.rb +++ b/lib/discordrb/api/client.rb @@ -1,15 +1,23 @@ # frozen_string_literal: true require 'faraday' -require 'faraday_middleware' +require 'faraday/multipart' require 'discordrb/api/endpoints/application_command' +require 'discordrb/api/endpoints/application' require 'discordrb/api/endpoints/audit_log' +require 'discordrb/api/endpoints/auto_moderation' require 'discordrb/api/endpoints/channel' require 'discordrb/api/endpoints/emoji' +require 'discordrb/api/endpoints/entitlement' +require 'discordrb/api/endpoints/guild_scheduled_event' require 'discordrb/api/endpoints/guild' require 'discordrb/api/endpoints/guild_template' require 'discordrb/api/endpoints/interaction' require 'discordrb/api/endpoints/invite' +require 'discordrb/api/endpoints/message' +require 'discordrb/api/endpoints/poll' +require 'discordrb/api/endpoints/sku' +require 'discordrb/api/endpoints/soundboard' require 'discordrb/api/endpoints/stage_instance' require 'discordrb/api/endpoints/sticker' require 'discordrb/api/endpoints/user' @@ -99,13 +107,21 @@ def update(data) # Client for making HTTP requests to the Discord API. class Client include ApplicationCommandEndpoints + include ApplicationEndpoints include AuditLogEndpoints + include AutoModerationEndpoints include ChannelEndpoints include EmojiEndpoints + include EntitlementEndpoints + include GuildScheduledEventEndpoints include GuildEndpoints include GuildTemplateEndpoints include InteractionEndpoints include InviteEndpoints + include MessageEndpoints + include PollEndpoints + include SkuEndpoints + include SoundboardEndpoints include StageInstanceEndpoints include StickerEndpoints include UserEndpoints @@ -113,7 +129,7 @@ class Client include WebhookEndpoints # The user agent used when making requests. - USER_AGENT = "DiscordBot (https://github.com/shardlab/discordrb, #{Discordrb::VERSION})" + USER_AGENT = "DiscordBot (https://github.com/shardlab/discordrb, #{Discordrb::VERSION})".freeze def initialize(token, version: 9) @conn = Faraday.new("https://discord.com/api/v#{version}") do |builder| @@ -136,6 +152,11 @@ def get_gateway_bot(**params) params: filter_undef(params) end + def get_gateway(**params) + request Route[:GET, '/gateway'], + params: filter_undef(params) + end + private # @param route [Route] diff --git a/lib/discordrb/api/endpoints/application.rb b/lib/discordrb/api/endpoints/application.rb new file mode 100644 index 000000000..c371373ce --- /dev/null +++ b/lib/discordrb/api/endpoints/application.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module Discordrb + module API + # @!discord_api https://discord.com/developers/docs/resources/application + module ApplicationEndpoints + # @!discord_api https://discord.com/developers/docs/resources/application#get-current-application + # @return [Hash] + def get_current_application(**params) + request Route[:GET, '/applications/@me'], params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/application#edit-current-application + # @param custom_install_url [String] Default custom authorization for the URL. + # @param description [String] Description of the app. + # @param role_connections_verification_url [String] Role connection verification URL for the app. + # @param install_params [Hash] Settings for the app's default in-app authorization link. + # @param integration_types_config [Hash] Hash containing supported install types. + # @param flags [Integer] Public flags for the app. + # @param icon [String, #read] A base64 encoded string with the image data. + # @param cover_image [String, #read] A base64 encoded string with the image data. + # @param interactions_endpoint_url [String] An endpoint an app can use to reccive interactions via the REST API. + # @param tags [Array] Tags that describe the functionality of the app. + # @return [Hash] + def edit_current_application(custom_install_url: :undef, description: :undef, + role_connections_verification_url: :undef, install_params: :undef, + integration_types_config: :undef, flags: :undef, icon: :undef, + cover_image: :undef, interactions_endpoint_url: :undef, tags: :undef, **rest) + data = { + custom_install_url: custom_install_url, + description: description, + role_connections_verification_url: role_connections_verification_url, + install_params: install_params, + integration_types_config: integration_types_config, + flags: flags, + icon: icon, + cover_image: cover_image, + interactions_endpoint_url: interactions_endpoint_url, + tags: tags, + **rest + } + + request Route[:PATCH, '/applications/@me'], + body: filter_undef(data) + end + + # @!discord_api https://discord.com/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @return [Hash] + def get_application_role_connection_metadata_records(application_id, **params) + request Route[:GET, "/applications/#{application_id}/role-connections/metadata"], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param application_role_connection_metadata [Hash] An application role connection metadata object. + # @return [Hash] + def update_application_role_connection_metadata_records(application_id, application_role_connection_metadata, **rest) + request Route[:PUT, "/applications/#{application_id}/role-connections/metadata"], + body: filter_undef({ application_role_connection_metadata: application_role_connection_metadata, **rest }) + end + end + end +end diff --git a/lib/discordrb/api/endpoints/application_command.rb b/lib/discordrb/api/endpoints/application_command.rb index 52556c2a1..bca86e953 100644 --- a/lib/discordrb/api/endpoints/application_command.rb +++ b/lib/discordrb/api/endpoints/application_command.rb @@ -12,21 +12,33 @@ def get_global_application_commands(application_id, **params) end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#create-global-application-command - # @param application_id [Integer, String] - # @param name [String] 1-32 character name - # @param description [String] 1-100 character description - # @param options [Array] the parameters for the command - # @param default_permission [true, false, nil] whether the command is enabled by default when the app is added to a guild - # @param type [1, 2, 3] the type of command, defaults `1` if not set. + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param name [String] 1-32 character name. + # @param description [String] 1-100 character description. + # @param options [Array] The parameters for the command. + # @param default_member_permissions [Integer] The bitwise permissions needed to use this command. + # @param type [1, 2, 3] The type of command, defaults `1` if not set. + # @param contexts [0, 1, 2] The contexts in which this command can be used. + # @param integration_types [0, 1] Supported installation contexts. + # @param nsfw [Boolean] Whether this command should be age-restricted. + # @param name_localizations [Hash] Localized name of the application command. + # @param description_localizations [Hash] Localized description of the application command. # @return [Hash] def create_global_application_command(application_id, name:, description:, options: :undef, - default_permission: :undef, type: :undef, **rest) + default_member_permissions: :undef, type: :undef, contexts: :undef, + integration_types: :undef, nsfw: :undef, name_localizations: :undef, + description_localizations: :undef, **rest) data = { name: name, description: description, options: options, - default_permission: default_permission, + default_member_permissions: default_member_permissions, type: type, + contexts: contexts, + integration_types: integration_types, + nsfw: nsfw, + name_localizations: name_localizations, + description_localizations: description_localizations, **rest } @@ -35,28 +47,40 @@ def create_global_application_command(application_id, name:, description:, optio end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#get-global-application-command - # @param application_id [Integer, String] - # @param command_id [Integer, String] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param command_id [Integer, String] An ID that uniquely identifies an application command. # @return [Hash] def get_global_application_command(application_id, command_id, **params) request Route[:GET, "/applications/#{application_id}/commands/#{command_id}"], params: params end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#edit-global-application-command - # @param application_id [Integer, String] - # @param command_id [Integer, String] - # @param name [String] 1-32 character name - # @param description [String] 1-100 character description - # @param options [Array] the parameters for the command - # @param default_permission [true, false, nil] whether the command is enabled by default when the app is added to a guild + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param command_id [Integer, String] An ID that uniquely identifies an application command. + # @param name [String] 1-32 character name. + # @param description [String] 1-100 character description. + # @param options [Array] The parameters for the command. + # @param default_member_permissions [Integer] The bitwise permissions needed to use this command. + # @param contexts [0, 1, 2] The contexts in which this command can be used. + # @param integration_types [0, 1] Supported installation contexts. + # @param nsfw [Boolean] Whether this command should be age-restricted. + # @param name_localizations [Hash] Localized name of the application command. + # @param description_localizations [Hash] Localized description of the application command. # @return [Hash] def edit_global_application_command(application_id, command_id, name: :undef, description: :undef, - options: :undef, default_permission: :undef, **rest) + options: :undef, default_member_permissions: :undef, contexts: :undef, + integration_types: :undef, nsfw: :undef, name_localizations: :undef, + description_localizations: :undef, **rest) data = { name: name, description: description, options: options, - default_permission: default_permission, + default_member_permissions: default_member_permissions, + contexts: contexts, + integration_types: integration_types, + nsfw: nsfw, + name_localizations: name_localizations, + description_localizations: description_localizations, **rest } @@ -65,16 +89,16 @@ def edit_global_application_command(application_id, command_id, name: :undef, de end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#delete-global-application-command - # @param application_id [Integer, String] - # @param command_id [Integer, String] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param command_id [Integer, String] An ID that uniquely identifies an application command. # @return [Hash] def delete_global_application_command(application_id, command_id) request Route[:DELETE, "/applications/#{application_id}/commands/#{command_id}"] end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands - # @param application_id [Integer, String] - # @param commands [Array] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param commands [Array] An array of application command objects. # @return [Array] def bulk_overwrite_global_application_commands(application_id, commands) request Route[:PUT, "/applications/#{application_id}/commands"], @@ -82,8 +106,8 @@ def bulk_overwrite_global_application_commands(application_id, commands) end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands - # @param application_id [Integer, String] - # @param guild_id [Integer, String] + # @param application_id [Integer, String] An ID that uniquely identifies an application command. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def get_guild_application_commands(application_id, guild_id, **params) request Route[:GET, "/applications/#{application_id}/guilds/#{guild_id}/commands"], @@ -91,22 +115,29 @@ def get_guild_application_commands(application_id, guild_id, **params) end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command - # @param application_id [Integer, String] - # @param guild_id [Integer, String] - # @param name [String] 1-32 character name - # @param description [String] 1-100 character description - # @param options [Array, nil] the parameters for the command - # @param default_permission [true, false, nil] whether the command is enabled by default when the app is added to a guild - # @param type [1, 2, 3] the type of command, defaults 1 if not set + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param name [String] 1-32 character name. + # @param description [String] 1-100 character description. + # @param options [Array, nil] The parameters for the command. + # @param default_member_permissions [Integer] The bitwise permissions needed to use this command. + # @param type [Integer] The type of command, defaults 1 if not set. + # @param nsfw [Boolean] Whether this command should be age-restricted. + # @param name_localizations [Hash] Localized name of the application command. + # @param description_localizations [Hash] Localized description of the application command. # @return [Hash] def create_guild_application_command(application_id, guild_id, name:, description:, options: :undef, - default_permission: :undef, type: :undef, **rest) + default_member_permissions: :undef, type: :undef, nsfw: :undef, + name_localizations: :undef, description_localizations: :undef, **rest) data = { name: name, description: description, options: options, - default_permission: default_permission, + default_member_permissions: default_member_permissions, type: type, + nsfw: nsfw, + name_localizations: name_localizations, + description_localizations: description_localizations, **rest } @@ -115,30 +146,37 @@ def create_guild_application_command(application_id, guild_id, name:, descriptio end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command - # @param application_id [Integer, String] - # @param guild_id [Integer, String] - # @param command_id [Integer, String] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param command_id [Integer, String] An ID that uniquely identifies an application command. # @return [Hash] def get_guild_application_command(application_id, guild_id, command_id, **params) request Route[:GET, "/applications/#{application_id}/guilds/#{guild_id}/commands/#{command_id}"], params: params end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command - # @param application_id [Integer, String] - # @param guild_id [Integer, String] - # @param command_id [Integer, String] - # @param name [String] 1-32 character name - # @param description [String] 1-100 character description - # @param options [Array] the parameters for the command - # @param default_permission [true, false, nil] whether the command is enabled by default when the app is added to a guild + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param command_id [Integer, String] An ID that uniquely identifies an application command. + # @param name [String] 1-32 character name. + # @param description [String] 1-100 character description. + # @param options [Array] The parameters for the command. + # @param default_member_permissions [Integer] The bitwise permissions needed to use this command. + # @param nsfw [Boolean] Whether this command should be age-restricted. + # @param name_localizations [Hash] Localized name of the application command. + # @param description_localizations [Hash] Localized description of the application command. # @return [Hash] def edit_guild_application_command(application_id, guild_id, command_id, name: :undef, description: :undef, - options: :undef, default_permission: :undef, **rest) + options: :undef, default_member_permissions: :undef, nsfw: :undef, + name_localizations: :undef, description_localizations: :undef, **rest) data = { name: name, description: description, options: options, - default_permission: default_permission, + default_member_permissions: default_member_permissions, + nsfw: nsfw, + name_localizations: name_localizations, + description_localizations: description_localizations, **rest } @@ -147,18 +185,18 @@ def edit_guild_application_command(application_id, guild_id, command_id, name: : end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#delete-guild-application-command - # @param application_id [Integer, String] - # @param guild_id [Integer, String] - # @param command_id [Integer, String] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param command_id [Integer, String] An ID that uniquely identifies an application commmand. # @return [Hash] def delete_guild_application_command(application_id, guild_id, command_id) request Route[:DELETE, "/applications/#{application_id}/guilds/#{guild_id}/commands/#{command_id}"] end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands - # @param application_id [Integer, String] - # @param guild_id [Integer, String] - # @param commands [Array] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param commands [Array] An array of application commmand objects. # @return [Array] def bulk_overwrite_guild_application_commands(application_id, guild_id, commands) request Route[:PUT, "/applications/#{application_id}/guilds/#{guild_id}/commands"], @@ -166,8 +204,8 @@ def bulk_overwrite_guild_application_commands(application_id, guild_id, commands end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command-permissions - # @param application_id [Integer, String] - # @param guild_id [Integer, String] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array] def get_guild_application_command_permissions(application_id, guild_id, **params) request Route[:GET, "/applications/#{application_id}/guilds/#{guild_id}/commands/permissions"], @@ -175,9 +213,9 @@ def get_guild_application_command_permissions(application_id, guild_id, **params end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions - # @param application_id [Integer, String] - # @param guild_id [Integer, String] - # @param command_id [Integer, String] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param command_id [Integer, String] An ID that uniquely identifies an application command. # @return [Hash] def get_application_command_permissions(application_id, guild_id, command_id, **params) request Route[:GET, "/applications/#{application_id}/guilds/#{guild_id}/commands/#{command_id}/permissions"], @@ -185,24 +223,15 @@ def get_application_command_permissions(application_id, guild_id, command_id, ** end # @!discord_api https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions - # @param application_id [Integer, String] - # @param guild_id [Integer, String] - # @param command_id [Integer, String] - # @param permissions [Array] + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param command_id [Integer, String] An ID that uniquely identifies an application command. + # @param permissions [Array] An array of application command permissions objects. # @return [Hash] def edit_application_command_permissions(application_id, guild_id, command_id, permissions: [], **rest) request Route[:PUT, "/applications/#{application_id}/guilds/#{guild_id}/commands/#{command_id}/permissions"], body: filter_undef({ permissions: permissions, **rest }) end - - # @!discord_api https://discord.com/developers/docs/interactions/application-commands#batch-edit-application-command-permissions - # @param application_id [Integer, String] - # @param guild_id [Integer, String] - # @param permissions [Array] - def batch_edit_application_command_permissions(application_id, guild_id, permissions) - request Route[:PUT, "/applications/#{application_id}/guilds/#{guild_id}/commands/permissions"], - body: permissions - end end end end diff --git a/lib/discordrb/api/endpoints/audit_log.rb b/lib/discordrb/api/endpoints/audit_log.rb index 862d3eab5..e93485d25 100644 --- a/lib/discordrb/api/endpoints/audit_log.rb +++ b/lib/discordrb/api/endpoints/audit_log.rb @@ -2,13 +2,23 @@ module Discordrb module API + # @!discord_api https://discord.com/developers/docs/resources/audit-log module AuditLogEndpoints - def get_guild_audit_log(guild_id, user_id: :undef, action_type: :undef, before: :undef, limit: :undef, **params) + # @!discord_api https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] Entries from a specifc user. + # @param action_type [Integer] Entries for a specific type of audit log action. + # @param before [Integer, String] Entries with an ID less than a specific audit log entry ID. + # @param after [Integer, String] Entries with an ID greater than a specific audit log entry ID. + # @param limit [Integer] Maximum number of entries to return; default is 50. + # @return [Array>] + def get_guild_audit_log(guild_id, user_id: :undef, action_type: :undef, before: :undef, limit: :undef, after: :undef, **params) query = { user_id: user_id, action_type: action_type, before: before, limit: limit, + after: after, **params } diff --git a/lib/discordrb/api/endpoints/auto_moderation.rb b/lib/discordrb/api/endpoints/auto_moderation.rb new file mode 100644 index 000000000..c88ecbb9d --- /dev/null +++ b/lib/discordrb/api/endpoints/auto_moderation.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +module Discordrb + module API + # @!discord_api https://discord.com/developers/docs/resources/auto-moderation + module AutoModerationEndpoints + # @!discord_api https://discord.com/developers/docs/resources/auto-moderation#list-auto-moderation-rules-for-guild + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @return [Array>] + def list_auto_moderation_rules_for_guild(guild_id, **params) + request Route[:GET, "/guilds/#{guild_id}/auto-moderation/rules", guild_id], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/auto-moderation#get-auto-moderation-rule + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param auto_moderation_rule_id [Integer, String] An ID that uniquely identifies an auto-moderation rule. + # @return [Hash] + def get_auto_moderation_rule(guild_id, auto_moderation_rule_id, **params) + request Route[:GET, "/guilds/#{guild_id}/auto-moderation/rules/#{auto_moderation_rule_id}", guild_id], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/auto-moderation#create-auto-moderation-rule + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param name [String] Name of the rule. + # @param event_type [1, 2] The context this rule applies to. + # @param trigger_type [Integer] The type of action that can trigger this rule. + # @param trigger_metadata [Hash] Extra data used to determine when a rule should should be triggered. + # @param actions [Array] The action to perform when the rule is triggered. + # @param enabled [Boolean] Whether this rule is enabled or not. + # @param exempt_roles [Array] ID of roles that should not be affected by this rule. + # @param exempt_channels [Array] ID of channels that should not be affected by this rule. + # @param reason [String] The reason for creating this rule. + # @return [Hash] + def create_auto_moderation_rule(guild_id, name:, event_type:, trigger_type:, actions:, trigger_metadata: :undef, + enabled: :undef, exempt_roles: :undef, exempt_channels: :undef, reason: :undef, + **rest) + data = { + name: name, + event_type: event_type, + trigger_type: trigger_type, + trigger_metadata: trigger_metadata, + actions: actions, + enabled: enabled, + exempt_roles: exempt_roles, + exempt_channels: exempt_channels, + **rest + } + + request Route[:POST, "/guilds/#{guild_id}/auto-moderation/rules"], + body: filter_undef(data), + reason: reason + end + + # @!discord_api https://discord.com/developers/docs/resources/auto-moderation#modify-auto-moderation-rule + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param auto_moderation_rule_id [Integer, String] An ID that uniquely identifies an auto-moderation rule. + # @param name [String] Name of the rule. + # @param event_type [1, 2] The context this rule applies to. + # @param trigger_metadata [Hash] Extra data used to determine when a rule should should be triggered. + # @param actions [Array] The action to perform when the rule is triggered. + # @param enabled [Boolean] Whether this rule is enabled or not. + # @param exempt_roles [Array] ID of roles that should not be affected by this rule. + # @param exempt_channels [Array] ID of channels that should not be affected by this rule. + # @param reason [String] The reason for editing this rule. + # @return [Hash] + def modify_auto_moderation_rule(guild_id, auto_moderation_rule_id, name: :undef, event_type: :undef, trigger_metadata: :undef, + actions: :undef, enabled: :undef, exempt_roles: :undef, exempt_channels: :undef, reason: :undef, + **rest) + data = { + name: name, + event_type: event_type, + trigger_metadata: trigger_metadata, + actions: actions, + enabled: enabled, + exempt_roles: exempt_roles, + exempt_channels: exempt_channels, + **rest + } + + request Route[:PATCH, "/guilds/#{guild_id}/auto-moderation/rules/#{auto_moderation_rule_id}"], + body: filter_undef(data), + reason: reason + end + + # @!discord_api https://discord.com/developers/docs/resources/auto-moderation#delete-auto-moderation-rule + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param auto_moderation_rule_id [Integer, String] An ID that uniquely identifies an auto-moderation rule. + # @param reason [String] The reason for deleting this rule. + # @return [nil] + def delete_auto_moderation_rule(guild_id, auto_moderation_rule_id, reason: :undef) + request Route[:DELETE, "/guilds/#{guild_id}/auto-moderation/rules/#{auto_moderation_rule_id}"], + reason: reason + end + end + end +end diff --git a/lib/discordrb/api/endpoints/channel.rb b/lib/discordrb/api/endpoints/channel.rb index deafc0613..a9a223887 100644 --- a/lib/discordrb/api/endpoints/channel.rb +++ b/lib/discordrb/api/endpoints/channel.rb @@ -5,6 +5,7 @@ module API # @!discord_api https://discord.com/developers/docs/resources/channel module ChannelEndpoints # @!discord_api https://discord.com/developers/docs/resources/channel#get-channel + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. # @return [Hash] def get_channel(channel_id, **params) request Route[:GET, "/channels/#{channel_id}", channel_id], @@ -12,12 +13,36 @@ def get_channel(channel_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/channel#modify-channel + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param name [String] 1-100 character name. + # @param icon [File, #Read] A base64 encoded string with the image data. + # @param type [Integer] Type of channel. + # @param position [Integer] Position of the channel in the channel list. + # @param topic [String] 0-1024 character topic. + # @param nsfw [Boolean] If this channel is age-restricted or not. + # @param rate_limit_per_user [Integer] The wait between sending messages (0-21600). + # @param bitrate [Intger] the bitrate of the voice or stage channel. + # @param user_limit [Intger] The max amount of users that can be in this voice or stage channel. + # @param permission_overwrites [Array] Array of permission overwrite objects for the channel or category. + # @param parent_id [Integer, String] ID of the new parent category for this channel. + # @param rtc_region [String] Channel voice region. Defaults to auto when null. + # @param video_quality_mode [Integer] Camera quality mode in voice channels. + # @param default_auto_archive_duration [Integer] Default client duration to auto-archive new threads. + # @param flags [Integer] Bitfield value of channel flags to set. + # @param available_tags [Array] Avalibile tags for posts in GUILD_FORUM channels. + # @param default_reaction_emoji [Hash] Emoji to show for reactions on posts in GUILD_FORUM channels. + # @param default_thread_rate_limit_per_user [Integer] Inital rate-limit-per-user for new threads. + # @param default_sort_order [Integer] Default sort order for GUILD_FORUM channels. + # @param default_forum_layout [Integer] Default forum layout for GUILD_FORUM channels. + # @param reason [String] The reason for modifiying this channel. # @return [Hash] def modify_channel(channel_id, name: :undef, icon: :undef, type: :undef, position: :undef, topic: :undef, nsfw: :undef, rate_limit_per_user: :undef, bitrate: :undef, user_limit: :undef, permission_overwrites: :undef, parent_id: :undef, rtc_region: :undef, - video_quality_mode: :undef, default_auto_archive_duration: :undef, reason: :undef, **rest) + video_quality_mode: :undef, default_auto_archive_duration: :undef, flags: :undef, + available_tags: :undef, default_reaction_emoji: :undef, default_thread_rate_limit_per_user: :undef, + default_sort_order: :undef, default_forum_layout: :undef, reason: :undef, **rest) data = { name: name, icon: icon, @@ -33,6 +58,12 @@ def modify_channel(channel_id, rtc_region: rtc_region, video_quality_mode: video_quality_mode, default_auto_archive_duration: default_auto_archive_duration, + flags: flags, + available_tags: available_tags, + default_reaction_emoji: default_reaction_emoji, + default_thread_rate_limit_per_user: default_thread_rate_limit_per_user, + default_sort_order: default_sort_order, + default_forum_layout: default_forum_layout, **rest } @@ -42,141 +73,20 @@ def modify_channel(channel_id, end # @!discord_api https://discord.com/developers/docs/resources/channel#deleteclose-channel + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param reason [String] Reason for deleting this channel. # @return [Hash] def delete_channel(channel_id, reason: :undef) request Route[:DELETE, "/channels/#{channel_id}", channel_id], reason: reason end - # @!discord_api https://discord.com/developers/docs/resources/channel#get-channel-messages - # @return [Array>] - def get_channel_messages(channel_id, around: :undef, before: :undef, after: :undef, limit: :undef, **params) - request Route[:GET, "/channels/#{channel_id}/messages", channel_id], - params: filter_undef({ around: around, before: before, after: after, limit: limit, **params }) - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#get-channel-message - # @return [Hash] - def get_channel_message(channel_id, message_id, **params) - request Route[:GET, "/channels/#{channel_id}/message/#{message_id}", channel_id], - params: params - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#create-message - # @return [Hash] - def create_message(channel_id, - content: :undef, tts: :undef, files: :undef, embeds: :undef, allowed_mentions: :undef, - message_reference: :undef, components: :undef, sticker_ids: :undef, **rest) - body = filter_undef({ - content: content, - tts: tts, - embeds: embeds, - allowed_mentions: allowed_mentions, - message_reference: message_reference, - components: components, - sticker_ids: sticker_ids, - **rest - }) - - if files - files = files.zip(0...files.count).map { |file, index| ["file[#{index}]", file] }.to_h - body = { **files, payload_json: JSON.dump(body) } - end - - request Route[:POST, "/channels/#{channel_id}/messages", channel_id], - body: body - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#crosspost-message - # @return [Hash] - def crosspost_message(channel_id, message_id, **rest) - request Route[:POST, "/channels/#{channel_id}/messages/#{message_id}/crosspost", channel_id], - body: rest - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#create-reaction - # @return [nil] - def create_reaction(channel_id, message_id, emoji) - request Route[:PUT, "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/@me", channel_id], - body: '' - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#delete-own-reaction - # @return [nil] - def delete_own_reaction(channel_id, message_id, emoji) - request Route[:DELETE, "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/@me", channel_id] - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#delete-user-reaction - # @return [nil] - def delete_user_reaction(channel_id, message_id, emoji, user_id) - request Route[:DELETE, - "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/#{user_id}", - channel_id] - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#get-reactions - # @return [Array>] - def get_reactions(channel_id, message_id, emoji, after: :undef, limit: :undef, **params) - query = { - after: after, - limit: limit, - **params - } - - request Route[:GET, "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}", channel_id], - params: filter_undef(query) - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#delete-all-reactions - # @return [nil] - def delete_all_reactions(channel_id, message_id) - request Route[:DELETE, "/channels/#{channel_id}/messages/#{message_id}/reactions", channel_id] - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji - # @return [nil] - def delete_all_reactions_for_emoji(channel_id, message_id, emoji) - request Route[:DELETE, "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}", channel_id] - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#edit-message - # @return [Hash] - def edit_message(channel_id, - message_id, content: :undef, embeds: :undef, flags: :undef, file: :undef, - allowed_mentions: :undef, attachments: :undef, components: :undef, **rest) - body = filter_undef({ - content: content, - tts: tts, - embeds: embeds, - allowed_mentions: allowed_mentions, - message_reference: message_reference, - components: components, - **rest - }) - - body = { file: file, payload_json: JSON.dump(body) } if file - - request Route[:PATCH, "/channels/#{channel_id}/messages/#{message_id}", channel_id], - body: body - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#delete-message - # @return [nil] - def delete_message(channel_id, message_id, reason: :undef) - request Route[:DELETE, "/channels/#{channel_id}/messages/#{message_id}"], - reason: reason - end - - # @!discord_api https://discord.com/developers/docs/resources/channel#bulk-delete-messages - # @return [nil] - def bulk_delete_messages(channel_id, messages, reason: :undef) - request Route[:POST, "/channels/#{channel_id}/messages/bulk-delete", channel_id], - body: messages, - reason: reason - end - # @!discord_api https://discord.com/developers/docs/resources/channel#edit-channel-permissions + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param allow [String] Bitwise value of allowed permissions. Defaults to "0". + # @param deny [String] Bitwise value of denied permissions. Defaults to "0". + # @param type [Integer] 0 for a role, and 1 for a member. + # @param reason [String] The reason for updating this channel's permissions. # @return [nil] def edit_channel_permissions(channel_id, overwrite_id, allow: :undef, deny: :undef, type: :undef, reason: :undef, **rest) @@ -186,6 +96,7 @@ def edit_channel_permissions(channel_id, overwrite_id, end # @!discord_api https://discord.com/developers/docs/resources/channel#get-channel-invites + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. # @return [Array>] def get_channel_invites(channel_id, **params) request Route[:GET, "/channels/#{channel_id}/invites", channel_id], @@ -193,6 +104,15 @@ def get_channel_invites(channel_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/channel#create-channel-invite + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param max_age [Integer] How long this invite lasts before expiring in seconds. + # @param max_uses [Integer] Max number of times this invite can be used; 0-100. + # @param temporary [Boolean] If this invite only grants temporary membership. + # @param unique [Boolean] Whether to avoid using a similar type of invite. + # @param target_type [Integer] Type of target for voice channels. + # @param target_user_id [Integer, String] ID of the user's stream to display. + # @param target_application_id [Integer, String] ID of the embedded application to open. + # @param reason [String] The reason for making this invite. # @return [Hash] def create_channel_invite(channel_id, max_age: :undef, max_uses: :undef, temporary: :undef, unique: :undef, @@ -215,20 +135,26 @@ def create_channel_invite(channel_id, end # @!discord_api https://discord.com/developers/docs/resources/channel#delete-channel-permission + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param overwrite_id [Integer, String] An ID that uniquely identifies a channel overwrite. + # @param reason [String] The reason for deleting this permission overwrite. # @return [nil] def delete_channel_permission(channel_id, overwrite_id, reason: :undef) request Route[:DELETE, "/channels/#{channel_id}/permissions/#{overwrite_id}", channel_id], reason: reason end - # @!discord_api https://discord.com/developers/docs/resources/channel#follow-news-channel + # @!discord_api https://discord.com/developers/docs/resources/channel#follow-announcement-channel + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param webhook_channel_id [Integer, String] ID of the channel to target. # @return [Hash] - def follow_news_channel(channel_id, webhook_channel_id:) + def follow_announcement_channel(channel_id, webhook_channel_id:) request Route[:POST, "/channels/#{channel_id}/followers", channel_id], body: { webhook_channel_id: webhook_channel_id } end # @!discord_api https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. # @return [nil] def trigger_typing_indicator(channel_id, **rest) request Route[:POST, "/channels/#{channel_id}/typing", channel_id], @@ -236,6 +162,7 @@ def trigger_typing_indicator(channel_id, **rest) end # @!discord_api https://discord.com/developers/docs/resources/channel#get-pinned-messages + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. # @return [Array>] def get_pinned_messages(channel_id, **params) request Route[:GET, "/channels/#{channel_id}/pins", channel_id], @@ -243,6 +170,9 @@ def get_pinned_messages(channel_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/channel#pin-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param reason [String] The reason for pinning this message. # @return [nil] def pin_message(channel_id, message_id, reason: :undef) request Route[:PUT, "/channels/#{channel_id}/pins/#{message_id}", channel_id], @@ -251,6 +181,9 @@ def pin_message(channel_id, message_id, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/channel#unpin-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param reason [String] The reason for un-pinning this message. # @return [nil] def unpin_message(channel_id, message_id, reason: :undef) request Route[:DELETE, "/channels/#{channel_id}/pins/#{message_id}", channel_id], @@ -258,12 +191,19 @@ def unpin_message(channel_id, message_id, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/channel#start-thread-with-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param name [String] 1-100 character name. + # @param auto_archive_duration [Integer] The thread won't show in the channel list once this duration is reached. + # @param rate_limit_per_user [Integer] Slowmode, or amount of seconds a user has to wait between messages. + # @param reason [String] The reason for starting this thread. # @return [Hash] def start_thread_with_message(channel_id, message_id, - name:, auto_archive_duration: :undef, reason: :undef, **rest) + name:, auto_archive_duration: :undef, rate_limit_per_user: :undef, + reason: :undef, **rest) data = { name: name, auto_archive_duration: auto_archive_duration, + rate_limit_per_user: rate_limit_per_user, **rest } @@ -273,16 +213,24 @@ def start_thread_with_message(channel_id, message_id, end # @!discord_api https://discord.com/developers/docs/resources/channel#start-thread-without-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param name [String] 1-100 character name. + # @param auto_archive_duration [Integer] The thread won't show in the channel list once this duration is reached. + # @param type [Integer] Type of thread to make. + # @param invitable [Boolean] If non-moderators can add other non-moderators to this thread. + # @param rate_limit_per_user [Integer] Slowmode, or amount of seconds a user has to wait between messages. + # @param reason [String] The reason for starting this thread. # @return [Hash] def start_thread_without_message(channel_id, name:, auto_archive_duration: :undef, type: :undef, invitable: :undef, - reason: :undef, **rest) + rate_limit_per_user: :undef, reason: :undef, **rest) data = { name: name, auto_archive_duration: auto_archive_duration, type: type, invitable: invitable, - **reason + rate_limit_per_user: rate_limit_per_user, + **rest } request Route[:POST, "/channels/#{channel_id}/threads", channel_id], @@ -290,7 +238,42 @@ def start_thread_without_message(channel_id, reason: reason end + # rubocop:disable Style/MapToHash + # @!discord_api https://discord.com/developers/docs/resources/channel#start-thread-in-forum-or-media-channel + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param name [String] 1-100 character name. + # @param auto_archive_duration [Integer] The thread won't show in the channel list once this duration is reached. + # @param message [Hash] The first message in a forum or media channel. + # @param files [File] File contents being sent. + # @param applied_tags [Array] ID's of tags to apply. + # @param rate_limit_per_user [Integer] Slowmode, or amount of seconds a user has to wait between messages. + # @param reason [String] The reason for starting this thread in forum or media channel. + # @return [Hash] + def start_thread_in_forum_or_media_channel(channel_id, + name:, message:, auto_archive_duration: :undef, applied_tags: :undef, + files: :undef, rate_limit_per_user: :undef, reason: :undef, **rest) + body = filter_undef({ + name: name, + auto_archive_duration: auto_archive_duration, + message: message, + applied_tags: applied_tags, + rate_limit_per_user: rate_limit_per_user, + **rest + }) + + if files + files = files.zip(0...files.count).map { |file, index| ["file[#{index}]", file] }.to_h + body = { **files, payload_json: JSON.dump(body) } + end + + request Route[:POST, "/channels/#{channel_id}/threads", channel_id], + body: body, + reason: reason + end + # rubocop:enable Style/MapToHash + # @!discord_api https://discord.com/developers/docs/resources/channel#join-thread + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. # @return [nil] def join_thread(channel_id) request Route[:PUT, "/channels/#{channel_id}/thread-members/@me", channel_id], @@ -298,37 +281,53 @@ def join_thread(channel_id) end # @!discord_api https://discord.com/developers/docs/resources/channel#add-thread-member + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param user_id [Integer, String] An ID that uniquely identifies a user. # @return [nil] def add_thread_member(channel_id, user_id) request Route[:PUT, "/channels/#{channel_id}/thread-members/#{user_id}", channel_id] end # @!discord_api https://discord.com/developers/docs/resources/channel#leave-thread + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. # @return [nil] def leave_thread(channel_id) request Route[:DELETE, "/channels/#{channel_id}/thread-members/@me", channel_id] end # @!discord_api https://discord.com/developers/docs/resources/channel#remove-thread-member + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param user_id [Integer, String] An ID that uniquely identifies a user. # @return [nil] def remove_thread_member(channel_id, user_id) request Route[:DELETE, "/channels/#{channel_id}/thread-members/#{user_id}", channel_id] end - # @!discord_api https://discord.com/developers/docs/resources/channel#remove-thread-member - # @return [Array>] - def list_thread_members(channel_id, **params) - request Route[:GET, "/channels/#{channel_id}/thread-members", channel_id], - params: params + # @!discord_api https://discord.com/developers/docs/resources/channel#get-thread-member + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param with_member [Boolean] Whether to return guild member object. + # @return [nil] + def get_thread_member(channel_id, user_id, with_member: :undef, **params) + request Route[:GET, "/channels/#{channel_id}/thread-members/#{user_id}", channel_id], + params: filter_undef({ with_member: with_member, **params }) end - # @!discord_api https://discord.com/developers/docs/resources/channel#list-thread-members + # @!discord_api https://discord.com/developers/docs/resources/channel#remove-thread-member + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param with_member [Boolean] hether to return guild member object. + # @param before [Integer, String] Thread members to get after this user ID. + # @param limit [Integer] 1-100 max number of thread members to return. # @return [Array>] - def list_active_threads(channel_id, **params) - request Route[:GET, "/channels/#{channel_id}/threads/active", channel_id] + def list_thread_members(channel_id, with_member: :undef, before: :undef, limit: :undef, **params) + request Route[:GET, "/channels/#{channel_id}/thread-members", channel_id], + params: filter_undef({ with_member: with_member, before: before, limit: limit, **params }) end # @!discord_api https://discord.com/developers/docs/resources/channel#list-public-archived-threads + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param before [Time] Threads archived before this timestamp. + # @param limit [Integer] Max number of threads to return. # @return [Array>] def list_public_archived_threads(channel_id, before: :undef, limit: :undef, **params) request Route[:GET, "/channels/#{channel_id}/threads/archived/public", channel_id], @@ -336,6 +335,9 @@ def list_public_archived_threads(channel_id, before: :undef, limit: :undef, **pa end # @!discord_api https://discord.com/developers/docs/resources/channel#list-private-archived-threads + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param before [Time] Threads archived before this timestamp. + # @param limit [Integer] Max number of threads to return. # @return [Array>] def list_private_archived_threads(channel_id, before: :undef, limit: :undef, **params) request Route[:GET, "/channels/#{channel_id}/threads/archived/private", channel_id], @@ -343,6 +345,9 @@ def list_private_archived_threads(channel_id, before: :undef, limit: :undef, **p end # @!discord_api https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param before [Integer, String] Threads before this ID. + # @param limit [Integer] Max number of threads to return. # @return [Array>] def list_joined_private_archived_threads(channel_id, before: :undef, limit: :undef, **params) request Route[:GET, "/channels/#{channel_id}/users/@me/threads/archived/private"], diff --git a/lib/discordrb/api/endpoints/emoji.rb b/lib/discordrb/api/endpoints/emoji.rb index 850445417..fdb7c8e62 100644 --- a/lib/discordrb/api/endpoints/emoji.rb +++ b/lib/discordrb/api/endpoints/emoji.rb @@ -5,6 +5,7 @@ module API # @!discord_api https://discord.com/developers/docs/resources/emoji module EmojiEndpoints # @!discord_api https://discord.com/developers/docs/resources/emoji#list-guild-emojis + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def list_guild_emojis(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/emojis", guild_id], @@ -12,6 +13,8 @@ def list_guild_emojis(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/emoji#get-guild-emoji + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param emoji_id [Integer, String] An ID that uniquely identifies an emoji. # @return [Hash] def get_guild_emoji(guild_id, emoji_id, **params) request Route[:GET, "/guilds/#{guild_id}/emojis/#{emoji_id}", guild_id], @@ -19,6 +22,11 @@ def get_guild_emoji(guild_id, emoji_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/emoji#create-guild-emoji + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param name [String] Character name of the new emoji. + # @param image [String, #read] A base64 encoded string with the image data. + # @param roles [Array] Roles that can use this emoji. + # @param reason [String] The reason for creating this emoji. # @return [Hash] def create_guild_emoji(guild_id, name:, image:, roles: :undef, reason: :undef, **rest) data = { @@ -34,6 +42,11 @@ def create_guild_emoji(guild_id, name:, image:, roles: :undef, reason: :undef, * end # @!discord_api https://discord.com/developers/docs/resources/emoji#modify-guild-emoji + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param emoji_id [Integer, String] An ID that uniquely identifies an emoji. + # @param name [String] The new name of this emoji. + # @param roles [Array] Roles that can use this emoji. + # @param reason [String] The reason for updating this emoji. # @return [Hash] def modify_guild_emoji(guild_id, emoji_id, name: :undef, roles: :undef, reason: :undef, **rest) data = { @@ -48,11 +61,70 @@ def modify_guild_emoji(guild_id, emoji_id, name: :undef, roles: :undef, reason: end # @!discord_api https://discord.com/developers/docs/resources/emoji#delete-guild-emoji + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param emoji_id [Integer, String] An ID that uniquely identifies an emoji. + # @param reason [String] The reason for deleting this emoji. # @return [nil] def delete_guild_emoji(guild_id, emoji_id, reason: :undef) request Route[:DELETE, "/guilds/#{guild_id}/emojis/#{emoji_id}"], reason: reason end + + # @!discord_api https://discord.com/developers/docs/resources/emoji#list-application-emojis + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @return [Array>] + def list_application_emojis(application_id, **params) + request Route[:GET, "/applications/#{application_id}/emojis", application_id], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/emoji#get-application-emoji + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param emoji_id [Integer, String] An ID that uniquely identifies an emoji. + # @return [Hash] + def get_application_emoji(application_id, emoji_id, **params) + request Route[:GET, "/applications/#{application_id}/emojis/#{emoji_id}", application_id], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/emoji#create-application-emoji + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param name [String] Name of the new application emoji. + # @param image [String, #read] A base64 encoded string with the image data. + # @return [Hash] + def create_application_emoji(application_id, name:, image:, **rest) + data = { + name: name, + image: image, + **rest + } + + request Route[:POST, "/applications/#{application_id}/emojis"], + body: filter_undef(data) + end + + # @!discord_api https://discord.com/developers/docs/resources/emoji#modify-application-emoji + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param emoji_id [Integer, String] An ID that uniquely identifies an emoji. + # @param name [String] The new name of this application emoji + # @return [Hash] + def modify_application_emoji(application_id, emoji_id, name: :undef, **rest) + data = { + name: name, + **rest + } + + request Route[:PATCH, "/applications/#{application_id}/emojis/#{emoji_id}"], + body: filter_undef(data) + end + + # @!discord_api https://discord.com/developers/docs/resources/emoji#delete-application-emoji + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param emoji_id [Integer, String] An ID that uniquely identifies an emoji. + # @return [nil] + def delete_application_emoji(application_id, emoji_id) + request Route[:DELETE, "/applications/#{application_id}/emojis/#{emoji_id}"] + end end end -end \ No newline at end of file +end diff --git a/lib/discordrb/api/endpoints/entitlement.rb b/lib/discordrb/api/endpoints/entitlement.rb new file mode 100644 index 000000000..9b6449690 --- /dev/null +++ b/lib/discordrb/api/endpoints/entitlement.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module Discordrb + module API + # @!discord_api https://discord.com/developers/docs/resources/entitlement + module EntitlementEndpoints + # @!discord_api https://discord.com/developers/docs/resources/entitlement#list-entitlements + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @return [Array>] + def list_entitlements(application_id, **params) + request Route[:GET, "/applications/#{application_id}/entitlements", application_id], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/entitlement#consume-an-entitlement + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param entitlement_id [Integer, String] An ID that uniquely identifies an entitlement. + # @return [Array>] + def consume_an_entitlement(application_id, entitlement_id, **rest) + request Route[:POST, "/applications/#{application_id}/entitlements/#{entitlement_id}", application_id], + body: filter_undef(**rest) + end + + # @!discord_api https://discord.com/developers/docs/resources/entitlement#create-test-entitlement + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param sku_id [Integer, String] An ID that uniquely identifies a SKU. + # @param owner_id [Integer, String] The ID of the user or guild to grant this entitlement to. + # @param owner_type [1, 2] The type of entitlement to create. + # @return [Array>] + def create_test_entitlement(application_id, sku_id:, owner_id:, owner_type:, **rest) + data = { + sku_id: sku_id, + owner_id: owner_id, + owner_type: owner_type, + **rest + } + + request Route[:POST, "/applications/#{application_id}/entitlements", application_id], + body: filter_undef(data) + end + + # @!discord_api https://discord.com/developers/docs/resources/entitlement#delete-test-entitlement + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param entitlement_id [Integer, String] An ID that uniquely identifies an entitlement. + # @return [nil] + def delete_test_entitlement(application_id, entitlement_id) + request Route[:DELETE, "/applications/#{application_id}/entitlements/#{entitlement_id}"] + end + end + end +end diff --git a/lib/discordrb/api/endpoints/guild.rb b/lib/discordrb/api/endpoints/guild.rb index 600e5b56f..6e736f60d 100644 --- a/lib/discordrb/api/endpoints/guild.rb +++ b/lib/discordrb/api/endpoints/guild.rb @@ -5,14 +5,24 @@ module API # @!discord_api https://discord.com/developers/docs/resources/guild module GuildEndpoints # @!discord_api https://discord.com/developers/docs/resources/guild#create-guild + # @param name [String] 2-100 character name. + # @param icon [String, #read] A base64 encoded string with the image data. + # @param verification_level [Integer] Required verification level for members; 0-4. + # @param default_message_notifications [Integer] Default message notification level; 0-1. + # @param explicit_content_filter [Integer] Explicit content filter level; 0-2. + # @param roles [Array>] Array of new roles to create. + # @param channels [Array>] Array of new channels to create. + # @param afk_channel_id [Integer, String] ID for the AFK channel. + # @param afk_timeout [Integer] AFK timeout in seconds. + # @param system_channel_id [Integer, String] Where messages such as welcomes and boosts should be posted. + # @param system_channel_flags [Integer] Bitwise value of system channel flags. # @return [Hash] - def create_guild(name:, region: :undef, icon: :undef, verification_level: :undef, + def create_guild(name:, icon: :undef, verification_level: :undef, default_message_notifications: :undef, explicit_content_filter: :undef, roles: :undef, channels: :undef, afk_channel_id: :undef, afk_timeout: :undef, system_channel_id: :undef, system_channel_flags: :undef, **rest) data = { name: name, - region: region, icon: icon, verification_level: verification_level, default_message_notifications: default_message_notifications, @@ -31,13 +41,16 @@ def create_guild(name:, region: :undef, icon: :undef, verification_level: :undef end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param with_counts [Boolean] Whether to include an approximate member count. # @return [Hash] - def get_guild(guild_id, **params) + def get_guild(guild_id, with_counts: :undef, **params) request Route[:GET, "/guilds/#{guild_id}", guild_id], - params: params + params: filter_undef({ with_counts: with_counts, **params }) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-preview + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Hash] def get_guild_preview(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/preview", guild_id], @@ -45,18 +58,40 @@ def get_guild_preview(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param name [String] 2-100 character name. + # @param verification_level [String] Required verification level for members; 0-4. + # @param default_message_notifications [Integer] Default message notification level; 0-1. + # @param explicit_content_filter [Integer] Explicit content filter level; 0-2. + # @param afk_channel_id [Integer, String] ID for the AFK channel. + # @param afk_timeout [Integer] AFK timeout in seconds. + # @param icon [String, #read] A base64 encoded string with the image data. + # @param owner_id [Integer, String] ID of the new guild owner. + # @param splash [String, #read] A base64 encoded string with the image data. + # @param discovery_splash [String, #read] A base64 encoded string with the image data. + # @param banner [String, #read] A base64 encoded string with the image data. + # @param system_channel_id [Integer, String] Where messages such as welcomes and boosts should be posted. + # @param system_channel_flags [Integer] Bitwise value of system channel flags. + # @param rules_channel_id [Integer, String] ID of the channel to mark as that guild's rules channels. + # @param public_updates_channel_id [Integer, String] ID of the channel where server staff reccive updates from Discord. + # @param preferred_locale [String] preferred locale of a guild used in server discovery. Default is "en-US". + # @param features [Array] Array of strings that specifiy enabled features for this guild. + # @param description [String] Description for this guild. + # @param premium_progress_bar_enabled [Boolean] Whether the boost progress bar should be visible. + # @param safety_alerts_channel_id [Integer, String] Channel where safety alerts should be sent from Discord. + # @param reason [String] The reason for modifiying this guild. # @return [Hash] - def modify_guild(guild_id, name: :undef, region: :undef, verification_level: :undef, + def modify_guild(guild_id, name: :undef, verification_level: :undef, default_message_notifications: :undef, explicit_content_filter: :undef, afk_channel_id: :undef, afk_timeout: :undef, icon: :undef, owner_id: :undef, splash: :undef, discovery_splash: :undef, banner: :undef, system_channel_id: :undef, system_channel_flags: :undef, rules_channel_id: :undef, public_updates_channel_id: :undef, preferred_locale: :undef, - features: :undef, description: :undef, **rest) + features: :undef, description: :undef, premium_progress_bar_enabled: :undef, + safety_alerts_channel_id: :undef, reason: :undef, **rest) data = { name: name, - region: region, verification_level: verification_level, - default_message_notifications:default_message_notifications, + default_message_notifications: default_message_notifications, explicit_content_filter: explicit_content_filter, afk_channel_id: afk_channel_id, afk_timeout: afk_timeout, @@ -72,20 +107,25 @@ def modify_guild(guild_id, name: :undef, region: :undef, verification_level: :un preferred_locale: preferred_locale, features: features, description: description, + premium_progress_bar_enabled: premium_progress_bar_enabled, + safety_alerts_channel_id: safety_alerts_channel_id, **rest } request Route[:PATCH, "/guilds/#{guild_id}", guild_id], - body: filter_undef(data) + body: filter_undef(data), + reason: reason end # @!discord_api https://discord.com/developers/docs/resources/guild#delete-guild + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [nil] def delete_guild(guild_id) request Route[:DELETE, "/guilds/#{guild_id}", guild_id] end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-channels + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def get_guild_channels(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/channels", guild_id], @@ -93,10 +133,33 @@ def get_guild_channels(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#create-guild-channel + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param name [String] 1-100 character name. + # @param type [Integer] Type of channel to create. + # @param topic [String] 0-1024 character topic. + # @param bitrate [Integer] Bitrate in bits of the voice channel. + # @param user_limit [Integer] Max amount of users that can be in this voice channel. + # @param rate_limit_per_user [Integer] Time between 0-21600 that a user has to wait between messages. + # @param position [Integer] Sorting position of the channel. + # @param permission_overwrites [Array>] Permission overwrites for this channel. + # @param parent_id [Integer, String] The parent category of this channel. + # @param nsfw [Boolean] Whether this channel is age-restricted or not. + # @param rtc_region [String] Voice region of the voice channel. + # @param video_quality_mode [Integer] Camera quality mode of the voice channel. + # @param default_auto_archive_duration [Integer] Default client duration to auto-archive new threads. + # @param default_reaction_emoji [Hash] Emoji to show for reactions on posts in GUILD_FORUM channels. + # @param available_tags [Array>] Avalibile tags for posts in GUILD_FORUM channels. + # @param default_sort_order [Integer] Default sort order for GUILD_FORUM channels. + # @param default_forum_layout [Integer] Default forum layout for GUILD_FORUM channels. + # @param default_thread_rate_limit_per_user [Integer] Inital rate-limit-per-user for new threads. + # @param reason [String] The reason for creating this channel. # @return [Hash] def create_guild_channel(guild_id, name:, type: :undef, topic: :undef, bitrate: :undef, user_limit: :undef, rate_limit_per_user: :undef, position: :undef, permission_overwrites: :undef, - parent_id: :undef, nsfw: :undef, reason: :undef, **rest) + parent_id: :undef, nsfw: :undef, rtc_region: :undef, video_quality_mode: :undef, + default_auto_archive_duration: :undef, default_reaction_emoji: :undef, + available_tags: :undef, default_sort_order: :undef, default_forum_layout: :undef, + default_thread_rate_limit_per_user: :undef, reason: :undef, **rest) data = { name: name, type: type, @@ -108,6 +171,14 @@ def create_guild_channel(guild_id, name:, type: :undef, topic: :undef, bitrate: permission_overwrites: permission_overwrites, parent_id: parent_id, nsfw: nsfw, + rtc_region: rtc_region, + video_quality_mode: video_quality_mode, + default_auto_archive_duration: default_auto_archive_duration, + default_reaction_emoji: default_reaction_emoji, + available_tags: available_tags, + default_sort_order: default_sort_order, + default_forum_layout: default_forum_layout, + default_thread_rate_limit_per_user: default_thread_rate_limit_per_user, **rest } @@ -117,21 +188,25 @@ def create_guild_channel(guild_id, name:, type: :undef, topic: :undef, bitrate: end # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param channels [Array>] An array of channel objects. # @return [nil] - def modify_guild_channel_positions(guild_id, channels, reason: :undef) + def modify_guild_channel_positions(guild_id, channels) request Route[:PATCH, "/guilds/#{guild_id}/channels", guild_id], - body: channels, - reason: reason + body: channels end - # @!discord_api https://discord.com/developers/docs/resources/guild#list-active-threads + # @!discord_api https://discord.com/developers/docs/resources/guild#list-active-guild-threads + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Hash] - def list_active_threads(guild_id, **params) + def list_active_guild_threads(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/threads/active"], params: params end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-member + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. # @return [Hash] def get_guild_member(guild_id, user_id, **params) request Route[:GET, "/guilds/#{guild_id}/members/#{user_id}", guild_id], @@ -139,6 +214,9 @@ def get_guild_member(guild_id, user_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-preview + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param limit [Integer] 1-1000 max number of members to return. + # @param after [Integer, String] Highest user ID on the previous page. # @return [Array>] def list_guild_members(guild_id, limit: :undef, after: :undef, **params) request Route[:GET, "/guilds/#{guild_id}/members", guild_id], @@ -146,6 +224,9 @@ def list_guild_members(guild_id, limit: :undef, after: :undef, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#search-guild-members + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param query [String] Usernames or nicknames to search against. + # @param limit [Integer] 1-1000 max number of members to return. # @return [Array>] def search_guild_members(guild_id, query:, limit: :undef, **params) request Route[:GET, "/guilds/#{guild_id}/members/search", guild_id], @@ -153,6 +234,13 @@ def search_guild_members(guild_id, query:, limit: :undef, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#add-guild-member + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param access_token [String] oauth2 access token with the guilds.join scope. + # @param nick [String] The nickname they should be assigned. + # @param roles [Array] Array of role ID's to assign to the member. + # @param mute [Boolean] Whether this user should be muted in voice channels. + # @param deaf [Boolean] Whether this user should be deafened in voice channels. # @return [Hash] def add_guild_member(guild_id, user_id, access_token:, nick: :undef, roles: :undef, mute: :undef, deaf: :undef, **rest) @@ -170,15 +258,25 @@ def add_guild_member(guild_id, user_id, access_token:, nick: :undef, roles: :und end # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild-member + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param nick [String] The user's new nickname. + # @param roles [Array] Array of role ID's to assign to the member. + # @param mute [Boolean] Whether this user should be muted in voice channels. + # @param deaf [Boolean] Whether this user should be deafened in voice channels. + # @param channel_id [Integer, String] ID of the voice channel to move the user to. + # @param communication_disabled_until [Time] When the user's timeout should expire. + # @param reason [String] The reason for modifiying this guild member. # @return [Hash] def modify_guild_member(guild_id, user_id, nick: :undef, roles: :undef, mute: :undef, deaf: :undef, - channel_id: :undef, reason: :undef, **rest) + channel_id: :undef, communication_disabled_until: :undef, reason: :undef, **rest) data = { nick: nick, roles: roles, mute: mute, deaf: deaf, channel_id: channel_id, + communication_disabled_until: communication_disabled_until, **rest } @@ -188,6 +286,9 @@ def modify_guild_member(guild_id, user_id, nick: :undef, roles: :undef, mute: :u end # @!discord_api https://discord.com/developers/docs/resources/guild#modify-current-member + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param nick [String] The user's new nickname. + # @param reason [String] The reason for modifiying yourself. # @return [Hash] def modify_current_member(guild_id, nick: :undef, reason: :undef, **rest) request Route[:PATCH, "/guilds/#{guild_id}/members/@me", guild_id], @@ -196,6 +297,10 @@ def modify_current_member(guild_id, nick: :undef, reason: :undef, **rest) end # @!discord_api https://discord.com/developers/docs/resources/guild#add-guild-member-role + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param role_id [Integer, String] An ID that uniquely identifies a role. + # @param reason [String] The reason for adding a role to this member. # @return [nil] def add_guild_member_role(guild_id, user_id, role_id, reason: :undef) request Route[:PUT, "/guilds/#{guild_id}/members/#{user_id}/roles/#{role_id}", guild_id], @@ -204,6 +309,10 @@ def add_guild_member_role(guild_id, user_id, role_id, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/guild#remove-guild-member-role + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param role_id [Integer, String] An ID that uniquely identifies a role. + # @param reason [String] The reason for removing a role from this member. # @return [nil] def remove_guild_member_role(guild_id, user_id, role_id, reason: :undef) request Route[:DELETE, "/guilds/#{guild_id}/members/#{user_id}/roles/#{role_id}", guild_id], @@ -211,6 +320,9 @@ def remove_guild_member_role(guild_id, user_id, role_id, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/guild#remove-guild-member + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param reason [String] The reason for removing this member. # @return [nil] def remove_guild_member(guild_id, user_id, reason: :undef) request Route[:DELETE, "/guilds/#{guild_id}/members/#{user_id}", guild_id], @@ -218,13 +330,19 @@ def remove_guild_member(guild_id, user_id, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-bans + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param limit [Integer] Max number of banned users to return up to 1000. + # @param before [Integer, String] Get users only before this ID. + # @param after [Integer, String] Get users only after this ID. # @return [Array>] - def get_guild_bans(guild_id, **params) + def get_guild_bans(guild_id, limit: :undef, before: :undef, after: :undef, **params) request Route[:GET, "/guilds/#{guild_id}/bans", guild_id], - params: params + params: filter_undef({ limit: limit, before: before, after: after, **params }) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-ban + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. # @return [Hash] def get_guild_ban(guild_id, user_id, **params) request Route[:GET, "/guilds/#{guild_id}/bans/#{user_id}", guild_id], @@ -232,28 +350,70 @@ def get_guild_ban(guild_id, user_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#create-guild-ban + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param delete_message_seconds [Integer] Number between 0-604800 to delete messages for. # @return [nil] - def create_guild_ban(guild_id, user_id, delete_message_days: :undef, reason: :undef, **rest) + def create_guild_ban(guild_id, user_id, delete_message_seconds: :undef, reason: :undef, **rest) request Route[:PUT, "/guilds/#{guild_id}/bans/#{user_id}", guild_id], - body: filter_undef({ delete_message_days: delete_message_days, **rest }), + body: filter_undef({ delete_message_seconds: delete_message_seconds, **rest }), reason: reason end # @!discord_api https://discord.com/developers/docs/resources/guild#remove-guild-ban + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param reason [String] The reason for removing this user's ban. # @return [nil] def remove_guild_ban(guild_id, user_id, reason: :undef) request Route[:DELETE, "/guilds/#{guild_id}/bans/#{user_id}", guild_id], reason: reason end + # @!discord_api https://discord.com/developers/docs/resources/guild#bulk-guild-ban + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_ids [Array] An array containing user ID's to ban. + # @param delete_message_seconds [Integer] Number between 0-604800 to delete messages for. + # @return [Hash] + def bulk_guild_ban(guild_id, user_ids, delete_message_seconds: :undef, reason: :undef, **rest) + data = { + user_ids: user_ids, + delete_message_seconds: delete_message_seconds, + **rest + } + + request Route[:POST, "/guilds/#{guild_id}/bulk-ban", guild_id], + body: filter_undef(data), + reason: reason + end + # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-roles + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def get_guild_roles(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/roles", guild_id], params: params end + # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-role + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param role_id [Integer, String] An ID that uniquely identifies a role. + # @return [Array>] + def get_guild_role(guild_id, role_id, **params) + request Route[:GET, "/guilds/#{guild_id}/roles/#{role_id}", guild_id], + params: params + end + # @!discord_api https://discord.com/developers/docs/resources/guild#create-guild-role + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param name [String] Name of the role to create. + # @param permissions [String] Bitwise permissions for this role. + # @param color [Integer] An RGB color value for this role. + # @param hoist [Boolean] Whether this role's members should be displayed seperately in the sidebar. + # @param icon [String, #read] An icon to display next to this role. + # @param unicode_emoji [String] Unicode emoji for the role. + # @param mentionable [Boolean] Whether everyone should be able to mention this role. + # @param reason [String] The reason for creating this role. # @return [Hash] def create_guild_role(guild_id, name: :undef, permissions: :undef, color: :undef, hoist: :undef, icon: :undef, unicode_emoji: :undef, mentionable: :undef, reason: :undef, **rest) @@ -274,6 +434,9 @@ def create_guild_role(guild_id, name: :undef, permissions: :undef, color: :undef end # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild-role-positions + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param roles [Array>] Role objects sorted in their new positions. + # @param reason [String] The reason for modifiying the positions of this role. # @return [Array>] def modify_guild_role_positions(guild_id, roles, reason: :undef) request Route[:PATCH, "/guilds/#{guild_id}/roles", guild_id], @@ -282,6 +445,16 @@ def modify_guild_role_positions(guild_id, roles, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild-role + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param role_id [Integer, String] An ID that uniquely identifies a role. + # @param name [String] New name of the role. + # @param permissions [String] Bitwise permissions for this role. + # @param color [Integer] An RGB color value for this role. + # @param hoist [Boolean] Whether this role's members should be displayed seperately in the sidebar. + # @param icon [String, #read] An icon to display next to this role. + # @param unicode_emoji [String] Unicode emoji for the role. + # @param mentionable [Boolean] Whether everyone should be able to mention this role. + # @param reason [String] The reason for modifiying this role. # @return [Hash] def modify_guild_role(guild_id, role_id, name: :undef, permissions: :undef, color: :undef, hoist: :undef, icon: :undef, unicode_emoji: :undef, mentionable: :undef, reason: :undef, **rest) @@ -301,7 +474,21 @@ def modify_guild_role(guild_id, role_id, name: :undef, permissions: :undef, colo reason: reason end + # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild-mfa-level + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param level [Integer] The new MFA level; either 0 or 1. + # @param reason [String] The reason for modifiying the MFA level. + # @return [Intger] + def modify_guild_mfa_level(guild_id, level:, reason: :undef, **rest) + request Route[:POST, "/guilds/#{guild_id}/mfa", guild_id], + body: filter_undef({ level: level, **rest }), + reason: reason + end + # @!discord_api https://discord.com/developers/docs/resources/guild#delete-guild-role + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param role_id [Integer, String] An ID that uniquely identifies a role. + # @param reason [String] The reason for deleting this role. # @return [nil] def delete_guild_role(guild_id, role_id, reason: :undef) request Route[:DELETE, "/guilds/#{guild_id}/roles/#{role_id}", guild_id], @@ -309,6 +496,9 @@ def delete_guild_role(guild_id, role_id, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-prune-count + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param days [Integer] Number of days between 1-30 to count for. + # @param include_roles [String] comma-delimited array of role ID's. # @return [Hash] def get_guild_prune_count(guild_id, days: :undef, include_roles: :undef, **params) request Route[:GET, "/guilds/#{guild_id}/prune", guild_id], @@ -316,6 +506,11 @@ def get_guild_prune_count(guild_id, days: :undef, include_roles: :undef, **param end # @!discord_api https://discord.com/developers/docs/resources/guild#begin-guild-prune + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param days [Integer] Number of days between 1-30 to prune for. + # @param compute_prune_count [Boolean] Whether to return the prune key. + # @param include_roles [Array] Array of role ID's to include. + # @param reason [String] The reason for initiating a prune. # @return [Hash] def begin_guild_prune(guild_id, days: :undef, compute_prune_count: :undef, include_roles: :undef, reason: :undef, **rest) @@ -332,6 +527,7 @@ def begin_guild_prune(guild_id, days: :undef, compute_prune_count: :undef, inclu end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-voice-regions + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def get_guild_voice_regions(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/regions", guild_id], @@ -339,6 +535,7 @@ def get_guild_voice_regions(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-invites + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def get_guild_invites(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/invites", guild_id], @@ -346,6 +543,7 @@ def get_guild_invites(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-integrations + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def get_guild_integrations(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/integrations", guild_id], @@ -353,6 +551,9 @@ def get_guild_integrations(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#delete-guild-integration + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param integration_id [Integer, String] An ID that uniquely identifies an integration. + # @param reason [String] The reason for deleting this integration. # @return [nil] def delete_guild_integration(guild_id, integration_id, reason: :undef) request Route[:DELETE, "/guilds/#{guild_id}/integrations/#{integration_id}", guild_id], @@ -360,6 +561,7 @@ def delete_guild_integration(guild_id, integration_id, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-widget-settings + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Hash] def get_guild_widget_settings(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/widget", guild_id], @@ -367,6 +569,10 @@ def get_guild_widget_settings(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild-widget + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param enabled [Boolean] Whether the widget is enabled. + # @param channel_id [Integer, String] The channel ID of the widget. + # @param reason [String] The reason for modifiying the guild widget. # @return [Hash] def modify_guild_widget(guild_id, enabled: :undef, channel_id: :undef, reason: :undef, **rest) request Route[:PATCH, "/guilds/#{guild_id}/widget", guild_id], @@ -375,6 +581,7 @@ def modify_guild_widget(guild_id, enabled: :undef, channel_id: :undef, reason: : end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-widget + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Hash] def get_guild_widget(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/widget.json", guild_id], @@ -382,6 +589,7 @@ def get_guild_widget(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-vanity-url + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Hash] def get_guild_vanity_url(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/vanity-url", guild_id], @@ -389,6 +597,8 @@ def get_guild_vanity_url(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-widget-image + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param style [String] Style of the widget image to return. # @return [String] def get_guild_widget_image(guild_id, style: :undef, **params) request Route[:GET, "/guilds/#{guild_id}/widget.png", guild_id], @@ -396,6 +606,7 @@ def get_guild_widget_image(guild_id, style: :undef, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Hash] def get_guild_welcome_screen(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/welcome-screen", guild_id], @@ -403,6 +614,11 @@ def get_guild_welcome_screen(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param enabled [Boolean] Whether the welcome screen should be enabled. + # @param welcome_channels [Array] Array of welcome screen channel objects. + # @param description [String] Server description to show on the welcome screen. + # @param reason [String] The reason for modifiying the welcome screen. # @return [Hash] def modify_guild_welcome_screen(guild_id, enabled: :undef, welcome_channels: :undef, description: :undef, reason: :undef, **rest) @@ -418,32 +634,34 @@ def modify_guild_welcome_screen(guild_id, enabled: :undef, welcome_channels: :un reason: reason end - # @!discord_api https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state - # @return [nil] - def modify_current_user_voice_state(guild_id, channel_id: :undef, suppress: :undef, - request_to_speak_timestamp: :undef, **rest) - data = { - channel_id: channel_id, - suppress: suppress, - request_to_speak_timestamp: request_to_speak_timestamp, - **rest - } - - request Route[:PATCH, "/guilds/#{guild_id}/voice-states/@me", guild_id], - body: filter_undef(data) + # @!discord_api https://discord.com/developers/docs/resources/guild#get-guild-onboarding + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @return [Hash] + def get_guild_onboarding(guild_id, **params) + request Route[:GET, "/guilds/#{guild_id}/onboarding", guild_id], + params: params end - # @!discord_api https://discord.com/developers/docs/resources/guild#modify-user-voice-state - # @return [nil] - def modify_user_voice_state(guild_id, user_id, channel_id: :undef, suppress: :undef, **rest) + # @!discord_api https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # prompts [Hash] Array of onboarding prompt objects. + # default_channel_ids [Array] Array of channel ID's members get added to by default. + # enabled [Boolean] Whether onboarding is enabled or not. + # reason [String] Reason for modifiying this server's onboarding. + # @return [Hash] + def modify_guild_onboarding(guild_id, prompts:, default_channel_ids:, enabled:, mode:, + reason: :undef, **rest) data = { - channel_id: channel_id, - suppress: suppress, + prompts: prompts, + default_channel_ids: default_channel_ids, + enabled: enabled, + mode: mode, **rest } - request Route[:PATCH, "/guilds/#{guild_id}/voice-states/#{user_id}", guild_id], - body: filter_undef(data) + request Route[:PUT, "/guilds/#{guild_id}/onboarding", guild_id], + body: filter_undef(data), + reason: reason end end end diff --git a/lib/discordrb/api/endpoints/guild_scheduled_event.rb b/lib/discordrb/api/endpoints/guild_scheduled_event.rb new file mode 100644 index 000000000..360de6633 --- /dev/null +++ b/lib/discordrb/api/endpoints/guild_scheduled_event.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +module Discordrb + module API + # @!discord_api https://discord.com/developers/docs/resources/guild-scheduled-event + module GuildScheduledEventEndpoints + # @!discord_api https://discord.com/developers/docs/resources/guild-scheduled-event#list-scheduled-events-for-guild + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @return [Hash] + def list_scheduled_events_for_guild(guild_id, **params) + request Route[:GET, "/guilds/#{guild_id}/scheduled-events", guild_id], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/auto-moderation#get-guild-scheduled-event + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param guild_scheduled_event_id [Integer, String] An ID that uniquely identifies a scheduled event. + # @return [Hash] + def get_guild_scheduled_event(guild_id, guild_scheduled_event_id, **params) + request Route[:GET, "/guilds/#{guild_id}/scheduled-events/#{guild_scheduled_event_id}", guild_id], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param channel_id [Integer, String] The channel ID for this scheduled event. + # @param entity_metadata [String] Location of the event. Required for external event types. + # @param name [String] Name of the event. + # @param privacy_level [Integer] Who the scheduled event can be accessed by. + # @param scheduled_start_time [Time] A time object that indicates when to start the event. + # @param scheduled_end_time [Time] A time object that indicates when to end the event. + # @param description [String] Description of the scheduled event. + # @param entity_type [Integer] The location of the event; E.g. voice, stage-instance, etc. + # @param image [String, #read] A base64 encoded string with the image data. + # @param recurrence_rule [Hash] A recurrence rule object. See the offical API docs for more info. + # @param reason [String] The reason for creating this scheduled event. + # @return [Hash] + def create_guild_scheduled_event(guild_id, name:, channel_id: :undef, entity_metadata: :undef, privacy_level: :undef, + scheduled_start_time: :undef, scheduled_end_time: :undef, description: :undef, + entity_type: :undef, image: :undef, recurrence_rule: :undef, reason: :undef, **rest) + data = { + channel_id: channel_id, + entity_metadata: entity_metadata, + name: name, + privacy_level: privacy_level, + scheduled_start_time: scheduled_start_time, + scheduled_end_time: scheduled_end_time, + description: description, + entity_type: entity_type, + image: image, + recurrence_rule: recurrence_rule, + **rest + } + + request Route[:POST, "/guilds/#{guild_id}/scheduled-events"], + body: filter_undef(data), + reason: reason + end + + # @!discord_api https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param guild_scheduled_event_id [Integer, String] An ID that uniquely identifies a scheduled event. + # @param channel_id [Integer, String] The channel ID for this scheduled event. + # @param entity_metadata [String] Location of the event. Required for external event types. + # @param name [String] New name of the event. + # @param privacy_level [Integer] Who the scheduled event can be accessed by. + # @param scheduled_start_time [Time] A time object that indicates when to start the event. + # @param scheduled_end_time [Time] A time object that indicates when to end the event. + # @param description [String] Description of the scheduled event. + # @param entity_type [Integer] The location of the event; E.g. voice, stage-instance, etc. + # @param image [String, #read] A base64 encoded string with the image data. + # @param recurrence_rule [Hash] A recurrence rule object. See the offical API docs for more info. + # @param reason [String] The reason for updating this scheduled event. + # @return [Hash] + def modify_guild_scheduled_event(guild_id, guild_scheduled_event_id:, channel_id: :undef, entity_metadata: :undef, + name: :undef, privacy_level: :undef, scheduled_start_time: :undef, scheduled_end_time: :undef, + description: :undef, entity_type: :undef, status: :undef, image: :undef, recurrence_rule: :undef, + reason: :undef, **rest) + data = { + channel_id: channel_id, + entity_metadata: entity_metadata, + name: name, + privacy_level: privacy_level, + scheduled_start_time: scheduled_start_time, + scheduled_end_time: scheduled_end_time, + description: description, + entity_type: entity_type, + status: status, + image: image, + recurrence_rule: recurrence_rule, + **rest + } + + request Route[:PATCH, "/guilds/#{guild_id}/scheduled-events/#{guild_scheduled_event_id}"], + body: filter_undef(data), + reason: reason + end + + # @!discord_api https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param guild_scheduled_event_id [Integer, String] An ID that uniquely identifies a scheduled event. + # @return [nil] + def delete_guild_scheduled_event(guild_id, guild_scheduled_event_id) + request Route[:DELETE, "/guilds/#{guild_id}/scheduled-events/#{guild_scheduled_event_id}"] + end + end + end +end diff --git a/lib/discordrb/api/endpoints/guild_template.rb b/lib/discordrb/api/endpoints/guild_template.rb index 931890a26..1aca957cd 100644 --- a/lib/discordrb/api/endpoints/guild_template.rb +++ b/lib/discordrb/api/endpoints/guild_template.rb @@ -5,6 +5,7 @@ module API # @!discord_api https://discord.com/developers/docs/resources/guild-template module GuildTemplateEndpoints # @!discord_api https://discord.com/developers/docs/resources/guild-template#get-guild-template + # @param template_code [String] A code that creates a guild based on another guild. # @return [Hash] def get_guild_template(template_code, **params) request Route[:GET, "/guilds/templates/#{template_code}"], @@ -12,13 +13,16 @@ def get_guild_template(template_code, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild-template#create-guild-from-guild-template + # @param template_code [String] A code that creates a guild based on another guild. + # @param icon [String, #read] A base64 encoded string with the image data. # @return [Hash] - def create_guild_from_template(template_code, **rest) + def create_guild_from_template(template_code, icon: :undef, **rest) request Route[:POST, "/guilds/templates/#{template_code}"], - body: rest + body: filter_undef({ icon: icon, **rest }) end # @!discord_api https://discord.com/developers/docs/resources/guild-template#get-guild-templates + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def get_guild_templates(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/templates", guild_id], @@ -26,6 +30,9 @@ def get_guild_templates(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/guild-template#create-guild-template + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param name [String] 1-100 character name. + # @param description [String] 0-120 character description. # @return [Hash] def create_guild_template(guild_id, name:, description: :undef, **rest) request Route[:POST, "/guilds/#{guild_id}/templates", guild_id], @@ -33,6 +40,8 @@ def create_guild_template(guild_id, name:, description: :undef, **rest) end # @!discord_api https://discord.com/developers/docs/resources/guild-template#sync-guild-template + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param template_code [String] A code that creates a guild based on another guild. # @return [Hash] def sync_guild_template(guild_id, template_code) request Route[:PUT, "/guilds/#{guild_id}/templates/#{template_code}"], @@ -40,13 +49,19 @@ def sync_guild_template(guild_id, template_code) end # @!discord_api https://discord.com/developers/docs/resources/guild-template#modify-guild-template - # @return [Hash] + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param template_code [String] A code that creates a guild based on another guild. + # @param name [String] 1-100 character name. + # @param description [String] 0-120 character description. + # @return [Hash]. def modify_guild_template(guild_id, template_code, name: :undef, description: :undef, **rest) request Route[:PATCH, "/guilds/#{guild_id}/templates/#{template_code}"], body: filter_undef({ name: name, description: description, **rest }) end # @!discord_api https://discord.com/developers/docs/resources/guild-template#delete-guild-template + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param template_code [String] A code that creates a guild based on another guild. # @return [Hash] def delete_guild_template(guild_id, template_code) request Route[:DELETE, "/guilds/#{guild_id}/templates/#{template_code}"] diff --git a/lib/discordrb/api/endpoints/interaction.rb b/lib/discordrb/api/endpoints/interaction.rb index 5f52edddd..6ed8496d4 100644 --- a/lib/discordrb/api/endpoints/interaction.rb +++ b/lib/discordrb/api/endpoints/interaction.rb @@ -1,24 +1,27 @@ +# frozen_string_literal: true module Discordrb module API # @!discord_api https://discord.com/developers/docs/interactions/receiving-and-responding#interactions module InteractionEndpoints # @!discord_api https://discord.com/developers/docs/interactions/slash-commands#create-interaction-response - # @param id [Integer, String] - # @param token [String] - # @param type [Integer] - # @param content [String] - # @param tts [true, false] - # @param embeds [Array] - # @param allowed_mentions [Hash] - # @param flags [Integer] - # @param components [Array] + # @param id [Integer, String] An ID that uniquely identifies an interaction. + # @param token [String] A token to enable a response to an interaction. + # @param type [Integer] Type of interaction. + # @param content [String] Message content. + # @param tts [Boolean] Whether the response is TTS. + # @param embeds [Array] Embed objects to include. + # @param allowed_mentions [Hash] Allowed mentions object. + # @param flags [Integer] Bitfield value of message flags. + # @param components [Array] Array of component objects. + # @param poll [Hash] A poll request object. # @return [Hash] def create_interaction_response(id, token, type:, content: :undef, tts: :undef, embeds: :undef, - allowed_mentions: :undef, flags: :undef, components: :undef, **rest) + custom_id: :undef, title: :undef, allowed_mentions: :undef, flags: :undef, + components: :undef, poll: :undef, **rest) body = { type: type, content: content, tts: tts, embeds: embeds, allowed_mentions: allowed_mentions, - flags: flags, components: components, **rest + custom_id: custom_id, title: title, flags: flags, components: components, poll: poll, **rest } request Route[:POST, "/interactions/#{id}/#{token}/callback"], @@ -26,38 +29,100 @@ def create_interaction_response(id, token, type:, content: :undef, tts: :undef, end # @!discord_api https://discord.com/developers/docs/interactions/slash-commands#get-original-interaction-response - # @param id [Integer, String] - # @param token [String] + # @param id [Integer, String] An ID that uniquely identifies an application. + # @param token [String] A token to enable a response to an interaction. # @return [Hash] def get_original_interaction_response(id, token) get_webhook_message(id, token, '@original') end # @!discord_api https://discord.com/developers/docs/interactions/slash-commands#edit-original-interaction-response - # @param id [Integer, String] - # @param token [String] - # @param content [String] - # @param embeds [Array] - # @param allowed_mentions [Hash] - # @param flags [Integer] - # @param components [Array] + # @param id [Integer, String] An ID that uniquely identifies an application. + # @param token [String] A token to enable a response to an interaction. + # @param content [String] Message content. + # @param embeds [Array] Embed objects to include. + # @param allowed_mentions [Hash] Allowed mentions object. + # @param flags [Integer] Bitfield value of message flags. + # @param components [Array] Array of component objects. + # @param poll [Hash] A poll request object. # @return [Hash] def edit_original_interaction_response(id, token, content: :undef, embeds: :undef, allowed_mentions: :undef, - flags: :undef, components: :undef, **rest) + flags: :undef, components: :undef, poll: :undef, **rest) body = { - type: type, content: content, embeds: embeds, allowed_mentions: allowed_mentions, - flags: flags, components: components, **rest + content: content, embeds: embeds, allowed_mentions: allowed_mentions, + flags: flags, components: components, poll: poll, **rest } edit_webhook_message(id, token, '@original', **body) end # @!discord_api https://discord.com/developers/docs/interactions/slash-commands#delete-original-interaction-response - # @param id [Integer, String] - # @param token [String] + # @param id [Integer, String] An ID that uniquely identifies an application. + # @param token [String] A token to enable a response to an interaction. + # @return [nil] def delete_original_interaction_response(id, token) delete_webhook_message(id, token, '@original') end + + # @!discord_api https://discord.com/developers/docs/interactions/receiving-and-responding#create-followup-message + # @param id [Integer, String] An ID that uniquely identifies an application. + # @param token [String] A token to enable a response to an interaction. + # @param content [String] Message content. + # @param tts [Boolean] Whether the response is TTS. + # @param embeds [Array] Embed objects to include. + # @param allowed_mentions [Hash] Allowed mentions object. + # @param components [Array] Array of component objects. + # @param flags [Integer] Bitfield value of message flags. + # @param applied_tags [Array] Array of ID tags to apply to a forum. + # @param poll [Hash] A poll request object. + # @return [Hash] + def create_followup_message(id, token, content: :undef, tts: :undef, embeds: :undef, allowed_mentions: :undef, + components: :undef, flags: :undef, applied_tags: :undef, poll: :undef, **rest) + body = { + wait: true, content: content, tts: tts, embeds: embeds, allowed_mentions: allowed_mentions, + flags: flags, components: components, applied_tags: applied_tags, poll: poll, **rest + } + + execute_webhook(id, token, **body) + end + + # @!discord_api https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message + # @param id [Integer, String] An ID that uniquely identifies an application. + # @param token [String] A token to enable a response to an interaction. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @return [Hash] + def get_followup_message(id, token, message_id) + get_webhook_message(id, token, message_id) + end + + # @!discord_api https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message + # @param id [Integer, String] An ID that uniquely identifies an application. + # @param token [String] A token to enable a response to an interaction. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param content [String] Message content. + # @param embeds [Array] Embed objects to include. + # @param allowed_mentions [Hash] Allowed mentions object. + # @param components [Array] Array of component objects. + # @param poll [Hash] A poll request object. + # @return [Hash] + def edit_followup_message(id, token, message_id, content: :undef, embeds: :undef, allowed_mentions: :undef, + components: :undef, poll: :undef, **rest) + body = { + content: content, embeds: embeds, allowed_mentions: allowed_mentions, + components: components, poll: poll, **rest + } + + edit_webhook_message(id, token, message_id, **body) + end + + # @!discord_api https://discord.com/developers/docs/interactions/receiving-and-responding#delete-followup-message + # @param id [Integer, String] An ID that uniquely identifies an application. + # @param token [String] A token to enable a response to an interaction. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @return [nil] + def delete_followup_message(id, token, message_id) + delete_webhook_message(id, token, message_id) + end end end end diff --git a/lib/discordrb/api/endpoints/invite.rb b/lib/discordrb/api/endpoints/invite.rb index 987871ab1..cf830f5de 100644 --- a/lib/discordrb/api/endpoints/invite.rb +++ b/lib/discordrb/api/endpoints/invite.rb @@ -1,15 +1,30 @@ +# frozen_string_literal: true + module Discordrb module API # @!discord_api https://discord.com/developers/docs/resources/invite module InviteEndpoints # @!discord_api https://discord.com/developers/docs/resources/invite#get-invite + # @param invite_code [String] A code used to add a user to a guild. + # @param with_counts [Boolean] Whether this invite should contain an approximate member count. + # @param with_expiration [Boolean] Whether this invite should contain its expiration date. + # @param guild_scheduled_event_id [Boolean] The ID of the scheduled event to include with this invite. # @return [Hash] - def get_invite(invite_code, **params) + def get_invite(invite_code, with_counts: :undef, with_expiration: :undef, guild_scheduled_event_id: :undef, **_params) + data = { + with_counts: with_counts, + with_expiration: with_expiration, + guild_scheduled_event_id: guild_scheduled_event_id, + **rest + } + request Route[:GET, "/invites/#{invite_code}"], - params: params + params: filter_undef(data) end # @!discord_api https://discord.com/developers/docs/resources/invite#delete-invite + # @param invite_code [String] A code used to add a user to a guild. + # @param reason [String] The reason for deleting this invite code. # @return [Hash] def delete_invite(invite_code, reason: :undef) request Route[:DELETE, "/invites/#{invite_code}"], diff --git a/lib/discordrb/api/endpoints/message.rb b/lib/discordrb/api/endpoints/message.rb new file mode 100644 index 000000000..cd39205d6 --- /dev/null +++ b/lib/discordrb/api/endpoints/message.rb @@ -0,0 +1,203 @@ +# frozen_string_literal: true + +module Discordrb + module API + # @!discord_api https://discord.com/developers/docs/resources/message + module MessageEndpoints + # @!discord_api https://discord.com/developers/docs/resources/message#get-channel-messages + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param around [Integer, String] Messages around this ID. + # @param before [Integer, String] Messages before this ID. + # @param after [Integer, String] Messages after this ID. + # @param limit [Integer] 1-100 max number of messages to get. + # @return [Array>] + def get_channel_messages(channel_id, around: :undef, before: :undef, after: :undef, limit: :undef, **params) + request Route[:GET, "/channels/#{channel_id}/messages", channel_id], + params: filter_undef({ around: around, before: before, after: after, limit: limit, **params }) + end + + # @!discord_api https://discord.com/developers/docs/resources/message#get-channel-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @return [Hash] + def get_channel_message(channel_id, message_id, **params) + request Route[:GET, "/channels/#{channel_id}/message/#{message_id}", channel_id], + params: params + end + + # rubocop:disable Style/MapToHash + # @!discord_api https://discord.com/developers/docs/resources/message#create-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param content [String] Message content up to 2,000 characters. + # @param nonce [Integer, String] Unique number Used to verifiy if a message was sent. + # @param tts [Boolean] Whether this is a TTS message. + # @param files [File] File contents being sent. + # @param embeds [Array] Up to 10 embed objects to include. + # @param allowed_mentions [Hash] An allowed mentions object. + # @param message_reference [Hash] Whether this message should be a reply or forward. + # @param components [Array] Message components to include. + # @param sticker_ids [Array] ID of up to 3 stickers. + # @param flags [Integer] Bitfield value of message flags. + # @param enforce_nonce [Boolean] Whether the nonce should be enforced. + # @param poll [Hash] A poll request object. + # @return [Hash] + def create_message(channel_id, + content: :undef, nonce: :undef, tts: :undef, files: :undef, embeds: :undef, allowed_mentions: :undef, + message_reference: :undef, components: :undef, sticker_ids: :undef, flags: :undef, + enforce_nonce: :undef, poll: :undef, **rest) + body = filter_undef({ + content: content, + nonce: nonce, + tts: tts, + embeds: embeds, + allowed_mentions: allowed_mentions, + message_reference: message_reference, + components: components, + sticker_ids: sticker_ids, + flags: flags, + enforce_nonce: enforce_nonce, + poll: poll, + **rest + }) + + if files + files = files.zip(0...files.count).map { |file, index| ["file[#{index}]", file] }.to_h + body = { **files, payload_json: JSON.dump(body) } + end + + request Route[:POST, "/channels/#{channel_id}/messages", channel_id], + body: body + end + # rubocop:enable Style/MapToHash + + # @!discord_api https://discord.com/developers/docs/resources/message#crosspost-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @return [Hash] + def crosspost_message(channel_id, message_id, **rest) + request Route[:POST, "/channels/#{channel_id}/messages/#{message_id}/crosspost", channel_id], + body: rest + end + + # @!discord_api https://discord.com/developers/docs/resources/message#create-reaction + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param emoji [String] An URL encoded emoji. Custom emojis should be in the name:id format. + # @return [nil] + def create_reaction(channel_id, message_id, emoji) + request Route[:PUT, "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/@me", channel_id], + body: '' + end + + # @!discord_api https://discord.com/developers/docs/resources/message#delete-own-reaction + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param emoji [String] An URL encoded emoji. Custom emojis should be in the name:id format. + # @return [nil] + def delete_own_reaction(channel_id, message_id, emoji) + request Route[:DELETE, "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/@me", channel_id] + end + + # @!discord_api https://discord.com/developers/docs/resources/message#delete-user-reaction + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param emoji [String] An URL encoded emoji. Custom emojis should be in the name:id format. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @return [nil] + def delete_user_reaction(channel_id, message_id, emoji, user_id) + request Route[:DELETE, + "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/#{user_id}", + channel_id] + end + + # @!discord_api https://discord.com/developers/docs/resources/message#get-reactions + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param emoji [String] An URL encoded emoji. Custom emojis should be in the name:id format. + # @param after [Integer, String] Users after this user ID. + # @param limit [Integer] 1-100 max number of users to get. + # @param type [Integer] Type of reaction. + # @return [Array>] + def get_reactions(channel_id, message_id, emoji, after: :undef, limit: :undef, type: :undef, **params) + query = { + after: after, + limit: limit, + type: type, + **params + } + + request Route[:GET, "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}", channel_id], + params: filter_undef(query) + end + + # @!discord_api https://discord.com/developers/docs/resources/message#delete-all-reactions + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @return [nil] + def delete_all_reactions(channel_id, message_id) + request Route[:DELETE, "/channels/#{channel_id}/messages/#{message_id}/reactions", channel_id] + end + + # @!discord_api https://discord.com/developers/docs/resources/message#delete-all-reactions-for-emoji + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param emoji [String] An URL encoded emoji. Custom emojis should be in the name:id format. + # @return [nil] + def delete_all_reactions_for_emoji(channel_id, message_id, emoji) + request Route[:DELETE, "/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}", channel_id] + end + + # @!discord_api https://discord.com/developers/docs/resources/message#edit-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param content [String] Message content up to 2,000 characters. + # @param embeds [Array] Up to 10 embed objects to add. + # @param flags [Integer] Bitfield value of message flags. + # @param files [File] File contents being added. + # @param allowed_mentions [Hash] An allowed mentions object. + # @param components [Array] Message components to add. + # @return [Hash] + def edit_message(channel_id, + message_id, content: :undef, embeds: :undef, flags: :undef, + files: :undef, allowed_mentions: :undef, components: :undef, + **rest) + body = filter_undef({ + content: content, + tts: tts, + embeds: embeds, + allowed_mentions: allowed_mentions, + message_reference: message_reference, + components: components, + flags: flags, + **rest + }) + + body = { files: files, payload_json: JSON.dump(body) } if files + + request Route[:PATCH, "/channels/#{channel_id}/messages/#{message_id}", channel_id], + body: body + end + + # @!discord_api https://discord.com/developers/docs/resources/message#delete-message + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param reason [String] The reason for deleting this message. + # @return [nil] + def delete_message(channel_id, message_id, reason: :undef) + request Route[:DELETE, "/channels/#{channel_id}/messages/#{message_id}"], + reason: reason + end + + # @!discord_api https://discord.com/developers/docs/resources/message#bulk-delete-messages + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param messages [Array] An array containing 2-100 message ID's to delete. + # @param reason [String] The reason for deleting these messages. + # @return [nil] + def bulk_delete_messages(channel_id, messages, reason: :undef) + request Route[:POST, "/channels/#{channel_id}/messages/bulk-delete", channel_id], + body: messages, + reason: reason + end + end + end +end diff --git a/lib/discordrb/api/endpoints/poll.rb b/lib/discordrb/api/endpoints/poll.rb new file mode 100644 index 000000000..1e81bd391 --- /dev/null +++ b/lib/discordrb/api/endpoints/poll.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Discordrb + module API + # @!discord_api https://discord.com/developers/docs/resources/poll + module PollEndpoints + # @!discord_api https://discord.com/developers/docs/resources/poll#get-answer-voters + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param answer_id [Integer, String] An ID that uniquely identifies a poll answer. + # @param after [Integer, String] Gets users after this user ID. + # @param limit [Integer] Max number of users between 1-100 to return. + # @return [Array>] + def get_answer_voters(channel_id, message_id, answer_id, after: :undef, limit: :undef, **params) + request Route[:GET, "/channels/#{channel_id}/polls/#{message_id}/answer/#{answer_id}", channel_id], + params: filter_undef({ after: after, limit: limit, **params }) + end + + # @!discord_api https://discord.com/developers/docs/resources/poll#end-poll + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @return [Array>] + def end_poll(channel_id, message_id, **rest) + request Route[:POST, "/channels/#{channel_id}/polls/#{message_id}/expire", channel_id], + body: rest + end + end + end +end diff --git a/lib/discordrb/api/endpoints/sku.rb b/lib/discordrb/api/endpoints/sku.rb new file mode 100644 index 000000000..cf96c2bb2 --- /dev/null +++ b/lib/discordrb/api/endpoints/sku.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Discordrb + module API + # @!discord_api https://discord.com/developers/docs/resources/sku + module SkuEndpoints + # @!discord_api https://discord.com/developers/docs/resources/sku#list-skus + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @return [Array>] + def list_skus(application_id, **params) + request Route[:GET, "/applications/#{application_id}/skus", application_id], + params: params + end + end + end +end diff --git a/lib/discordrb/api/endpoints/soundboard.rb b/lib/discordrb/api/endpoints/soundboard.rb new file mode 100644 index 000000000..74e6f4fec --- /dev/null +++ b/lib/discordrb/api/endpoints/soundboard.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module Discordrb + module API + # @!discord_api https://discord.com/developers/docs/resources/soundboard + module SoundboardEndpoints + # @!discord_api https://discord.com/developers/docs/resources/soundboard#send-soundboard-sound + # @param channel_id [String, Integer] An ID that uniquely identifies a channel. + # @param sound_id [String, Integer] ID of the sound to play. + # @param source_guild_id [String, Integer] ID of the guild the sound is from. + # @return [nil] + def send_soundboard_sound(channel_id, sound_id, source_guild_id: :undef, **rest) + request Route[:POST, "/channels/#{channel_id}/send-soundboard-sound", channel_id], + body: filter_undef({ sound_id: sound_id, source_guild_id: source_guild_id, **rest }) + end + + # @!discord_api https://discord.com/developers/docs/resources/soundboard#list-default-soundboard-sounds + # @return [Array>] + def list_default_soundboard_sounds(**params) + request Route[:GET, '/soundboard-default-sounds'], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/soundboard#list-guild-soundboard-sounds + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @return [Array>] + def list_guild_soundboard_sounds(guild_id, **params) + request Route[:GET, "/guilds/#{guild_id}/soundboard-sounds"], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/soundboard#get-guild-soundboard-sound + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param sound_id [Integer, String] An ID that uniquely identifies a soundboard sound. + # @return [Hash] + def get_guild_soundboard_sound(guild_id, sound_id, **params) + request Route[:GET, "/guilds/#{guild_id}/soundboard-sounds/#{sound_id}"], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/soundboard#create-guild-soundboard-sound + # @param guild_id [String, Integer] An ID that uniquely identifies a channel. + # @param name [String] 2-32 character name of the soundboard sound. + # @param sound [String, #read] A base64 encoded string with the sound data. + # @param volume [Integer] 0-1 volume of the sound. + # @param emoji_id [String, Integer] ID of the custom emoji for this sound. + # @param emoji_name [String] Unicode character of the standard emoji for this sound. + # @param reason [String] The reason for creating this soundboard sound. + # @return [Hash] + def create_guild_soundboard_sound(guild_id, name:, sound:, volume: :undef, + emoji_id: :undef, emoji_name: :undef, reason: :undef, + **rest) + data = { + name: name, + sound: sound, + volume: volume, + emoji_id: emoji_id, + emoji_name: emoji_name, + **rest + } + + request Route[:POST, "/guilds/#{guild_id}/soundboard-sounds", guild_id], + body: filter_undef(data), + reason: reason + end + + # @!discord_api https://discord.com/developers/docs/resources/soundboard#modify-guild-soundboard-sound + # @param guild_id [String, Integer] An ID that uniquely identifies a channel. + # @param sound_id [String, Integer] An ID that uniquely identifies a soundboard sound. + # @param name [String] 2-32 character name of the soundboard sound. + # @param volume [Integer] 0-1 volume of the sound. + # @param emoji_id [String, Integer] ID of the custom emoji for this sound. + # @param emoji_name [String] Unicode character of the standard emoji for this sound. + # @param reason [String] The reason for modifiying this soundboard sound. + # @return [Hash] + def modifiy_guild_soundboard_sound(guild_id, sound_id, name: :undef, volume: :undef, + emoji_id: :undef, emoji_name: :undef, reason: :undef, + **rest) + data = { + name: name, + volume: volume, + emoji_id: emoji_id, + emoji_name: emoji_name, + **rest + } + + request Route[:PATCH, "/guilds/#{guild_id}/soundboard-sounds/#{sound_id}", guild_id], + body: filter_undef(data), + reason: reason + end + + # @!discord_api https://discord.com/developers/docs/resources/soundboard#delete-guild-soundboard-sound + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param sound_id [Integer, String] An ID that uniquely identifies a soundboard sound. + # @param reason [String] The reason for deleting this soundboard sound. + # @return [Hash] + def delete_guild_soundboard_sound(guild_id, sound_id, reason: :undef) + request Route[:DELETE, "/guilds/#{guild_id}/soundboard-sounds/#{sound_id}"], + reason: reason + end + end + end +end diff --git a/lib/discordrb/api/endpoints/stage_instance.rb b/lib/discordrb/api/endpoints/stage_instance.rb index 677b0d117..cf39932ab 100644 --- a/lib/discordrb/api/endpoints/stage_instance.rb +++ b/lib/discordrb/api/endpoints/stage_instance.rb @@ -5,12 +5,21 @@ module API # @!discord_api https://discord.com/developers/docs/resources/stage-instance module StageInstanceEndpoints # @!discord_api https://discord.com/developers/docs/resources/stage-instance#create-stage-instance + # @param channel_id [Integer, String] An ID of a stage channel. + # @param topic [String] 1-120 character description; + # @param privacy_level [Integer] Who this stage instance is visible to. + # @param send_start_notification [Boolean] Whether @everyone should be pinged when the stage instance begins. + # @param guild_scheduled_event_id [Integer, String] A scheduled event associated with this stage instance. + # @param reason [String] The reason for creating this stage instance. # @return [Hash] - def create_stage_instance(channel_id:, topic:, privacy_level: :undef, reason: :undef, **rest) + def create_stage_instance(channel_id, topic:, privacy_level: :undef, send_start_notification: :undef, + guild_scheduled_event_id: :undef, reason: :undef, **rest) data = { channel_id: channel_id, topic: topic, privacy_level: privacy_level, + send_start_notification: send_start_notification, + guild_scheduled_event_id: guild_scheduled_event_id, **rest } @@ -20,6 +29,7 @@ def create_stage_instance(channel_id:, topic:, privacy_level: :undef, reason: :u end # @!discord_api https://discord.com/developers/docs/resources/stage-instance#get-stage-instance + # @param channel_id [Integer, String] An ID of a stage channel. # @return [Hash] def get_stage_instance(channel_id, **params) request Route[:GET, "/stage-instances/#{channel_id}", channel_id], @@ -27,6 +37,10 @@ def get_stage_instance(channel_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/stage-instance#modify-stage-instance + # @param channel_id [Integer, String] An ID of a stage channel. + # @param topic [String] 1-120 character description. + # @param privacy_level [Integer] Who this stage instance is visible to. + # @param reason [String] The reason for modifiying this stage instance. # @return [Hash] def modify_stage_instance(channel_id, topic: :undef, privacy_level: :undef, reason: :undef, **rest) data = { @@ -41,6 +55,8 @@ def modify_stage_instance(channel_id, topic: :undef, privacy_level: :undef, reas end # @!discord_api https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance + # @param channel_id [Integer, String] An ID of a stage channel. + # @param reason [String] The reason for deleting this stage instance. # @return [untyped] def delete_stage_instance(channel_id, reason: :undef) request Route[:DELETE, "/stage-instances/#{channel_id}", channel_id], diff --git a/lib/discordrb/api/endpoints/sticker.rb b/lib/discordrb/api/endpoints/sticker.rb index 56da2ac50..c20869b48 100644 --- a/lib/discordrb/api/endpoints/sticker.rb +++ b/lib/discordrb/api/endpoints/sticker.rb @@ -5,6 +5,7 @@ module API # @!discord_api https://discord.com/developers/docs/resources/sticker module StickerEndpoints # @!discord_api https://discord.com/developers/docs/resources/sticker#get-sticker + # @param sticker_id [String, Integer] An ID that uniquely identifies a sticker. # @return [Hash] def get_sticker(sticker_id, **params) request Route[:GET, "/stickers/#{sticker_id}"], @@ -19,6 +20,7 @@ def list_nitro_sticker_packs(**params) end # @!discord_api https://discord.com/developers/docs/resources/sticker#list-guild-stickers + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def list_guild_stickers(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/stickers", guild_id], @@ -26,6 +28,8 @@ def list_guild_stickers(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/sticker#get-guild-sticker + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param sticker_id [String, Integer] An ID that uniquely identifies a sticker. # @return [Hash] def get_guild_sticker(guild_id, sticker_id, **params) request Route[:GET, "/guilds/#{guild_id}/stickers/#{sticker_id}", guild_id], @@ -33,6 +37,12 @@ def get_guild_sticker(guild_id, sticker_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/sticker#create-guild-sticker + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param name [String] Name of the sticker. + # @param description [String] Description of the sticker. + # @param tags [string] Autocomplete tags, max 200 characters. + # @param file [File] PNG, APNG, GIF, or Lottie file. + # @param reason [String] The reason the for creating sticker. # @return [Hash] def create_guild_sticker(guild_id, name:, description:, tags:, file:, reason: :undef, **rest) data = { @@ -49,6 +59,12 @@ def create_guild_sticker(guild_id, name:, description:, tags:, file:, reason: :u end # @!discord_api https://discord.com/developers/docs/resources/sticker#modify-guild-sticker + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param sticker_id [String, Integer] An ID that uniquely identifies a sticker. + # @param name [String] Name of the sticker. + # @param description [String] Description of the sticker. + # @param tags [string] Autocomplete tags, max 200 characters. + # @param reason [String] The reason the for modifiying this sticker. # @return [Hash] def modify_guild_sticker(guild_id, sticker_id, name: :undef, description: :undef, tags: :undef, reason: :undef, **rest) @@ -67,11 +83,13 @@ def modify_guild_sticker(guild_id, sticker_id, end # @!discord_api https://discord.com/developers/docs/resources/sticker#delete-guild-sticker - # @return nil + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param sticker_id [String, Integer] An ID that uniquely identifies a sticker. + # @return [nil] def delete_guild_sticker(guild_id, sticker_id, reason: :undef) request Route[:PATCH, "/guilds/#{guild_id}/stickers/#{sticker_id}", guild_id], reason: reason end end end -end \ No newline at end of file +end diff --git a/lib/discordrb/api/endpoints/user.rb b/lib/discordrb/api/endpoints/user.rb index 7926a610c..ee64a0a7c 100644 --- a/lib/discordrb/api/endpoints/user.rb +++ b/lib/discordrb/api/endpoints/user.rb @@ -11,7 +11,7 @@ def get_current_user(**params) end # @!discord_api https://discord.com/developers/docs/resources/user#get-user - # @param user_id [Integer, String] + # @param user_id [Integer, String] An ID that uniquely identifies a user. # @return [Hash] def get_user(user_id, **params) request Route[:GET, "/users/#{user_id}"], @@ -19,8 +19,8 @@ def get_user(user_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/user#modify-current-user - # @param username [String] - # @param avatar [String, nil] + # @param username [String] New name of the current user. May randomize the discriminator. + # @param avatar [String, read] A base64 encoded string with the image data. # @return [Hash] def modify_current_user(username: :undef, avatar: :undef, **rest) request Route[:PATCH, '/users/@me'], @@ -35,14 +35,14 @@ def get_current_user_guilds(**params) end # @!discord_api https://discord.com/developers/docs/resources/user#leave-guild - # @param guild_id [Integer, String] + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Hash] def leave_guild(guild_id) request Route[:DELETE, "/users/@me/guilds/#{guild_id}"] end # @!discord_api https://discord.com/developers/docs/resources/user#create-dm - # @param recipient_id [Integer, String] + # @param recipient_id [Integer, String] The user to open a DM with. # @return [Hash] def create_dm(recipient_id, **rest) request Route[:POST, '/users/@me/channels'], body: { recipient_id: recipient_id, **rest } @@ -53,6 +53,32 @@ def create_dm(recipient_id, **rest) def get_current_user_connections(**params) request Route[:GET, '/users/@me/connections'], params: params end + + # @!discord_api https://discord.com/developers/docs/resources/user#get-current-user-application-role-connection + # @return [Hash] + def get_current_user_application_role_connections(application_id, **params) + request Route[:GET, "/users/@me/applications/#{application_id}/role-connection"], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/user#update-current-user-application-role-connection + # @param application_id [Integer, String] An ID that uniquely identifies an application. + # @param platform_name [String] Max 50 character name of a connected platform. + # @param platform_username [String] Max 100 character username of a connected platform. + # @param metadata [Hash] Max 100 character Role connection metadata keys for a connected platform. + # @return [Hash] + def update_current_user_application_role_connections(application_id, platform_name: :undef, platform_username: :undef, + metadata: :undef, **params) + data = { + platform_name: platform_name, + platform_username: platform_username, + metadata: metadata, + **params + } + + request Route[:PUT, "/users/@me/applications/#{application_id}/role-connection"], + body: filter_undef(data) + end end end end diff --git a/lib/discordrb/api/endpoints/voice.rb b/lib/discordrb/api/endpoints/voice.rb index 1e3ecae62..d60d36c17 100644 --- a/lib/discordrb/api/endpoints/voice.rb +++ b/lib/discordrb/api/endpoints/voice.rb @@ -10,6 +10,58 @@ def list_voice_regions(**params) request Route[:GET, '/voice/regions'], params: params end + + # @!discord_api https://discord.com/developers/docs/resources/voice#get-current-user-voice-state + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @return [Array>] + def get_current_user_voice_state(guild_id, **params) + request Route[:GET, "/guilds/#{guild_id}/voice-states/@me"], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/voice#get-user-voice-state + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @return [Array>] + def get_user_voice_state(guild_id, user_id, **params) + request Route[:GET, "/guilds/#{guild_id}/voice-states/#{user_id}"], + params: params + end + + # @!discord_api https://discord.com/developers/docs/resources/voice#modify-current-user-voice-state + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param suppress [Boolean] Whether the current user should be suppressed. + # @param request_to_speak_timestamp [Time] User's request to speak timestamp. + # @return [nil] + def modify_current_user_voice_state(guild_id, channel_id: :undef, suppress: :undef, + request_to_speak_timestamp: :undef, **rest) + data = { + channel_id: channel_id, + suppress: suppress, + request_to_speak_timestamp: request_to_speak_timestamp, + **rest + } + + request Route[:PATCH, "/guilds/#{guild_id}/voice-states/@me", guild_id], + body: filter_undef(data) + end + + # @!discord_api https://discord.com/developers/docs/resources/voice#modify-user-voice-state + # @param channel_id [Integer, String] An ID that uniquely identifies a channel. + # @param user_id [Integer, String] An ID that uniquely identifies a user. + # @param suppress [Boolean] Whether the user should be suppressed. + # @return [nil] + def modify_user_voice_state(guild_id, user_id, channel_id: :undef, suppress: :undef, **rest) + data = { + channel_id: channel_id, + suppress: suppress, + **rest + } + + request Route[:PATCH, "/guilds/#{guild_id}/voice-states/#{user_id}", guild_id], + body: filter_undef(data) + end end end end diff --git a/lib/discordrb/api/endpoints/webhook.rb b/lib/discordrb/api/endpoints/webhook.rb index 2ead352a8..ef99c14de 100644 --- a/lib/discordrb/api/endpoints/webhook.rb +++ b/lib/discordrb/api/endpoints/webhook.rb @@ -5,6 +5,10 @@ module API # @!discord_api https://discord.com/developers/docs/resources/webhook module WebhookEndpoints # @!discord_api https://discord.com/developers/docs/resources/webhook#create-webhook + # @param channel_id [String, Integer] An ID that uniquely identifies a channel. + # @param name [String] 1-80 character name of the webhook to create. + # @param avatar [String, #read] A base64 encoded string with the image data. + # @param reason [String] The reason for creating this webhook. # @return [Hash] def create_webhook(channel_id, name: :undef, avatar: :undef, reason: :undef, **rest) request Route[:POST, "/channels/#{channel_id}/webhooks", channel_id], @@ -13,6 +17,7 @@ def create_webhook(channel_id, name: :undef, avatar: :undef, reason: :undef, **r end # @!discord_api https://discord.com/developers/docs/resources/webhook#get-channel-webhooks + # @param channel_id [String, Integer] An ID that uniquely identifies a channel. # @return [Array>] def get_channel_webhooks(channel_id, **params) request Route[:GET, "/channels/#{channel_id}/webhooks", channel_id], @@ -20,6 +25,7 @@ def get_channel_webhooks(channel_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/webhook#get-guild-webhooks + # @param guild_id [Integer, String] An ID that uniquely identifies a guild. # @return [Array>] def get_guild_webhooks(guild_id, **params) request Route[:GET, "/guilds/#{guild_id}/webhooks", guild_id], @@ -27,6 +33,7 @@ def get_guild_webhooks(guild_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/webhook#get-webhook + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. # @return [Hash] def get_webhook(webhook_id, **params) request Route[:GET, "/webhooks/#{webhook_id}", webhook_id], @@ -34,6 +41,8 @@ def get_webhook(webhook_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/webhook#get-webhook-with-token + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param webhook_token [String] The secure token of the webhook. # @return [Hash] def get_webhook_with_token(webhook_id, webhook_token, **params) request Route[:GET, "/webhooks/#{webhook_id}/#{webhook_token}", webhook_id, :get_webhooks_id_token], @@ -41,6 +50,10 @@ def get_webhook_with_token(webhook_id, webhook_token, **params) end # @!discord_api https://discord.com/developers/docs/resources/webhook#modify-webhook + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param name [String] Default name of the webhook. + # @param avatar [String, #read] A base64 encoded string with the image data. + # @param channel_id [String, Integer] New channel ID to move this webhook to. # @return [Hash] def modify_webhook(webhook_id, name: :undef, avatar: :undef, channel_id: :undef, **rest) request Route[:PATCH, "/webhooks/#{webhook_id}", webhook_id], @@ -48,13 +61,19 @@ def modify_webhook(webhook_id, name: :undef, avatar: :undef, channel_id: :undef, end # @!discord_api https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param webhook_token [String] The secure token of the webhook. + # @param name [String] Default name of the webhook. + # @param avatar [String, #read] A base64 encoded string with the image data. # @return [Hash] - def modify_webhook_with_token(webhook_id, webhook_token, name: :undef, avatar: :undef, channel_id: :undef, **rest) + def modify_webhook_with_token(webhook_id, webhook_token, name: :undef, avatar: :undef, **rest) request Route[:PATCH, "/webhooks/#{webhook_id}/#{webhook_token}", webhook_id, :patch_webhooks_id_token], body: filter_undef({ name: name, avatar: avatar, channel_id: channel_id, **rest }) end # @!discord_api https://discord.com/developers/docs/resources/webhook#delete-webhook + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param reason [String] The reason for deleting this webhook. # @return [nil] def delete_webhook(webhook_id, reason: :undef) request Route[:DELETE, "/webhooks/#{webhook_id}", webhook_id], @@ -62,6 +81,9 @@ def delete_webhook(webhook_id, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param webhook_token [String] The secure token of the webhook. + # @param reason [String] The reason for deleting this webhook. # @return [nil] def delete_webhook_with_token(webhook_id, webhook_token, reason: :undef) request Route[:DELETE, "/webhooks/#{webhook_id}/#{webhook_token}", webhook_id, :delete_webhook_id_token], @@ -69,18 +91,23 @@ def delete_webhook_with_token(webhook_id, webhook_token, reason: :undef) end # @!discord_api https://discord.com/developers/docs/resources/webhook#execute-webhook + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param webhook_token [String] The secure token of the webhook. # @return [nil, Hash] def execute_webhook(webhook_id, webhook_token, wait: :undef, thread_id: :undef, content: :undef, username: :undef, - avatar_url: :undef, tts: :undef, file: :undef, embeds: :undef, allowed_mentions: :undef, - components: :undef, params: {}, **rest) + avatar_url: :undef, tts: :undef, file: :undef, flags: :undef, embeds: :undef, allowed_mentions: :undef, + components: :undef, applied_tags: :undef, poll: :undef, params: {}, **rest) data = { content: content, username: username, avatar_url: avatar_url, tts: tts, + flags: flags, embeds: embeds, allowed_mentions: allowed_mentions, components: components, + applied_tags: applied_tags, + poll: poll, **rest } @@ -92,6 +119,9 @@ def execute_webhook(webhook_id, webhook_token, wait: :undef, thread_id: :undef, end # @!discord_api https://discord.com/developers/docs/resources/webhook#get-webhook-message + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param webhook_token [String] The secure token of the webhook. + # @param message_id [Integer, String] An ID that uniquely identifies a message. # @return [Hash] def get_webhook_message(webhook_id, webhook_token, message_id, **params) request Route[:GET, "/webhooks/#{webhook_id}/#{webhook_token}/messages/#{message_id}", webhook_id, :get_webhooks_id_token_messages_id], @@ -99,6 +129,15 @@ def get_webhook_message(webhook_id, webhook_token, message_id, **params) end # @!discord_api https://discord.com/developers/docs/resources/webhook#edit-webhook-message + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param webhook_token [String] The secure token of the webhook. + # @param message_id [Integer, String] An ID that uniquely identifies a message. + # @param content [String] Message content up to 2,000 characters. + # @param embeds [Array] Up to 10 embed objects to include. + # @param file [File] File contents being sent. + # @param allowed_mentions [Hash] An allowed mentions object. + # @param attachments [File] Attatched files. + # @param components [Array] An array of message components to include. # @return [Hash] def edit_webhook_message(webhook_id, webhook_token, message_id, content: :undef, embeds: :undef, file: :undef, allowed_mentions: :undef, attachments: :undef, components: :undef, **rest) @@ -118,6 +157,9 @@ def edit_webhook_message(webhook_id, webhook_token, message_id, content: :undef, end # @!discord_api https://discord.com/developers/docs/resources/webhook#delete-webhook-message + # @param webhook_id [Integer, String] An ID that uniquely identifies a webhook. + # @param webhook_token [String] The secure token of the webhook. + # @param message_id [Integer, String] An ID that uniquely identifies a message. # @return [nil] def delete_webhook_message(webhook_id, webhook_token, message_id) request Route[:DELETE, "/webhooks/#{webhook_id}/#{webhook_token}/messages/#{message_id}", webhook_id, :delete_webhooks_id_token_messages_id] diff --git a/lib/discordrb/bot.rb b/lib/discordrb/bot.rb index f829c410d..81509dd0e 100644 --- a/lib/discordrb/bot.rb +++ b/lib/discordrb/bot.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'zlib' -require 'set' require 'discordrb/api/client' require 'discordrb/events/message' @@ -20,7 +19,7 @@ require 'discordrb/events/webhooks' require 'discordrb/events/invites' require 'discordrb/events/interactions' - +require 'discordrb/events/threads' require 'discordrb/errors' require 'discordrb/data' @@ -82,8 +81,6 @@ class Bot # you're trying to log in as a bot. # @param client_id [Integer] If you're logging in as a bot, the bot's client ID. This is optional, and may be fetched # from the API by calling {Bot#bot_application} (see {Application}). - # @param type [Symbol] This parameter lets you manually overwrite the account type. This needs to be set when - # logging in as a user, otherwise discordrb will treat you as a bot account. Valid values are `:user` and `:bot`. # @param name [String] Your bot's name. This will be sent to Discord with any API requests, who will use this to # trace the source of excessive API requests; it's recommended to set this to something if you make bots that many # people will host on their servers separately. @@ -110,7 +107,7 @@ class Bot def initialize( log_mode: :normal, token: nil, client_id: nil, - type: nil, name: '', fancy_log: false, suppress_ready: false, parse_self: false, + name: '', fancy_log: false, suppress_ready: false, parse_self: false, shard_id: nil, num_shards: nil, redact_token: true, ignore_bots: false, compress_mode: :large, intents: :all ) @@ -121,7 +118,6 @@ def initialize( @client_id = client_id - @type = type || :bot @name = name @shard_key = num_shards ? [shard_id, num_shards] : nil @@ -144,7 +140,7 @@ def initialize( calculate_intents(intents) end - @token = process_token(@type, token) + @token = "Bot #{token.delete_prefix('Bot ')}" @gateway = Gateway.new(self, @token, @shard_key, @compress_mode, @intents) init_cache @@ -181,6 +177,14 @@ def servers @servers end + # The list of members in threads the bot can see. + # @return [Hash Hash Hash Object>>] + def thread_members + gateway_check + unavailable_servers_check + @thread_members + end + # @overload emoji(id) # Return an emoji by its ID # @param id [String, Integer] The emoji's ID. @@ -291,12 +295,18 @@ def connected? # @param server [Server, nil] The server the bot should be invited to, or nil if a general invite should be created. # @param permission_bits [String, Integer] Permission bits that should be appended to invite url. # @return [String] the OAuth invite URL. - def invite_url(server: nil, permission_bits: nil) + def invite_url(server: nil, permission_bits: nil, redirect_uri: nil, scopes: ['bot']) @client_id ||= bot_application.id - server_id_str = server ? "&guild_id=#{server.id}" : '' - permission_bits_str = permission_bits ? "&permissions=#{permission_bits}" : '' - "https://discord.com/oauth2/authorize?&client_id=#{@client_id}#{server_id_str}#{permission_bits_str}&scope=bot" + query = URI.encode_www_form({ + client_id: @client_id, + guild_id: server&.id, + permissions: permission_bits, + redirect_uri: redirect_uri, + scope: scopes.join(' ') + }.compact) + + "https://discord.com/oauth2/authorize?#{query}" end # @return [Hash VoiceBot>] the voice connections this bot currently has, by the server ID to which they are connected. @@ -374,23 +384,24 @@ def delete_invite(code, reason: nil) # @param channel [Channel, String, Integer] The channel, or its ID, to send something to. # @param content [String] The text that should be sent as a message. It is limited to 2000 characters (Discord imposed). # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech. - # @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message. + # @param embeds [Hash, Discordrb::Webhooks::Embed, Array, Array nil] The rich embed(s) to append to this message. # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any. # @param components [View, Array] Interaction components to associate with this message. # @return [Message] The message that was sent. - def send_message(channel, content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil) + def send_message(channel, content, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil) channel = channel.resolve_id debug("Sending message to #{channel} with content '#{content}'") allowed_mentions = { parse: [] } if allowed_mentions == false message_reference = { message_id: message_reference.id } if message_reference.respond_to?(:id) + embeds = (embeds.instance_of?(Array) ? embeds.map(&:to_hash) : [embeds&.to_hash]).compact attachments = attachments&.map do |attachment| attachment.is_a?(UploadIO) ? attachment : file_to_file_part(attachment) end resp = @client.create_message(channel, **{ - content: content, tts: tts, files: attachments, embed: embed&.to_hash, + content: content, tts: tts, files: attachments, embeds: embeds, allowed_mentions: allowed_mentions&.to_hash, message_reference: message_reference, components: components }.compact) Message.new(resp, self) @@ -402,16 +413,16 @@ def send_message(channel, content, tts = false, embed = nil, attachments = nil, # @param content [String] The text that should be sent as a message. It is limited to 2000 characters (Discord imposed). # @param timeout [Float] The amount of time in seconds after which the message sent will be deleted. # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech. - # @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message. + # @param embeds [Hash, Discordrb::Webhooks::Embed, Array, Array nil] The rich embed(s) to append to this message. # @param attachments [Array] Files that can be referenced in embeds via `attachment://file.png` # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any. # @param components [View, Array] Interaction components to associate with this message. - def send_temporary_message(channel, content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil) + def send_temporary_message(channel, content, timeout, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil) Thread.new do Thread.current[:discordrb_name] = "#{@current_thread}-temp-msg" - message = send_message(channel, content, tts, embed, attachments, allowed_mentions, message_reference, components) + message = send_message(channel, content, tts, embeds, attachments, allowed_mentions, message_reference, components) sleep(timeout) message.delete end @@ -457,17 +468,6 @@ def create_server(name, region = :'eu-central') server end - # Creates a new application to do OAuth authorization with. This allows you to use OAuth to authorize users using - # Discord. For information how to use this, see the docs: https://discord.com/developers/docs/topics/oauth2 - # @deprecated TODO: Remove - # @param name [String] What your application should be called. - # @param redirect_uris [Array] URIs that Discord should redirect your users to after authorizing. - # @return [Array(String, String)] your applications' client ID and client secret to be used in OAuth authorization. - def create_oauth_application(name, redirect_uris) - response = JSON.parse(API.create_oauth_application(@token, name, redirect_uris)) - [response[:id], response[:secret]] - end - # Gets the users, channels, roles and emoji from a string. # @param mentions [String] The mentions, which should look like `<@12314873129>`, `<#123456789>`, `<@&123456789>` or `<:name:126328:>`. # @param server [Server, nil] The server of the associated mentions. (recommended for role parsing, to speed things up) @@ -606,6 +606,36 @@ def invisible update_status(:invisible, @activity, nil) end + # Join a thread + # @param channel [Channel, Integer, String] + def join_thread(channel) + @client.join_thread(channel.resolve_id) + nil + end + + # Leave a thread + # @param channel [Channel, Integer, String] + def leave_thread(channel) + @client.leave_thread(channel.resolve_id) + nil + end + + # Add a member to a thread + # @param channel [Channel, Integer, String] + # @param member [Member, Integer, String] + def add_thread_member(channel, member) + @client.add_thread_member(channel.resolve_id, member.resolve_id) + nil + end + + # Remove a member from a thread + # @param channel [Channel, Integer, String] + # @param member [Member, Integer, String] + def remove_thread_member(channel, member) + @client.remove_thread_member(channel.resolve_id, member.resolve_id) + nil + end + # Sets debug mode. If debug mode is on, many things will be outputted to STDOUT. def debug=(new_debug) LOGGER.debug = new_debug @@ -744,7 +774,22 @@ def get_application_command(command_id, server_id: nil) ApplicationCommand.new(resp, self, server_id) end + # Get all application commands. + # @param server_id [String, Integer, nil] The ID of the server to get the commands from. Global if `nil`. + # @return [Array] + def get_application_commands(server_id: nil) + resp = if server_id + @client.get_guild_application_commands(profile.id, server_id) + else + @client.get_global_application_commands(profile.id) + end + resp.map do |command_data| + ApplicationCommand.new(command_data, self, server_id) + end + end + # @yieldparam [OptionBuilder] + # @yieldparam [PermissionBuilder] # @example # bot.register_application_command(:reddit, 'Reddit Commands') do |cmd| # cmd.subcommand_group(:subreddit, 'Subreddit Commands') do |group| @@ -757,15 +802,18 @@ def get_application_command(command_id, server_id: nil) # end # end # end - def register_application_command(name, description, server_id: nil, default_permission: nil, type: :chat_input) + def register_application_command(name, description, server_id: nil, type: :chat_input, default_member_permissions: nil, contexts: nil, integration_types: nil, name_localizations: nil, description_localizations: nil) type = ApplicationCommand::TYPES[type] || type builder = Interactions::OptionBuilder.new - yield(builder) if block_given? + permission_builder = Interactions::PermissionBuilder.new + yield(builder, permission_builder) if block_given? params = { name: name, description: description, options: builder.to_a, - default_permission: default_permission, type: type + default_member_permissions: default_member_permissions, type: type, + contexts: contexts, integration_types: integration_types, + name_localizations: name_localizations, description_localizations: description_localizations }.compact resp = if server_id @@ -773,17 +821,30 @@ def register_application_command(name, description, server_id: nil, default_perm else @client.create_global_application_command(profile.id, **params) end - ApplicationCommand.new(resp, self, server_id) + cmd = ApplicationCommand.new(resp, self, server_id) + + if permission_builder.to_a.any? + raise ArgumentError, 'Permissions can only be set for guild commands' unless server_id + + edit_application_command_permissions(cmd.id, server_id, permission_builder.to_a) + end + + cmd end - def edit_application_command(command_id, server_id: nil, name: nil, description: nil, default_permission: nil, type: :chat_input) + # @yieldparam [OptionBuilder] + # @yieldparam [PermissionBuilder] + def edit_application_command(command_id, server_id: nil, name: nil, description: nil, type: :chat_input, default_member_permissions: nil, contexts: nil, integration_types: nil, name_localizations: nil, description_localizations: nil) type = ApplicationCommand::TYPES[type] || type builder = Interactions::OptionBuilder.new - yield(builder) if block_given? + permission_builder = Interactions::PermissionBuilder.new + yield(builder, permission_builder) if block_given? params = { - name: name, description: description, default_permission: default_permission, type: type + name: name, description: description, default_member_permissions: default_member_permissions, type: type, + contexts: contexts, integration_types: integration_types, name_localizations: name_localizations, + description_localizations: description_localizations }.compact resp = if server_id @@ -791,7 +852,15 @@ def edit_application_command(command_id, server_id: nil, name: nil, description: else @client.edit_global_application_command(profile.id, command_id, **params) end - ApplicationCommand.new(resp, self, server_id) + cmd = ApplicationCommand.new(resp, self, server_id) + + if permission_builder.to_a.any? + raise ArgumentError, 'Permissions can only be set for guild commands' unless server_id + + edit_application_command_permissions(cmd.id, server_id, permission_builder.to_a) + end + + cmd end # Remove an application command from the commands registered with discord. @@ -805,10 +874,21 @@ def delete_application_command(command_id, server_id: nil) end end + # @param command_id [Integer, String] + # @param server_id [Integer, String] + # @param permissions [Array] An array of objects formatted as `{ id: ENTITY_ID, type: 1 or 2, permission: true or false }` + def edit_application_command_permissions(command_id, server_id, permissions = []) + builder = Interactions::PermissionBuilder.new + yield builder if block_given? + + permissions += builder.to_a + @client.edit_application_command_permissions(profile.id, server_id, command_id, permissions) + end + private def file_to_file_part(file, content_type = 'binary/octet-stream', filename = nil) - Faraday::FilePart.new(file, content_type, filename) + Faraday::Multipart::FilePart.new(file, content_type, filename) end # Throws a useful exception if there's currently no gateway connection. @@ -826,13 +906,13 @@ def unavailable_servers_check LOGGER.warn('Servers may be unavailable due to an outage, or your bot is on very large servers that are taking a while to load.') end - ### ## ## ######## ######## ######## ## ## ### ## ###### + #### ## ## ######## ######## ######## ## ## ### ## ###### ## ### ## ## ## ## ## ### ## ## ## ## ## ## ## #### ## ## ## ## ## #### ## ## ## ## ## ## ## ## ## ## ###### ######## ## ## ## ## ## ## ###### ## ## #### ## ## ## ## ## #### ######### ## ## ## ## ### ## ## ## ## ## ### ## ## ## ## ## - ### ## ## ## ######## ## ## ## ## ## ## ######## ###### + #### ## ## ## ######## ## ## ## ## ## ## ######## ###### # Internal handler for PRESENCE_UPDATE def update_presence(data) @@ -860,10 +940,16 @@ def update_presence(data) username = data[:user][:username] if username && !member_is_new # Don't set the username for newly-cached members - debug "Implicitly updating presence-obtained information for member #{user_id}" + debug "Implicitly updating presence-obtained information username for member #{user_id}" member.update_username(username) end + global_name = data[:user][:global_name] + if global_name && !member_is_new # Don't set the global_name for newly-cached members + debug "Implicitly updating presence-obtained information global_name for member #{user_id}" + member.update_global_name(global_name) + end + member.update_presence(data) member.avatar_id = data[:user][:avatar] if data[:user][:avatar] @@ -936,6 +1022,8 @@ def create_channel(data) elsif channel.group? @channels[channel.id] = channel end + + @thread_members.delete(channel.id) if channel.thread? end # Internal handler for CHANNEL_UPDATE @@ -1000,7 +1088,9 @@ def update_guild_member(data) member = server.member(data[:user][:id].to_i) member.update_roles(data[:roles]) member.update_nick(data[:nick]) + member.update_global_name(data[:user][:global_name]) if data[:user][:global_name] member.update_boosting_since(data[:premium_since]) + member.update_communication_disabled_until(data['communication_disabled_until']) end # Internal handler for GUILD_MEMBER_DELETE @@ -1106,14 +1196,6 @@ def remove_user_ban(data); end ## ## ## ## ## ## ## ### ######## ####### ###### #### ## ## - def process_token(type, token) - # Remove the "Bot " prefix if it exists - token = token[4..] if token.start_with? 'Bot ' - - token = "Bot #{token}" unless type == :user - token - end - def handle_dispatch(type, data) # Check whether there are still unavailable servers and there have been more than 10 seconds since READY if @unavailable_servers&.positive? && (Time.now - @unavailable_timeout_time) > 10 && !((@intents || 0) & INTENTS[:servers]).zero? @@ -1473,15 +1555,69 @@ def handle_dispatch(type, data) event = ButtonEvent.new(data, self) raise_event(event) - when Webhooks::View::COMPONENT_TYPES[:select_menu] - event = SelectMenuEvent.new(data, self) + when Webhooks::View::COMPONENT_TYPES[:string_select] + event = StringSelectEvent.new(data, self) + + raise_event(event) + when Webhooks::View::COMPONENT_TYPES[:user_select] + event = UserSelectEvent.new(data, self) + + raise_event(event) + when Webhooks::View::COMPONENT_TYPES[:role_select] + event = RoleSelectEvent.new(data, self) + + raise_event(event) + when Webhooks::View::COMPONENT_TYPES[:mentionable_select] + event = MentionableSelectEvent.new(data, self) + + raise_event(event) + when Webhooks::View::COMPONENT_TYPES[:channel_select] + event = ChannelSelectEvent.new(data, self) raise_event(event) end + when Interaction::TYPES[:modal_submit] + + event = ModalSubmitEvent.new(data, self) + raise_event(event) end when :WEBHOOKS_UPDATE event = WebhookUpdateEvent.new(data, self) raise_event(event) + + when :THREAD_CREATE + create_channel(data) + + event = ThreadCreateEvent.new(data, self) + raise_event(event) + when :THREAD_UPDATE + update_channel(data) + + event = ThreadUpdateEvent.new(data, self) + raise_event(event) + when :THREAD_DELETE + delete_channel(data) + @thread_members.delete(data['id']&.resolve_id) + + # raise ThreadDeleteEvent + when :THREAD_LIST_SYNC + data['members'].map { |member| ensure_thread_member(member) } + data['threads'].map { |channel| ensure_channel(channel, data['guild_id']) } + + # raise ThreadListSyncEvent? + when :THREAD_MEMBER_UPDATE + ensure_thread_member(data) + when :THREAD_MEMBERS_UPDATE + data['added_members']&.each do |added_member| + ensure_thread_member(added_member) if added_member['user_id'] + end + + data['removed_member_ids'].each do |member_id| + @thread_members[data['id']&.resolve_id]&.delete(member_id&.resolve_id) + end + + event = ThreadMembersUpdateEvent.new(data, self) + raise_event(event) else # another event that we don't support yet debug "Event #{type} has been received but is unsupported. Raising UnknownEvent" @@ -1543,7 +1679,7 @@ def call_event(handler, event) def handle_awaits(event) @awaits ||= {} - @awaits.each do |_, await| + @awaits.each_value do |await| key, should_delete = await.match(event) next unless key diff --git a/lib/discordrb/cache.rb b/lib/discordrb/cache.rb index 70d75db8d..c8f8cea8c 100644 --- a/lib/discordrb/cache.rb +++ b/lib/discordrb/cache.rb @@ -17,6 +17,7 @@ def init_cache @channels = {} @pm_channels = {} + @thread_members = {} end # Returns or caches the available voice regions @@ -167,6 +168,15 @@ def ensure_channel(data, server = nil) end end + # Ensures a given thread member object is cached. + # @param data [Hash] Thread member data. + def ensure_thread_member(data) + thread_id = data['id'].to_i + user_id = data['user_id'].to_i + @thread_members[thread_id] ||= {} + @thread_members[thread_id][user_id] = data.slice('join_timestamp', 'flags') + end + # Requests member chunks for a given server ID. # @param id [Integer] The server ID to request chunks for. def request_chunks(id) diff --git a/lib/discordrb/cdn.rb b/lib/discordrb/cdn.rb new file mode 100644 index 000000000..8f0dc623a --- /dev/null +++ b/lib/discordrb/cdn.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +module Discordrb + module API + # https://discord.com/developers/docs/reference#image-formatting + module CDN + # The URL of Discord's CDN. + CDN_URL = 'https://cdn.discordapp.com' + + module_function + + # the currently used CDN url. + # @return [String] + def cdn_url + CDN_URL + end + + # Make an emoji icon URL from emoji ID. + # @return [String] + def emoji_icon_url(emoji_id, format = 'webp') + "#{cdn_url}/emojis/#{emoji_id}.#{format}" + end + + # Make an icon URL from server and icon IDs. + # @return [String] + def icon_url(server_id, icon_id, format = 'webp') + "#{cdn_url}/icons/#{server_id}/#{icon_id}.#{format}" + end + + # Make a splash URL from server and splash IDs. + # @return [String] + def splash_url(server_id, splash_id, format = 'webp') + "#{cdn_url}/splashes/#{server_id}/#{splash_id}.#{format}" + end + + # Make a discovery splash URL from server and splash IDs. + # @return [String] + def discovery_splash_url(server_id, discovery_splash_id, format = 'webp') + "#{cdn_url}/discovery-splashes/#{server_id}/#{discovery_splash_id}.#{format}" + end + + # Make a banner URL from server and banner IDs. + # @return [String] + def banner_url(server_id, banner_id, format = 'webp') + "#{cdn_url}/banners/#{server_id}/#{banner_id}.#{format}" + end + + # Make a user banner URL from user and banner hash. + # @return [String] + def user_banner_url(user_id, banner_hash, format = 'webp') + "#{cdn_url}/banners/#{user_id}/#{banner_hash}.#{format}" + end + + # Make one of the "default" discord avatars given a discriminator or user ID. + # @return [String] + def default_avatar(discrim_id = 0, legacy: false) + index = if legacy + discrim_id.to_i % 5 + else + (discrim_id.to_i >> 22) % 5 + end + "#{Discordrb::API.cdn_url}/embed/avatars/#{index}.png" + end + + # Make a user avatar URL from the user ID and avatar ID. + # @return [String] + def avatar_url(user_id, avatar_id, format = nil) + format ||= if avatar_id.start_with?('a_') + 'gif' + else + 'webp' + end + "#{cdn_url}/embed/avatars/#{user_id}/#{avatar_id}.#{format}" + end + + # Make a guild member avatar URL from the user ID, guild ID, and avatar ID. + # @return [String] + def guild_avatar_url(user_id, guild_id, avatar_id, format = nil) + format ||= if avatar_id.start_with?('a_') + 'gif' + else + 'webp' + end + "#{cdn_url}/guilds/#{guild_id}/users/#{user_id}/avatars/#{avatar_id}.#{format}" + end + + # Make an avatar decoration URL from avatar decoration hash. + # @return [String] + def avatar_decoration_url(decoration_hash) + "#{cdn_url}/avatar-decoration-presets/#{decoration_hash}/.png" + end + + # Make an icon URL from application and icon IDs. + # @return [String] + def app_icon_url(app_id, icon_id, format = 'webp') + "#{cdn_url}/app-icons/#{app_id}/#{icon_id}.#{format}" + end + + # Make an icon URL from application and icon IDs. + # @return [String] + def app_icon_cover(app_id, cover_id, format = 'webp') + "#{cdn_url}/app-icons/#{app_id}/#{cover_id}.#{format}" + end + + # Make an asset URL from application and asset IDs. + # @return [String] + def asset_url(application_id, asset_id, format = 'webp') + "#{cdn_url}/app-assets/#{application_id}/#{asset_id}.#{format}" + end + + # Make a sticker pack banner given the banner ID. + # @return [String] + def sticker_pack_banner_url(banner_id, format = 'webp') + "#{cdn_url}/app-assets/710982414301790216/store#{banner_id}.#{format}" + end + + # Make a team icon URL given the team ID and icon hash. + # @return [String] + def team_icon(team_id, icon_hash, format = 'webp') + "#{cdn_url}/team-icons/#{team_id}/#{icon_hash}.#{format}" + end + + # Make a sticker URL given the sticker ID. + # @return [String] + def sticker_url(sticker_id, format = 'png') + "https://media.discordapp.net/stickers/#{sticker_id}.#{format}" + end + + # Make a role icon URL from role ID and icon hash. + # @return [String] + def role_icon_url(role_id, icon_hash, format = 'webp') + "#{cdn_url}/role-icons/#{role_id}/#{icon_hash}.#{format}" + end + + # Make a scheduled event cover given the event ID and cover image hash. + # @return [String] + def guild_scheduled_event_cover(scheduled_event_id, cover_image_hash, format = 'webp') + "#{cdn_ur}/guild-events/#{scheduled_event_id}/#{cover_image_hash}.#{format}" + end + + # Make a guild member banner URL from user ID, guild ID, and banner hash. + # @return [String] + def guild_user_banner_url(guild_id, user_id, banner_hash, format = 'webp') + "#{cdn_url}/guilds/#{guild_id}/users/#{user_id}/banners/#{banner_hash}.#{format}" + end + + # Make a widget picture URL from server ID. + # @return [String] + def widget_url(server_id, style = 'shield') + "#{api_base}/guilds/#{server_id}/widget.png?style=#{style}" + end + end + end +end diff --git a/lib/discordrb/commands/command_bot.rb b/lib/discordrb/commands/command_bot.rb index ddf68ea37..f2f9117a1 100644 --- a/lib/discordrb/commands/command_bot.rb +++ b/lib/discordrb/commands/command_bot.rb @@ -77,7 +77,6 @@ def initialize(**attributes) log_mode: attributes[:log_mode], token: attributes[:token], client_id: attributes[:client_id], - type: attributes[:type], name: attributes[:name], fancy_log: attributes[:fancy_log], suppress_ready: attributes[:suppress_ready], diff --git a/lib/discordrb/commands/container.rb b/lib/discordrb/commands/container.rb index 0c2b06926..069e6819f 100644 --- a/lib/discordrb/commands/container.rb +++ b/lib/discordrb/commands/container.rb @@ -86,7 +86,7 @@ def remove_command(name) # Adds all commands from another container into this one. Existing commands will be overwritten. # @param container [Module] A module that `extend`s {CommandContainer} from which the commands will be added. def include_commands(container) - handlers = container.instance_variable_get '@commands' + handlers = container.instance_variable_get :@commands return unless handlers @commands ||= {} diff --git a/lib/discordrb/commands/parser.rb b/lib/discordrb/commands/parser.rb index 2d9574690..d96f21778 100644 --- a/lib/discordrb/commands/parser.rb +++ b/lib/discordrb/commands/parser.rb @@ -159,7 +159,7 @@ def execute_bare(event) escaped = false hacky_delim, hacky_space, hacky_prev, hacky_newline = [0xe001, 0xe002, 0xe003, 0xe004].pack('U*').chars - @chain.each_char.each_with_index do |char, index| + @chain.each_char.with_index do |char, index| # Escape character if char == '\\' && !escaped escaped = true diff --git a/lib/discordrb/commands/rate_limiter.rb b/lib/discordrb/commands/rate_limiter.rb index ffdd83c72..c208a2b6a 100644 --- a/lib/discordrb/commands/rate_limiter.rb +++ b/lib/discordrb/commands/rate_limiter.rb @@ -125,7 +125,7 @@ def clean # Adds all the buckets from another RateLimiter onto this one. # @param limiter [Module] Another {RateLimiter} module def include_buckets(limiter) - buckets = limiter.instance_variable_get('@buckets') || {} + buckets = limiter.instance_variable_get(:@buckets) || {} @buckets ||= {} @buckets.merge! buckets end diff --git a/lib/discordrb/container.rb b/lib/discordrb/container.rb index ed6c530d7..5521437f9 100644 --- a/lib/discordrb/container.rb +++ b/lib/discordrb/container.rb @@ -528,8 +528,8 @@ def invite_delete(attributes = {}, &block) # @option attributes [Integer, Symbol, String] :type The interaction type, can be the integer value or the name # of the key in {Discordrb::Interaction::TYPES}. # @option attributes [String, Integer, Server, nil] :server The server where this event was created. `nil` for DM channels. - # @option attribtues [String, Integer, Channel] :channel The channel where this event was created. - # @option attribtues [String, Integer, User] :user The user that triggered this event. + # @option attributes [String, Integer, Channel] :channel The channel where this event was created. + # @option attributes [String, Integer, User] :user The user that triggered this event. # @yield The block is executed when the event is raised. # @yieldparam event [InteractionCreateEvent] The event that was raised. # @return [InteractionCreateEventHandler] The event handler that was registered. @@ -565,15 +565,74 @@ def button(attributes = {}, &block) register_event(ButtonEvent, attributes, block) end - # This **event** is raised whenever an select menu interaction is created. + # This **event** is raised whenever an select string interaction is created. # @param attributes [Hash] The event's attributes. # @option attributes [String, Regexp] :custom_id A custom_id to match against. # @option attributes [String, Integer, Message] :message The message to filter for. # @yield The block is executed when the event is raised. - # @yieldparam event [SelectMenuEvent] The event that was raised. - # @return [SelectMenuEventHandler] The event handler that was registered. - def select_menu(attributes = {}, &block) - register_event(SelectMenuEvent, attributes, block) + # @yieldparam event [StringSelectEvent] The event that was raised. + # @return [StringSelectEventHandler] The event handler that was registered. + def string_select(attributes = {}, &block) + register_event(StringSelectEvent, attributes, block) + end + + alias_method :select_menu, :string_select + + # This **event** is raised whenever a modal is submitted. + # @param attributes [Hash] The event's attributes. + # @option attributes [String, Regexp] :custom_id A custom_id to match against. + # @option attributes [String, Integer, Message] :message The message to filter for. + # @option attributes [String, Integer, Server, nil] :server The server where this event was created. `nil` for DM channels. + # @option attributes [String, Integer, Channel] :channel The channel where this event was created. + # @option attributes [String, Integer, User] :user The user that triggered this event. # @yield The block is executed when the event is raised. + # @yieldparam event [ModalSubmitEvent] The event that was raised. + # @return [ModalSubmitEventHandler] The event handler that was registered. + def modal_submit(attributes = {}, &block) + register_event(ModalSubmitEvent, attributes, block) + end + + # This **event** is raised whenever an select user interaction is created. + # @param attributes [Hash] The event's attributes. + # @option attributes [String, Regexp] :custom_id A custom_id to match against. + # @option attributes [String, Integer, Message] :message The message to filter for. + # @yield The block is executed when the event is raised. + # @yieldparam event [UserSelectEvent] The event that was raised. + # @return [UserSelectEventHandler] The event handler that was registered. + def user_select(attributes = {}, &block) + register_event(UserSelectEvent, attributes, block) + end + + # This **event** is raised whenever an select role interaction is created. + # @param attributes [Hash] The event's attributes. + # @option attributes [String, Regexp] :custom_id A custom_id to match against. + # @option attributes [String, Integer, Message] :message The message to filter for. + # @yield The block is executed when the event is raised. + # @yieldparam event [RoleSelectEvent] The event that was raised. + # @return [RoleSelectEventHandler] The event handler that was registered. + def role_select(attributes = {}, &block) + register_event(RoleSelectEvent, attributes, block) + end + + # This **event** is raised whenever an select mentionable interaction is created. + # @param attributes [Hash] The event's attributes. + # @option attributes [String, Regexp] :custom_id A custom_id to match against. + # @option attributes [String, Integer, Message] :message The message to filter for. + # @yield The block is executed when the event is raised. + # @yieldparam event [MentionableSelectEvent] The event that was raised. + # @return [MentionableSelectEventHandler] The event handler that was registered. + def mentionable_select(attributes = {}, &block) + register_event(MentionableSelectEvent, attributes, block) + end + + # This **event** is raised whenever an select channel interaction is created. + # @param attributes [Hash] The event's attributes. + # @option attributes [String, Regexp] :custom_id A custom_id to match against. + # @option attributes [String, Integer, Message] :message The message to filter for. + # @yield The block is executed when the event is raised. + # @yieldparam event [ChannelSelectEvent] The event that was raised. + # @return [ChannelSelectEventHandler] The event handler that was registered. + def channel_select(attributes = {}, &block) + register_event(ChannelSelectEvent, attributes, block) end # This **event** is raised for every dispatch received over the gateway, whether supported by discordrb or not. @@ -632,7 +691,7 @@ def add_handler(handler) # @param container [Module] A module that `extend`s {EventContainer} from which the handlers will be added. def include_events(container) application_command_handlers = container.instance_variable_get(:@application_commands) - handlers = container.instance_variable_get '@event_handlers' + handlers = container.instance_variable_get :@event_handlers return unless handlers || application_command_handlers @event_handlers ||= {} @@ -681,6 +740,7 @@ def self.class_from_string(str) include Discordrb::Events + # @return [EventHandler] def register_event(clazz, attributes, block) handler = EventContainer.handler_class(clazz).new(attributes, block) diff --git a/lib/discordrb/data/application.rb b/lib/discordrb/data/application.rb index 5eecb1982..a0a74b1c0 100644 --- a/lib/discordrb/data/application.rb +++ b/lib/discordrb/data/application.rb @@ -40,7 +40,7 @@ def initialize(data, bot) def icon_url return nil if @icon_id.nil? - API.app_icon_url(@id, @icon_id) + API::CDN.app_icon_url(@id, @icon_id) end # The inspect method is overwritten to give more useful output diff --git a/lib/discordrb/data/attachment.rb b/lib/discordrb/data/attachment.rb index d76215fac..1f878c4c5 100644 --- a/lib/discordrb/data/attachment.rb +++ b/lib/discordrb/data/attachment.rb @@ -27,6 +27,16 @@ class Attachment # @return [Integer, nil] the height of an image file, in pixels, or `nil` if the file is not an image. attr_reader :height + # @return [String, nil] the attachment's description. + attr_reader :description + + # @return [String, nil] the attachment's media type. + attr_reader :content_type + + # @return [true, false] whether this attachment is ephemeral. + attr_reader :ephemeral + alias_method :ephemeral?, :ephemeral + # @!visibility private def initialize(data, message, bot) @bot = bot @@ -41,6 +51,11 @@ def initialize(data, message, bot) @width = data[:width] @height = data[:height] + + @description = [:description] + @content_type = [:content_type] + + @ephemeral = data[:ephemeral] end # @return [true, false] whether this file is an image file. diff --git a/lib/discordrb/data/audit_logs.rb b/lib/discordrb/data/audit_logs.rb index 8c492e105..2a9a37b1f 100644 --- a/lib/discordrb/data/audit_logs.rb +++ b/lib/discordrb/data/audit_logs.rb @@ -41,13 +41,47 @@ class AuditLogs 75 => :message_unpin, 80 => :integration_create, 81 => :integration_update, - 82 => :integration_delete + 82 => :integration_delete, + 83 => :stage_instance_create, + 84 => :stage_instance_update, + 85 => :stage_instance_delete, + 90 => :sticker_create, + 91 => :sticker_update, + 92 => :sticker_delete, + 100 => :guild_scheduled_event_create, + 101 => :guild_scheduled_event_update, + 102 => :guild_scheduled_event_delete, + 110 => :thread_create, + 111 => :thread_update, + 112 => :thread_delete, + 121 => :application_command_permission_update, + 130 => :soundboard_sound_create, + 131 => :soundboard_sound_update, + 132 => :soundboard_sound_delete, + 140 => :auto_moderation_rule_create, + 141 => :auto_moderation_rule_update, + 142 => :auto_moderation_rule_delete, + 143 => :auto_moderation_block_message, + 144 => :auto_moderation_flag_to_channel, + 145 => :auto_moderation_user_communication_disabled, + 150 => :creator_monetization_request_created, + 151 => :creator_monetization_terms_accepted, + 163 => :onboarding_prompt_create, + 164 => :onboarding_prompt_update, + 165 => :onboarding_prompt_delete, + 166 => :onboarding_create, + 167 => :onboarding_update, + 190 => :home_settings_create, + 191 => :home_settings_update }.freeze # @!visibility private CREATE_ACTIONS = %i[ channel_create channel_overwrite_create member_ban_add role_create - invite_create webhook_create emoji_create integration_create + invite_create webhook_create emoji_create integration_create stage_instance_create + sticker_create guild_scheduled_event_create thread_create soundboard_sound_create + auto_moderation_rule_create creator_monetization_request_created onboarding_prompt_create + onboarding_create home_settings_create ].freeze # @!visibility private @@ -55,13 +89,18 @@ class AuditLogs channel_delete channel_overwrite_delete member_kick member_prune member_ban_remove role_delete invite_delete webhook_delete emoji_delete message_delete message_bulk_delete integration_delete + stage_instance_delete sticker_delete guild_scheduled_event_delete + thread_delete soundboard_sound_delete auto_moderation_rule_delete onboarding_prompt_delete ].freeze # @!visibility private UPDATE_ACTIONS = %i[ server_update channel_update channel_overwrite_update member_update member_role_update role_update invite_update webhook_update - emoji_update integration_update + emoji_update integration_update stage_instance_update sticker_update + guild_scheduled_event_update thread_update application_command_permission_update + soundboard_sound_update auto_moderation_rule_update onboarding_prompt_update + onboarding_update home_settings_update ].freeze # @return [Hash User>] the users included in the audit logs. @@ -177,7 +216,7 @@ def process_target(id, type) # The inspect method is overwritten to give more useful output def inspect - "" + "" end # Process action changes @@ -219,8 +258,8 @@ def initialize(data, server, bot, logs) @old = Permissions.new(@old) if @old && @key == 'permissions' @new = Permissions.new(@new) if @new && @key == 'permissions' - @old = @old.map { |o| Overwrite.new(o[:id], type: o[:type].to_sym, allow: o[:allow], deny: o[:deny]) } if @old && @key == 'permission_overwrites' - @new = @new.map { |o| Overwrite.new(o[:id], type: o[:type].to_sym, allow: o[:allow], deny: o[:deny]) } if @new && @key == 'permission_overwrites' + @old = @old.map { |o| Overwrite.new(o[:id], type: o[:type], allow: o[:allow], deny: o[:deny]) } if @old && @key == 'permission_overwrites' + @new = @new.map { |o| Overwrite.new(o[:id], type: o[:type], allow: o[:allow], deny: o[:deny]) } if @new && @key == 'permission_overwrites' end # @return [Channel, nil] the channel that was previously used in the server widget. Only present if the key for this change is `widget_channel_id`. @@ -325,7 +364,17 @@ def self.target_type_for(action) when 50..59 then :webhook when 60..69 then :emoji when 70..79 then :message - when 80..89 then :integration + when 80..82 then :integration + when 83..85 then :stage_instance + when 90..99 then :sticker + when 100..102 then :scheduled_event + when 110..112 then :thread + when 120..121 then :application_command + when 130..132 then :soundboard + when 140..145 then :auto_moderation + when 150..151 then :creator_monetization + when 163..167 then :onboarding + when 190..191 then :home_settings else :unknown end end diff --git a/lib/discordrb/data/channel.rb b/lib/discordrb/data/channel.rb index 5a5f4e88e..9955ecc8f 100644 --- a/lib/discordrb/data/channel.rb +++ b/lib/discordrb/data/channel.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'discordrb/webhooks/view' +require 'time' module Discordrb # A Discord channel, including data like the topic @@ -15,20 +16,29 @@ class Channel group: 3, category: 4, news: 5, - store: 6 + store: 6, + news_thread: 10, + public_thread: 11, + private_thread: 12, + stage_voice: 13, + directory: 14, + forum: 15, + media: 16 }.freeze # @return [String] this channel's name. attr_reader :name - # @return [Integer, nil] the ID of the parent channel, if this channel is inside a category + # @return [Integer, nil] the ID of the parent channel, if this channel is inside a category. If this channel is a + # thread, this is the text channel it is a child to. attr_reader :parent_id # @return [Integer] the type of this channel # @see TYPES attr_reader :type - # @return [Integer, nil] the ID of the owner of the group channel or nil if this is not a group channel. + # @return [Integer, nil] the ID of the owner of the group channel or nil if this is not a group channel. If this + # channel is a thread, this is the member that started the thread. attr_reader :owner_id # @return [Array, nil] the array of recipients of the private messages, or nil if this is not a Private channel @@ -55,6 +65,35 @@ class Channel attr_reader :rate_limit_per_user alias_method :slowmode_rate, :rate_limit_per_user + # @return [Integer, nil] An approximate count of messages sent in a thread. Stops counting at 50. + attr_reader :message_count + + # @return [Integer, nil] An approximate count of members in a thread. Stops counting at 50. + attr_reader :member_count + + # @return [true, false, nil] Whether or not this thread is archived. + attr_reader :archived + + # @return [Integer, nil] How long after the last message before a thread is automatically archived. + attr_reader :auto_archive_duration + + # @return [Time, nil] The timestamp of when this threads status last changed. + attr_reader :archive_timestamp + + # @return [true, false, nil] Whether this thread is locked or not. + attr_reader :locked + alias_method :locked?, :locked + + # @return [Time, nil] When the current user joined this thread. + attr_reader :join_timestamp + + # @return [Integer, nil] Member flags for this thread, used for notifications. + attr_reader :member_flags + + # @return [true, false] For private threads, determines whether non-moderators can add other non-moderators to + # a thread. + attr_reader :invitable + # @return [true, false] whether or not this channel is a PM or group channel. def private? pm? || group? @@ -104,6 +143,21 @@ def initialize(data, bot, server = nil) @nsfw = data[:nsfw] || false @rate_limit_per_user = data[:rate_limit_per_user] || 0 + @message_count = data['message_count'] + @member_count = data['member_count'] + + if (metadata = data['thread_metadata']) + @archived = metadata['archived'] + @auto_archive_duration = metadata['auto_archive_duration'] + @archive_timestamp = Time.iso8601(metadata['archive_timestamp']) + @locked = metadata['locked'] + @invitable = metadata['invitable'] + end + + if (member = data['member']) + @member_join = Time.iso8601(member['join_timestamp']) + @member_flags = member['flags'] + end process_permission_overwrites(data[:permission_overwrites]) end @@ -156,6 +210,26 @@ def store? @type == 6 end + # @return [true, false] whether or not this channel is a news thread. + def news_thread? + @type == 10 + end + + # @return [true, false] whether or not this channel is a public thread. + def public_thread? + @type == 11 + end + + # @return [true, false] whether or not this channel is a private thread. + def private_thread? + @type == 12 + end + + # @return [true, false] whether or not this channel is a thread. + def thread? + news_thread? || public_thread? || private_thread? + end + # @return [Channel, nil] the category channel, if this channel is in a category def category @bot.channel(@parent_id) if @parent_id @@ -168,7 +242,7 @@ def category # @raise [ArgumentError] if the target channel isn't a category def category=(channel) channel = @bot.channel(channel) - raise ArgumentError, 'Cannot set parent category to a channel that isn\'t a category' unless channel.category? + raise ArgumentError, "Cannot set parent category to a channel that isn't a category" unless channel.category? update_channel_data(parent_id: channel.id) end @@ -238,7 +312,7 @@ def sort_after(other = nil, lock_permissions = false, reason: nil) move_argument << hash end - client.modify_guild_channel_positions(@server_id, move_argument, reason: reason || :undef) + @bot.client.modify_guild_channel_positions(@server_id, move_argument, reason: reason || :undef) end # Sets whether this channel is NSFW @@ -275,9 +349,9 @@ def permission_overwrites=(overwrites) # Sets the amount of time (in seconds) users have to wait in between sending messages. # @param rate [Integer] - # @raise [ArgumentError] if value isn't between 0 and 120 + # @raise [ArgumentError] if value isn't between 0 and 21600 def rate_limit_per_user=(rate) - raise ArgumentError, 'rate_limit_per_user must be between 0 and 120' unless rate.between?(0, 120) + raise ArgumentError, 'rate_limit_per_user must be between 0 and 21600' unless rate.between?(0, 21_600) update_channel_data(rate_limit_per_user: rate) end @@ -546,7 +620,7 @@ def users if text? server.online_members(include_idle: true).select { |u| u.can_read_messages? self } elsif voice? - server.voice_states.map { |id, voice_state| server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id }.compact + server.voice_states.filter_map { |id, voice_state| server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id } end end @@ -722,6 +796,52 @@ def invites @bot.client.get_channel_invites(@id).map { |data| Invite.new(data, @bot) } end + # Start a thread. + # @param name [String] The name of the thread. + # @param auto_archive_duration [60, 1440, 4320, 10080] How long before a thread is automatically archived. + # @param message [Message, Integer, String] The message to reference when starting this thread. + # @param type [Symbol, Integer] The type of thread to create. Can be a key from {TYPES} or the value. + # @return [Channel] + def start_thread(name, auto_archive_duration, message: nil, type: 11) + message_id = message&.id || message + type = TYPES[type] || type + data = if message + @bot.client.start_thread_with_message(@id, message_id, name: name, auto_archive_duration: auto_archive_duration) + else + @bot.client.start_thread_without_message(@id, name: name, auto_archive_duration: auto_archive_duration, type: type) + end + Channel.new(data, @bot, @server) + end + + # @!group Threads + # Join this thread. + def join_thread + @bot.join_thread(@id) + end + + # Leave this thread + def leave_thread + @bot.leave_thread(@id) + end + + # Members in the thread. + def members + @bot.thread_members[@id].collect { |id| @server_id ? @bot.member(@server_id, id) : @bot.user(id) } + end + + # Add a member to the thread + # @param member [Member, Integer, String] The member, or ID of the member, to add to this thread. + def add_member(member) + @bot.add_thread_member(@id, member) + end + + # @param member [Member, Integer, String] The member, or ID of the member, to remove from a thread. + def remove_member(member) + @bot.remove_thread_member(@id, member) + end + + # @!endgroup + # The default `inspect` method is overwritten to give more useful output. def inspect "" diff --git a/lib/discordrb/data/component.rb b/lib/discordrb/data/component.rb index 341fc31c5..3401f12d7 100644 --- a/lib/discordrb/data/component.rb +++ b/lib/discordrb/data/component.rb @@ -14,8 +14,10 @@ def self.from_data(data, bot) ActionRow.new(data, bot) when Webhooks::View::COMPONENT_TYPES[:button] Button.new(data, bot) - when Webhooks::View::COMPONENT_TYPES[:select_menu] + when Webhooks::View::COMPONENT_TYPES[:string_select] SelectMenu.new(data, bot) + when Webhooks::Modal::COMPONENT_TYPES[:text_input] + TextInput.new(data, bot) end end @@ -24,17 +26,17 @@ class ActionRow include Enumerable # @return [Array