├── .github └── workflows │ └── gem-push.yml ├── .gitignore ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── console ├── breacan.gemspec └── lib ├── breacan.rb └── breacan ├── authentication.rb ├── client.rb ├── client ├── api.rb ├── auth.rb ├── channels.rb ├── chat.rb ├── emoji.rb ├── files.rb ├── groups.rb ├── im.rb ├── oauth.rb ├── rtm.rb ├── search.rb ├── star.rb ├── usergroups.rb └── users.rb ├── configurable.rb ├── error.rb ├── response └── custom.rb └── version.rb /.github/workflows/gem-push.yml: -------------------------------------------------------------------------------- 1 | name: Ruby Gem 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | 7 | jobs: 8 | build: 9 | name: Build + Publish 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up Ruby 18 | # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, 19 | # change this to (see https://github.com/ruby/setup-ruby#versioning): 20 | # uses: ruby/setup-ruby@v1 21 | uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 22 | with: 23 | ruby-version: 3 24 | 25 | - name: Publish to GPR 26 | id: gpr 27 | run: | 28 | mkdir -p $HOME/.gem 29 | touch $HOME/.gem/credentials 30 | chmod 0600 $HOME/.gem/credentials 31 | printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials 32 | gem build *.gemspec 33 | VERSION=$(gem spec *.gem version | grep version | awk '{print $2}') 34 | EXISTS=$(gem search ^breacan$ -a | grep -c $VERSION) 35 | if [ $EXISTS -eq 0 ]; then 36 | gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem 37 | else 38 | echo "Gem with the same version already exists. Skipping push..." 39 | echo "skip_publish_to_rubygems=true" >> $GITHUB_OUTPUT 40 | fi 41 | env: 42 | GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}" 43 | OWNER: ${{ github.repository_owner }} 44 | 45 | - name: Publish to RubyGems 46 | if: ${{ steps.gpr.outputs.skip_publish_to_rubygems != 'true' }} 47 | run: | 48 | mkdir -p $HOME/.gem 49 | touch $HOME/.gem/credentials 50 | chmod 0600 $HOME/.gem/credentials 51 | printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials 52 | gem build *.gemspec 53 | gem push *.gem 54 | env: 55 | GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | *.bundle 11 | *.so 12 | *.o 13 | *.a 14 | mkmf.log 15 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in breacan.gemspec 4 | gemspec 5 | 6 | gem 'pry' 7 | gem 'awesome_print' 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 linyows 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Breacan: Simple Ruby wrapper for Slack API. 4 |

5 | 6 | 7 | 8 | 9 | Installation 10 | ------------ 11 | 12 | Add this line to your application's Gemfile: 13 | 14 | ```ruby 15 | gem 'breacan' 16 | ``` 17 | 18 | And then execute: 19 | 20 | $ bundle 21 | 22 | Or install it yourself as: 23 | 24 | $ gem install breacan 25 | 26 | Usage 27 | ----- 28 | 29 | example: 30 | 31 | ```ruby 32 | require 'breacan' 33 | Breacan.access_token = 'xoxp-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 34 | Breacan.channels 35 | => [{:id=>"C03QXXXX", 36 | :name=>"hello", 37 | :is_channel=>true, 38 | :created=>1424735886, 39 | :creator=>"U03PXXXX", 40 | :is_archived=>false, 41 | :is_general=>false, 42 | :is_member=>false, 43 | :members=> ["U0NLXXXX",... 44 | Breacan.channel_by_name('hello').id 45 | => "C03QXXXX" 46 | ``` 47 | 48 | Contributing 49 | ------------ 50 | 51 | 1. Fork it ( https://github.com/linyows/breacan/fork ) 52 | 2. Create your feature branch (`git checkout -b my-new-feature`) 53 | 3. Commit your changes (`git commit -am 'Add some feature'`) 54 | 4. Push to the branch (`git push origin my-new-feature`) 55 | 5. Create a new Pull Request 56 | 57 | Authors 58 | ------- 59 | 60 | - [linyows](https://github.com/linyows) 61 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "breacan" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "pry" 14 | Pry.start 15 | -------------------------------------------------------------------------------- /breacan.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'breacan/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "breacan" 8 | spec.version = Breacan::VERSION 9 | spec.authors = ["linyows"] 10 | spec.email = ["linyows@gmail.com"] 11 | spec.summary = %q{Simple Ruby wrapper for Slack API.} 12 | spec.description = %q{Simple Ruby wrapper for Slack API.} 13 | spec.homepage = "https://github.com/linyows/breacan" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0") 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_dependency "sawyer", "~> 0.6" 22 | spec.add_dependency "activesupport" 23 | spec.add_development_dependency "bundler", "~> 1.7" 24 | spec.add_development_dependency "rake", "~> 10.0" 25 | 26 | end 27 | -------------------------------------------------------------------------------- /lib/breacan.rb: -------------------------------------------------------------------------------- 1 | require 'breacan/version' 2 | require 'breacan/client' 3 | 4 | module Breacan 5 | class << self 6 | include Breacan::Configurable 7 | 8 | def client 9 | unless defined?(@client) && @client.same_options?(options) 10 | @client = Breacan::Client.new(options) 11 | end 12 | @client 13 | end 14 | 15 | private 16 | 17 | def method_missing(method_name, *args, &block) 18 | return super unless client.respond_to?(method_name) 19 | client.send(method_name, *args, &block) 20 | end 21 | end 22 | end 23 | 24 | Breacan.setup 25 | -------------------------------------------------------------------------------- /lib/breacan/authentication.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | module Authentication 3 | def token_authenticated? 4 | !!@access_token 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/breacan/client.rb: -------------------------------------------------------------------------------- 1 | require 'sawyer' 2 | require 'active_support' 3 | require 'active_support/core_ext/hash' 4 | require 'breacan/configurable' 5 | require 'breacan/authentication' 6 | require 'breacan/client/api' 7 | require 'breacan/client/auth' 8 | require 'breacan/client/channels' 9 | require 'breacan/client/chat' 10 | require 'breacan/client/emoji' 11 | require 'breacan/client/files' 12 | require 'breacan/client/groups' 13 | require 'breacan/client/im' 14 | require 'breacan/client/oauth' 15 | require 'breacan/client/rtm' 16 | require 'breacan/client/search' 17 | require 'breacan/client/star' 18 | require 'breacan/client/users' 19 | require 'breacan/client/usergroups' 20 | 21 | module Breacan 22 | class Client 23 | include Breacan::Authentication 24 | include Breacan::Configurable 25 | include Breacan::Client::Api 26 | include Breacan::Client::Auth 27 | include Breacan::Client::Channels 28 | include Breacan::Client::Chat 29 | include Breacan::Client::Emoji 30 | include Breacan::Client::Files 31 | include Breacan::Client::Groups 32 | include Breacan::Client::Im 33 | include Breacan::Client::OAuth 34 | include Breacan::Client::Rtm 35 | include Breacan::Client::Search 36 | include Breacan::Client::Star 37 | include Breacan::Client::Users 38 | include Breacan::Client::Usergroups 39 | 40 | CONVENIENCE_HEADERS = Set.new(%i(accept content_type)) 41 | 42 | def initialize(options = {}) 43 | Breacan::Configurable.keys.each do |key| 44 | instance_variable_set(:"@#{key}", options[key] || Breacan.instance_variable_get(:"@#{key}")) 45 | end 46 | end 47 | 48 | def same_options?(opts) 49 | opts.hash == options.hash 50 | end 51 | 52 | def get(url, options = {}) 53 | return get_with_auto_paginate(url, options) if auto_paginate 54 | request :get, url, parse_query_and_convenience_headers(options) 55 | end 56 | 57 | def get_with_auto_paginate(url, options = {}) 58 | res = nil 59 | if url.end_with?('.list') 60 | nextc = nil 61 | loop do 62 | r = request :get, url, parse_query_and_convenience_headers(options.merge({cursor: nextc})) 63 | if r.is_a?(Sawyer::Resource) 64 | res ||= {} 65 | res = res.deep_merge(r) do |key, this_val, other_val| 66 | if this_val.is_a?(Array) && other_val.is_a?(Array) 67 | this_val + other_val 68 | else 69 | other_val 70 | end 71 | end 72 | elsif r.is_a?(Array) 73 | res ||= [] 74 | res.concat(r) 75 | else 76 | raise "unknown response type" 77 | end 78 | 79 | if r.respond_to?(:response_metadata) 80 | if r.response_metadata.next_cursor == "" 81 | res = Sawyer::Resource.new(Sawyer::Agent.new(api_endpoint), res) 82 | break 83 | end 84 | nextc = r.response_metadata.next_cursor 85 | else 86 | break 87 | end 88 | end 89 | else 90 | res = request :get, url, parse_query_and_convenience_headers(options) 91 | end 92 | res 93 | end 94 | 95 | def post(url, options = {}) 96 | request :post, url, options 97 | end 98 | 99 | def put(url, options = {}) 100 | request :put, url, options 101 | end 102 | 103 | def patch(url, options = {}) 104 | request :patch, url, options 105 | end 106 | 107 | def delete(url, options = {}) 108 | request :delete, url, options 109 | end 110 | 111 | def head(url, options = {}) 112 | request :head, url, parse_query_and_convenience_headers(options) 113 | end 114 | 115 | def agent 116 | @agent ||= Sawyer::Agent.new(api_endpoint, sawyer_options) do |http| 117 | http.headers[:accept] = media_type 118 | http.headers[:user_agent] = user_agent 119 | end 120 | end 121 | 122 | private 123 | 124 | def request(method, path, data, options = {}) 125 | if data.is_a?(Hash) 126 | options[:query] = data.delete(:query) || {} 127 | options[:headers] = data.delete(:headers) || {} 128 | if accept = data.delete(:accept) 129 | options[:headers][:accept] = accept 130 | end 131 | options[:query][:token] = access_token 132 | end 133 | 134 | @last_response = response = agent.call( 135 | method, 136 | URI::Parser.new.escape(path.to_s), 137 | data, 138 | options 139 | ) 140 | 141 | simplize_body response.data 142 | end 143 | 144 | def simplize_body(body) 145 | keys = body.to_hash.keys.dup 146 | 147 | @cache_ts = if keys.include?(:cache_ts) 148 | keys.delete(:cache_ts) 149 | body.cache_ts 150 | else 151 | nil 152 | end 153 | 154 | keys.count == 1 ? body[keys.last] : body 155 | end 156 | 157 | def cache_ts 158 | @cache_ts if defined? @cache_ts 159 | end 160 | 161 | def last_response 162 | @last_response if defined? @last_response 163 | end 164 | 165 | def boolean_from_response(method, path, options = {}) 166 | request(method, path, options) 167 | @last_response.status == 204 168 | rescue Breacan::NotFound 169 | false 170 | end 171 | 172 | def sawyer_options 173 | opts = { 174 | :links_parser => Sawyer::LinkParsers::Simple.new 175 | } 176 | conn_opts = @connection_options 177 | conn_opts[:builder] = @middleware if @middleware 178 | conn_opts[:proxy] = @proxy if @proxy 179 | opts[:faraday] = Faraday.new(conn_opts) 180 | opts[:serializer] = @serializer 181 | 182 | opts 183 | end 184 | 185 | def parse_query_and_convenience_headers(options) 186 | headers = options.fetch(:headers, {}) 187 | CONVENIENCE_HEADERS.each do |h| 188 | if header = options.delete(h) 189 | headers[h] = header 190 | end 191 | end 192 | query = options.delete(:query) 193 | opts = {:query => options} 194 | opts[:query].merge!(query) if query && query.is_a?(Hash) 195 | opts[:headers] = headers unless headers.empty? 196 | 197 | opts 198 | end 199 | end 200 | end 201 | -------------------------------------------------------------------------------- /lib/breacan/client/api.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Api 4 | def api_test(args) 5 | get 'api.test', query: args 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/breacan/client/auth.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Auth 4 | def auth_test(args) 5 | get 'auth.test', query: args 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/breacan/client/channels.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Channels 4 | def channels_archive(args) 5 | get 'channels.archive', query: args 6 | end 7 | 8 | def channels_create(args) 9 | get 'channels.create', query: args 10 | end 11 | 12 | def channels_history(args) 13 | get 'channels.history', query: args 14 | end 15 | 16 | def channels_info(id, options) 17 | get 'channels.info', query: { channel: id }.merge(options) 18 | end 19 | alias_method :channel, :channels_info 20 | 21 | def channels_invite(args) 22 | get 'channels.invite', query: args 23 | end 24 | 25 | def channels_join(args) 26 | get 'channels.join', query: args 27 | end 28 | 29 | def channels_kick(args) 30 | get 'channels.kick', query: args 31 | end 32 | 33 | def channels_leave(args) 34 | get 'channels.leave', query: args 35 | end 36 | 37 | def channels_list(args = nil) 38 | get 'channels.list', query: args 39 | end 40 | alias_method :channels, :channels_list 41 | 42 | def channels_mark(args) 43 | get 'channels.mark', query: args 44 | end 45 | 46 | def channels_rename(args) 47 | get 'channels.rename', query: args 48 | end 49 | 50 | def channels_set_purpose(args) 51 | get 'channels.setPurpose', query: args 52 | end 53 | 54 | def channels_set_topic(args) 55 | get 'channels.setTopic', query: args 56 | end 57 | 58 | def channels_unarchive(args) 59 | get 'channels.unarchive', query: args 60 | end 61 | 62 | def channel_by_name(name) 63 | channels.find { |ch| ch.name == name } 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/breacan/client/chat.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Chat 4 | def chat_delete(args) 5 | get 'chat.delete', query: args 6 | end 7 | 8 | def chat_post_message(args) 9 | get 'chat.postMessage', query: args 10 | end 11 | 12 | def chat_update(args) 13 | get 'chat.update', query: args 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/breacan/client/emoji.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Emoji 4 | def emoji_list 5 | get 'emoji.list' 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/breacan/client/files.rb: -------------------------------------------------------------------------------- 1 | 2 | module Breacan 3 | class Client 4 | module Files 5 | def files_delete(args) 6 | get 'files.delete', query: args 7 | end 8 | 9 | def files_info(args) 10 | get 'files.info', query: args 11 | end 12 | 13 | def files_list(args) 14 | get 'files.list', query: args 15 | end 16 | 17 | def files_upload(args) 18 | set_files_upload_builder 19 | data = {} 20 | if args[:file] 21 | data[:file] = args[:file] 22 | args.delete(:file) 23 | end 24 | data[:query] = args 25 | res = post 'files.upload', data 26 | reset_files_upload_builder 27 | res 28 | end 29 | 30 | def set_files_upload_builder 31 | @serializer = Breacan::Client::Files::FileSerializer.any_json 32 | @middleware = Faraday::RackBuilder.new do |c| 33 | c.request :multipart 34 | c.request :url_encoded 35 | c.adapter :net_http 36 | c.response :breacan_custom 37 | end 38 | end 39 | 40 | def reset_files_upload_builder 41 | @middleware = default_builder 42 | end 43 | 44 | class FileSerializer < Sawyer::Serializer 45 | def encode(data) 46 | data 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/breacan/client/groups.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Groups 4 | def groups_archive(args) 5 | get 'groups.archive', query: args 6 | end 7 | 8 | def groups_create(args) 9 | get 'groups.create', query: args 10 | end 11 | 12 | def groups_history(args) 13 | get 'groups.history', query: args 14 | end 15 | 16 | def groups_info(args) 17 | get 'groups.info', query: args 18 | end 19 | 20 | def groups_invite(args) 21 | get 'groups.invite', query: args 22 | end 23 | 24 | def groups_join(args) 25 | get 'groups.join', query: args 26 | end 27 | 28 | def groups_kick(args) 29 | get 'groups.kick', query: args 30 | end 31 | 32 | def groups_leave(args) 33 | get 'groups.leave', query: args 34 | end 35 | 36 | def groups_list(args = nil) 37 | get 'groups.list', query: args 38 | 39 | end 40 | 41 | def groups_mark(args) 42 | get 'groups.mark', query: args 43 | end 44 | 45 | def groups_rename(args) 46 | get 'groups.rename', query: args 47 | end 48 | 49 | def groups_set_purpose(args) 50 | get 'groups.setPurpose', query: args 51 | end 52 | 53 | def groups_set_topic(args) 54 | get 'groups.setTopic', query: args 55 | end 56 | 57 | def groups_unarchive(args) 58 | get 'groups.unarchive', query: args 59 | end 60 | 61 | def groups_info_by_name(name) 62 | groups_list.find { |ch| ch['name'] == name } 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/breacan/client/im.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Im 4 | def im_close(args) 5 | get 'im.close', query: args 6 | end 7 | 8 | def im_history(args) 9 | get 'im.history', query: args 10 | end 11 | 12 | def im_list(args) 13 | get 'im.list', query: args 14 | end 15 | 16 | def im_mark(args) 17 | get 'im.mark', query: args 18 | end 19 | 20 | def im_open(args) 21 | get 'im.open', query: args 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/breacan/client/oauth.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module OAuth 4 | def oauth_access(args) 5 | get 'oauth.access', query: args 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/breacan/client/rtm.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Rtm 4 | def rtm_start 5 | get 'rtm.start' 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/breacan/client/search.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Search 4 | def search_all(args) 5 | get 'serach.all', query: args 6 | end 7 | 8 | def search_files(args) 9 | get 'serach.files', query: args 10 | end 11 | 12 | def search_messages(args) 13 | get 'serach.messages', query: args 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/breacan/client/star.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Star 4 | def star_list(args) 5 | get 'star.list', query: args 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/breacan/client/usergroups.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Usergroups 4 | def usergroups_create(args) 5 | get 'usergroups.create', query: args 6 | end 7 | alias_method :create_usergroup, :usergroups_create 8 | 9 | def usergroups_disable(args) 10 | get 'usergroups.disable', query: args 11 | end 12 | alias_method :disable_usergroup, :usergroups_disable 13 | 14 | def usergroups_enable(args) 15 | get 'usergroups.enable', query: args 16 | end 17 | alias_method :enable_usergroup, :usergroups_enable 18 | 19 | def usergroups_list(args = {}) 20 | get 'usergroups.list', query: args 21 | end 22 | alias_method :usergroups, :usergroups_list 23 | 24 | def usergroups_update(args) 25 | get 'usergroups.update', query: args 26 | end 27 | alias_method :update_usergroup, :usergroups_update 28 | 29 | def usergroups_users_list(id, options = {}) 30 | get 'usergroups.users.list', query: { usergroup: id }.merge(options) 31 | end 32 | alias_method :usergroup_users, :usergroups_users_list 33 | 34 | def usergroups_users_update(args) 35 | get 'usergroups.users.update', query: args 36 | end 37 | alias_method :update_usergroup_users, :usergroups_users_update 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/breacan/client/users.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Client 3 | module Users 4 | def users_get_presence(args) 5 | get 'users.getPresence', query: args 6 | end 7 | alias_method :user_presence, :users_get_presence 8 | 9 | def users_info(id, options = {}) 10 | get 'users.info', query: { user: id }.merge(options) 11 | end 12 | alias_method :user, :users_info 13 | 14 | def users_list(args = {}) 15 | get 'users.list', query: args 16 | end 17 | alias_method :users, :users_list 18 | 19 | def users_set_active 20 | get 'users.setActive' 21 | end 22 | alias_method :activate_user, :users_set_active 23 | 24 | def users_set_presence(args) 25 | get 'users.setPresence', query: args 26 | end 27 | alias_method :set_user_presence, :users_set_presence 28 | 29 | def user_by_name(name) 30 | users.find { |u| u if u.name == name } 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/breacan/configurable.rb: -------------------------------------------------------------------------------- 1 | require 'breacan/response/custom' 2 | 3 | module Breacan 4 | module Configurable 5 | OPTIONS_KEYS = %i( 6 | access_token 7 | team 8 | api_endpoint 9 | web_endpoint 10 | user_agent 11 | media_type 12 | connection_options 13 | proxy 14 | middleware 15 | serializer 16 | auto_paginate 17 | ) 18 | 19 | attr_accessor(*OPTIONS_KEYS) 20 | 21 | class << self 22 | def keys 23 | @keys ||= OPTIONS_KEYS 24 | end 25 | end 26 | 27 | def configure 28 | yield self 29 | end 30 | 31 | def api_endpoint 32 | File.join(@api_endpoint, '') 33 | end 34 | 35 | def web_endpoint 36 | File.join(@web_endpoint, '') 37 | end 38 | 39 | def reset! 40 | 41 | @access_token = ENV['BREACAN_ACCESS_TOKEN'] 42 | @team = ENV['BREACAN_TEAM'] 43 | @api_endpoint = ENV['BREACAN_API_ENDPOINT'] || 'https://slack.com/api/' 44 | @web_endpoint = ENV['BREACAN_WEB_ENDPOINT'] || "https://#{@team}.slack.com/" 45 | @user_agent = "Breacan Ruby Gem #{Breacan::VERSION}" 46 | @media_type = 'application/json' 47 | @connection_options = { headers: { accept: media_type, user_agent: user_agent } } 48 | @proxy = ENV['BREACAN_PROXY'] 49 | @middleware = default_builder 50 | @serializer = default_serializer 51 | @auto_paginate = !!ENV['BREACAN_AUTO_PAGINATE'] 52 | 53 | self 54 | end 55 | alias setup reset! 56 | 57 | def default_serializer 58 | Sawyer::Serializer.any_json 59 | end 60 | 61 | def default_builder 62 | builder = if defined? Faraday::RackBuilder 63 | Faraday::RackBuilder 64 | else 65 | Faraday::Builder 66 | end 67 | 68 | builder.new do |c| 69 | c.response :breacan_custom 70 | c.adapter Faraday.default_adapter 71 | end 72 | end 73 | 74 | def options 75 | Hash[Breacan::Configurable.keys.map { |key| 76 | [key, instance_variable_get(:"@#{key}")] 77 | }] 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/breacan/error.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | class Error < StandardError 3 | def self.from_response(response) 4 | status = response[:status].to_i 5 | if klass = case status 6 | when 400 then Breacan::BadRequest 7 | when 401 then Breacan::Unauthorized 8 | when 403 then Breacan::Forbidden 9 | when 404 then Breacan::NotFound 10 | when 406 then Breacan::NotAcceptable 11 | when 409 then Breacan::Conflict 12 | when 415 then Breacan::UnsupportedMediaType 13 | when 422 then Breacan::UnprocessableEntity 14 | when 400..499 then Breacan::ClientError 15 | when 500 then Breacan::InternalServerError 16 | when 501 then Breacan::NotImplemented 17 | when 502 then Breacan::BadGateway 18 | when 503 then Breacan::ServiceUnavailable 19 | when 500..599 then Breacan::ServerError 20 | end 21 | klass.new(response) 22 | end 23 | end 24 | 25 | def self.from_body(response, body) 26 | Breacan::BadRequest.new(response) unless body[:ok] 27 | end 28 | 29 | def initialize(response = nil) 30 | @response = response 31 | super(build_error_message) 32 | end 33 | 34 | def errors 35 | if data && data.is_a?(Hash) 36 | data[:errors] || [] 37 | else 38 | [] 39 | end 40 | end 41 | 42 | private 43 | 44 | def data 45 | @data ||= 46 | if (body = @response[:body]) && !body.empty? 47 | if body.is_a?(String) && 48 | @response[:response_headers] && 49 | @response[:response_headers][:content_type] =~ /json/ 50 | 51 | Sawyer::Agent.serializer.decode(body) 52 | else 53 | body 54 | end 55 | else 56 | nil 57 | end 58 | end 59 | 60 | def response_message 61 | case data 62 | when Hash 63 | data[:message] 64 | when String 65 | data 66 | end 67 | end 68 | 69 | def response_error 70 | "Error: #{data[:error]}" if data.is_a?(Hash) && data[:error] 71 | end 72 | 73 | def build_error_message 74 | return nil if @response.nil? 75 | 76 | message = "#{@response[:method].to_s.upcase} " 77 | message << redact_url(@response[:url].to_s) + ": " 78 | message << "#{@response[:status]} - " 79 | message << "#{response_message}" unless response_message.nil? 80 | message << "#{response_error}" unless response_error.nil? 81 | message 82 | end 83 | 84 | def redact_url(url_string) 85 | %w[token client_secret access_token].each do |token| 86 | url_string.gsub!(/#{token}=\S+/, "#{token}=(redacted)") if url_string.include? token 87 | end 88 | url_string 89 | end 90 | end 91 | 92 | class ClientError < Error; end 93 | class BadRequest < ClientError; end 94 | class Unauthorized < ClientError; end 95 | class Forbidden < ClientError; end 96 | class TooManyRequests < Forbidden; end 97 | class TooManyLoginAttempts < Forbidden; end 98 | class NotFound < ClientError; end 99 | class NotAcceptable < ClientError; end 100 | class Conflict < ClientError; end 101 | class UnsupportedMediaType < ClientError; end 102 | class UnprocessableEntity < ClientError; end 103 | class ServerError < Error; end 104 | class InternalServerError < ServerError; end 105 | class NotImplemented < ServerError; end 106 | class BadGateway < ServerError; end 107 | class ServiceUnavailable < ServerError; end 108 | class MissingContentType < ArgumentError; end 109 | end 110 | -------------------------------------------------------------------------------- /lib/breacan/response/custom.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | require 'breacan/error' 3 | 4 | module Breacan 5 | module Response 6 | # NOTE: Since faraday v.2, `Faraday::Response::Middleware` has been removed. We can use `Faraday::Middleware` instead. 7 | # ref: https://github.com/lostisland/faraday/blob/c9cc1b30ecebcd57beffa67d275f68742b5b05c5/UPGRADING.md?plain=1#L128 8 | middleware_base = defined?(Faraday::Response::Middleware) ? Faraday::Response::Middleware : Faraday::Middleware 9 | 10 | class Custom < middleware_base 11 | private 12 | 13 | def on_complete(res) 14 | if error = Breacan::Error.from_response(res) 15 | raise error 16 | end 17 | 18 | return if res[:body].empty? 19 | 20 | body = Sawyer::Agent.serializer.decode(res[:body]) 21 | if body[:ok] 22 | res[:body].gsub!('"ok":true,', '') 23 | end 24 | 25 | if error = Breacan::Error.from_body(res, body) 26 | raise error 27 | end 28 | end 29 | end 30 | 31 | Faraday::Response.register_middleware \ 32 | breacan_custom: -> { Breacan::Response::Custom } 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/breacan/version.rb: -------------------------------------------------------------------------------- 1 | module Breacan 2 | VERSION = '0.11.0' 3 | end 4 | --------------------------------------------------------------------------------