diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c845ea722..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,152 +0,0 @@ -version: 2.1 - -commands: - setup-env: - description: Sets up the testing environment - steps: - - run: - name: Install OS packages - command: apk add git build-base ruby-dev ruby-etc ruby-json libsodium - - checkout - - run: - name: "Ruby version" - command: | - ruby -v - echo $RUBY_VERSION > ruby_version.txt - - restore_cache: - keys: - - bundle-cache-v1-{{ checksum "ruby_version.txt" }}-{{ .Branch }}-{{ checksum "Gemfile" }}-{{ checksum "discordrb.gemspec" }} - - bundle-cache-v1-{{ checksum "ruby_version.txt" }}-{{ .Branch }} - - bundle-cache-v1-{{ checksum "ruby_version.txt" }} - - run: - name: Install dependencies - command: bundle install --path vendor/bundle - - save_cache: - key: bundle-cache-v1-{{ checksum "ruby_version.txt" }}-{{ .Branch }}-{{ checksum "Gemfile" }}-{{ checksum "discordrb.gemspec" }} - paths: - - ./vendor/bundle - -jobs: - test_ruby_27: - docker: - - image: ruby:2.7-alpine - steps: - - setup-env - - run: - name: Run RSpec - command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml - - store_test_results: - path: ~/rspec - - test_ruby_30: - docker: - - image: ruby:3.0-alpine - steps: - - setup-env - - run: - name: Run RSpec - command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml - - store_test_results: - path: ~/rspec - - - test_ruby_31: - docker: - - image: ruby:3.1-alpine - steps: - - setup-env - - run: - name: Run RSpec - command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml - - rubocop: - docker: - - image: ruby:2.7-alpine - steps: - - setup-env - - run: - name: Run Rubocop - command: bundle exec rubocop - - yard: - docker: - - image: ruby:2.7-alpine - steps: - - setup-env - - attach_workspace: - at: /tmp/workspace - - run: - name: Run YARD - command: bundle exec yard --output-dir /tmp/workspace/docs - - persist_to_workspace: - root: /tmp/workspace - paths: - - docs - - pages: - 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: | - 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" - - run: - name: Push updated docs - command: | - git config user.name "Circle CI" - git config user.email "ci-build@shardlab.dev" - - SOURCE_BRANCH=$CIRCLE_BRANCH - if [ -n "$CIRCLE_TAG" ]; then - SOURCE_BRANCH=$CIRCLE_TAG - fi - - mkdir -p $SOURCE_BRANCH - rm -rf $SOURCE_BRANCH/* - cp -r /tmp/workspace/docs/. ./$SOURCE_BRANCH/ - - git add $SOURCE_BRANCH - git commit --allow-empty -m "[skip ci] Deploy docs" - git push -u origin gh-pages - -workflows: - test: - jobs: - - test_ruby_27 - - test_ruby_30 - - test_ruby_31 - - rubocop - - yard - deploy: - jobs: - - yard: - filters: - branches: - only: - - main - - slash_commands - tags: - only: /^v.*/ - - pages: - requires: - - yard - filters: - branches: - only: - - main - - slash_commands - tags: - only: /^v.*/ diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml new file mode 100644 index 000000000..a9def4743 --- /dev/null +++ b/.github/workflows/github-pages.yml @@ -0,0 +1,65 @@ +name: "Github Pages" + +on: + push: + branches: + - main + tags: + - v* + +jobs: + pages: + name: Github Pages + runs-on: ubuntu-latest + env: + api-dir: ./ + + strategy: + fail-fast: false + + steps: + - name: Checkout repository for Yard + uses: actions/checkout@v3 + with: + path: discordrb_docs + + - name: Install OS package + run: | + sudo apt update + sudo apt install -y git openssh-client + + - name: Setup Ruby 2.7 + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + + - name: Checkout repository for Github Pages + uses: actions/checkout@v3 + with: + path: discordrb_gh + ref: gh-pages + + - name: Run bundle install + working-directory: ${{env.api-dir}} + run: | + gem install bundler + BUNDLE_GEMFILE=discordrb_docs/Gemfile bundle install --jobs 4 --retry 3 + + - name: Generate Yard docs + working-directory: ${{env.api-dir}} + run: | + cd discordrb_docs + bundle exec yard --output-dir /tmp/docs + + - name: Commit & Push docs + run: | + cd discordrb_gh + git config user.email "github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + mkdir -p $GITHUB_REF_NAME + rm -rf $GITHUB_REF_NAME/* + cp -r /tmp/docs/. $GITHUB_REF_NAME + git add $GITHUB_REF_NAME + git commit --allow-empty -m "[skip ci] Deploy docs" + git push diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml new file mode 100644 index 000000000..3103db5f8 --- /dev/null +++ b/.github/workflows/rspec.yml @@ -0,0 +1,53 @@ +name: "RSpec" + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + rspec: + name: RSpec + runs-on: ubuntu-latest + env: + api-dir: ./ + + strategy: + fail-fast: false + matrix: + versions: [ '2.7', '3.0', '3.1', '3.2', '3.3' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup libraries + run: | + sudo apt update + sudo apt install -y libsodium-dev + + - name: Setup Ruby ${{ matrix.versions }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.versions }} + bundler-cache: true + + - name: Run bundle install (Ruby ${{ matrix.versions }}) + working-directory: ${{env.api-dir}} + run: | + gem install bundler + bundle install --jobs 4 --retry 3 + + - name: RSpec spec (Ruby ${{ matrix.versions }}) + working-directory: ${{env.api-dir}} + run: bundle exec rspec spec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml + + - name: Save test results (Ruby ${{ matrix.versions }}) + uses: actions/upload-artifact@v3 + with: + name: rspec_results_${{ matrix.versions }} + path: ~/rspec/rspec.xml + retention-days: 30 diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml new file mode 100644 index 000000000..bf1634633 --- /dev/null +++ b/.github/workflows/rubocop.yml @@ -0,0 +1,39 @@ +name: "RuboCop" + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + rubocop: + name: RuboCop + runs-on: ubuntu-latest + env: + api-dir: ./ + + strategy: + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup Ruby 2.7 + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + + - name: Run bundle install + working-directory: ${{env.api-dir}} + run: | + gem install bundler + bundle install --jobs 4 --retry 3 + + - name: RuboCop checks + working-directory: ${{env.api-dir}} + run: bundle exec rubocop diff --git a/.github/workflows/yard.yml b/.github/workflows/yard.yml new file mode 100644 index 000000000..1b7cea63f --- /dev/null +++ b/.github/workflows/yard.yml @@ -0,0 +1,51 @@ +name: "Yard" + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + yard: + name: Yard + runs-on: ubuntu-latest + env: + api-dir: ./ + + strategy: + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup Ruby 2.7 + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + + - name: Run bundle install + working-directory: ${{env.api-dir}} + run: | + gem install bundler + bundle install --jobs 4 --retry 3 + + - name: Generate Yard docs + working-directory: ${{env.api-dir}} + run: bundle exec yard --output-dir /tmp/docs + + - name: Compress docs to prevent error 429 + run: | + cd /tmp/docs + tar -cvf /tmp/docs.tar . + + - name: Keep temporary the generated docs + uses: actions/upload-artifact@v3 + with: + name: docs_${{ github.run_number }} + path: /tmp/docs.tar + retention-days: 30 diff --git a/README.md b/README.md index b492a372f..5dc2196e5 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,10 @@ [![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](https://inch-ci.org/github/shardlab/discordrb.svg?branch=main)](https://drb.shardlab.dev/v3.4.0/) +[![Gem](https://img.shields.io/badge/docs-v3.5.0-979797.svg)](https://drb.shardlab.dev/v3.5.0/) +[![Github Actions Rspec](https://github.com/shardlab/discordrb/actions/workflows/rspec.yml/badge.svg?branch=main&event=push)](https://github.com/shardlab/discordrb/actions/workflows/rspec.yml) +[![Github Actions Rubocop](https://github.com/shardlab/discordrb/actions/workflows/rubocop.yml/badge.svg?branch=main&event=push)](https://github.com/shardlab/discordrb/actions/workflows/rubocop.yml) +[![Inline docs](https://img.shields.io/badge/docs-main-979797.svg)](https://drb.shardlab.dev/main/) [![Join Discord](https://img.shields.io/badge/discord-join-7289DA.svg)](https://discord.gg/cyK3Hjm) An implementation of the [Discord](https://discord.com/) API using Ruby. @@ -19,7 +21,7 @@ An implementation of the [Discord](https://discord.com/) API using Ruby. - [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) +See also: [Documentation](https://drb.shardlab.dev/v3.5.0/), [Tutorials](https://github.com/shardlab/discordrb/wiki) ## Introduction @@ -67,6 +69,13 @@ gem 'discordrb' And then install via `bundle install`. +_If you want to run the latest code instead, use this gem line instead:_ +```ruby +gem 'discordrb', github: 'shardlab/discordrb', branch: 'main' +``` + +⚠️ **Note that main may contain breaking changes or other unstable code !** + 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: @@ -131,7 +140,7 @@ If you've made an open source project on GitHub that uses discordrb, consider ad Also included is a webhooks client, which can be used as a separate gem `discordrb-webhooks`. This special client can be used to form requests to Discord webhook URLs in a high-level manner. -- [`discordrb-webhooks` documentation](https://drb.shardlab.dev/v3.4.0/Discordrb/Webhooks.html) +- [`discordrb-webhooks` documentation](https://drb.shardlab.dev/v3.5.0/Discordrb/Webhooks.html) - [More information about webhooks](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) - [Embed visualizer tool](https://leovoel.github.io/embed-visualizer/) - Includes a discordrb code generator for forming embeds diff --git a/discordrb.gemspec b/discordrb.gemspec index 8867fd132..bba67e037 100644 --- a/discordrb.gemspec +++ b/discordrb.gemspec @@ -35,8 +35,8 @@ Gem::Specification.new do |spec| 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.11.0' + spec.add_development_dependency 'redcarpet', '~> 3.6.0' # YARD markdown formatting + spec.add_development_dependency 'rspec', '~> 3.12.0' spec.add_development_dependency 'rspec_junit_formatter', '~> 0.5.1' spec.add_development_dependency 'rspec-prof', '~> 0.0.7' spec.add_development_dependency 'rubocop', '~> 1.36.0' diff --git a/examples/channel_overwrite.rb b/examples/channel_overwrite.rb new file mode 100644 index 000000000..4723f577e --- /dev/null +++ b/examples/channel_overwrite.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'discordrb' +require 'securerandom' + +CHANNEL_EDIT = ENV.fetch('CHANNEL_EDIT') +ROLE_1 = ENV.fetch('ROLE_1') +ROLE_2 = ENV.fetch('ROLE_2') + +bot = Discordrb::Bot.new(token: ENV.fetch('DISCORDRB_TOKEN')) + +bot.message do |event| + if event.message.content == 'DEFINE_OVERWRITE' + event.channel.send_message('Define overwrite in this channel') + + allow = Discordrb::Permissions.new + allow.can_mention_everyone = true + + overwrite = Discordrb::Overwrite.new(ROLE_1, type: 'role', allow: allow, deny: Discordrb::Permissions.new) + + event.bot.channel(CHANNEL_EDIT, event.server).define_overwrite(overwrite) + end + + if event.message.content == 'CHECK_OVERWRITE' + event.channel.send_message('Check overwrite in this channel') + puts(event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites.map { |_, v| "#{v.type} - #{v.id} - #{v.allow.bits} / #{v.deny.bits}" }) + end + + if event.message.content == 'DELETE_OVERWRITE' + event.channel.send_message('Delete overwrite in this channel') + + event.bot.channel(CHANNEL_EDIT, event.server).delete_overwrite(ROLE_1) + end + + if event.message.content == 'BULK_OVERWRITE' + event.channel.send_message('Bulk overwrite in this channel') + + allow = Discordrb::Permissions.new + allow.can_mention_everyone = true + + deny = Discordrb::Permissions.new + deny.can_mention_everyone = true + + overwrites = [] + overwrites << Discordrb::Overwrite.new(ROLE_1, type: 'role', allow: allow, deny: Discordrb::Permissions.new) + overwrites << Discordrb::Overwrite.new(ROLE_2, type: 'role', allow: Discordrb::Permissions.new, deny: deny) + + event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites = overwrites + puts(event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites.map { |_, v| "#{v.type} - #{v.id} - #{v.allow.bits} / #{v.deny.bits}" }) + + # Bulk edit from permission_overwrites return values (this method return a Hash and not an Array) + event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites = event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites + puts(event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites.map { |_, v| "#{v.type} - #{v.id} - #{v.allow.bits} / #{v.deny.bits}" }) + + # Send nil to check if permission_overwrites not changed + event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites = nil + puts(event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites.map { |_, v| "#{v.type} - #{v.id} - #{v.allow.bits} / #{v.deny.bits}" }) + end + + if event.message.content == 'BULK_DELETE_OVERWRITE' + event.channel.send_message('Bulk delete overwrite in this channel') + event.bot.channel(CHANNEL_EDIT, event.server).permission_overwrites = [] + end +end + +bot.run diff --git a/examples/webhooks.rb b/examples/webhooks.rb index fa4e5584b..dc31f5957 100644 --- a/examples/webhooks.rb +++ b/examples/webhooks.rb @@ -26,7 +26,7 @@ if event.message.content == 'EDIT_TWO' wh = event.channel.webhooks.first - wh.update(avatar: BASE64_SMALL_PICTURE, reason: 'Edition test two') + wh.update({ avatar: BASE64_SMALL_PICTURE, reason: 'Edition test two' }) puts wh.inspect wh.execute(content: '[EDIT TWO]') diff --git a/lib/discordrb/data/channel.rb b/lib/discordrb/data/channel.rb index 4f442b7c8..5bd9f2368 100644 --- a/lib/discordrb/data/channel.rb +++ b/lib/discordrb/data/channel.rb @@ -341,7 +341,7 @@ def permission_overwrites(type = nil) alias_method :overwrites, :permission_overwrites # Bulk sets this channels permission overwrites - # @param overwrites [Array] + # @param overwrites [Array, Hash Overwrite>] def permission_overwrites=(overwrites) update_channel_data(permission_overwrites: overwrites) end @@ -965,7 +965,12 @@ def bulk_delete(ids, strict = false, reason = nil) def update_channel_data(new_data) new_nsfw = new_data[:nsfw].is_a?(TrueClass) || new_data[:nsfw].is_a?(FalseClass) ? new_data[:nsfw] : @nsfw # send permission_overwrite only when explicitly set - overwrites = new_data[:permission_overwrites] ? new_data[:permission_overwrites].map { |_, v| v.to_hash } : nil + overwrites = if new_data[:permission_overwrites].is_a?(Hash) + new_data[:permission_overwrites]&.map { |_, v| v&.to_hash } + else + new_data[:permission_overwrites]&.map(&:to_hash) + end + response = JSON.parse(API::Channel.update(@bot.token, @id, new_data[:name] || @name, new_data[:topic] || @topic, diff --git a/lib/discordrb/data/webhook.rb b/lib/discordrb/data/webhook.rb index 3e2bb8260..cb5693649 100644 --- a/lib/discordrb/data/webhook.rb +++ b/lib/discordrb/data/webhook.rb @@ -86,7 +86,7 @@ def update(data) data[:avatar] = avatarise(data[:avatar]) if data.key?(:avatar) data[:channel_id] = data[:channel]&.resolve_id data.delete(:channel) - update_webhook(**data) + update_webhook(data) end # Deletes the webhook. diff --git a/spec/data/channel_spec.rb b/spec/data/channel_spec.rb index acbae6cda..e068df8bf 100644 --- a/spec/data/channel_spec.rb +++ b/spec/data/channel_spec.rb @@ -103,7 +103,7 @@ allow(JSON).to receive(:parse) new_data = double('new data') allow(new_data).to receive(:[]) - allow(new_data).to receive(:[]).with(:permission_overwrites).and_return(false) + allow(new_data).to receive(:[]).with(:permission_overwrites).and_return(nil) expect(Discordrb::API::Channel).to receive(:update).with(any_args, nil, anything) channel.__send__(:update_channel_data, new_data) end diff --git a/spec/data/webhook_spec.rb b/spec/data/webhook_spec.rb index d6c5a5a05..1f3aa6b49 100644 --- a/spec/data/webhook_spec.rb +++ b/spec/data/webhook_spec.rb @@ -101,7 +101,7 @@ describe '#update' do it 'calls update_webhook' do - expect(webhook).to receive(:update_webhook).with(avatar: avatar_string, channel_id: edited_webhook_channel_id.to_i, name: edited_webhook_name, reason: reason) + expect(webhook).to receive(:update_webhook).with(hash_including(avatar: avatar_string, channel_id: edited_webhook_channel_id.to_i, name: edited_webhook_name, reason: reason)) webhook.update(avatar: avatar_string, channel: edited_webhook_channel_id, name: edited_webhook_name, reason: reason) end end