slack_cli 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ad43e690e15ffd38a98ac42584ecd59381891be4
4
+ data.tar.gz: 2f9aae7204c3e1846474774b3502c8b84f78afcf
5
+ SHA512:
6
+ metadata.gz: 15983cc24d35d068dcd1545cab55563747007626bb148a896928c30eb2a72d7667b89d69705192e0b4fbac3129b9bdc61df90fe7b8faf625f19e7c85752a1d43
7
+ data.tar.gz: ab5bce079b5faebe93edf000f28a2b6af4569ad73da0711c02f01e730de2f3fd3fcbc2a9facb56c9826bfab1860a453d592a8b6f1b6c9c8a59f0d1b87c52613f
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.4
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ ruby '> 2.3.0'
4
+
5
+ gemspec
6
+
7
+ group :development do
8
+ gem 'pry'
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,52 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ slack_cli (0.3.2)
5
+ dotenv (~> 2.2)
6
+ puma (~> 3.11)
7
+ rack (~> 2.0)
8
+ thor (~> 0.20)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ coderay (1.1.2)
14
+ diff-lcs (1.3)
15
+ dotenv (2.2.1)
16
+ method_source (0.9.0)
17
+ pry (0.11.3)
18
+ coderay (~> 1.1.0)
19
+ method_source (~> 0.9.0)
20
+ puma (3.11.2)
21
+ rack (2.0.4)
22
+ rake (10.5.0)
23
+ rspec (3.7.0)
24
+ rspec-core (~> 3.7.0)
25
+ rspec-expectations (~> 3.7.0)
26
+ rspec-mocks (~> 3.7.0)
27
+ rspec-core (3.7.1)
28
+ rspec-support (~> 3.7.0)
29
+ rspec-expectations (3.7.0)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.7.0)
32
+ rspec-mocks (3.7.0)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.7.0)
35
+ rspec-support (3.7.1)
36
+ thor (0.20.0)
37
+
38
+ PLATFORMS
39
+ ruby
40
+
41
+ DEPENDENCIES
42
+ bundler (~> 1.15)
43
+ pry
44
+ rake (~> 10.0)
45
+ rspec (~> 3.0)
46
+ slack_cli!
47
+
48
+ RUBY VERSION
49
+ ruby 2.5.0p0
50
+
51
+ BUNDLED WITH
52
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Slack CLI
2
+
3
+ A Slack CLI tool.
4
+
5
+ ## Usage
6
+
7
+ ```sh
8
+ $ slack help
9
+ Commands:
10
+ slack authorize # Authorize the CLI and get an access_token
11
+ slack help [COMMAND] # Describe available commands or one specific command
12
+ slack post TEXT -c, --channel=CHANNEL # Post message to channel or user
13
+ ```
14
+
15
+ ## Installation
16
+
17
+ Install from Rubygems
18
+
19
+ ```sh
20
+ $ gem install slack_cli
21
+ ```
22
+
23
+ ## Development
24
+
25
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
+
27
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ddrscott/slack_cli.
32
+
33
+ ## License
34
+
35
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/config.ru ADDED
@@ -0,0 +1,4 @@
1
+ require 'bundler/setup'
2
+ require 'slack_cli'
3
+
4
+ run SlackCLI::Server.new
data/exe/slack ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' # ruby1.9 doesn't "require" it though
4
+ require 'bundler/setup'
5
+ require 'slack_cli'
6
+ require 'thor'
7
+
8
+ # Thor options parser
9
+ class CLI < Thor
10
+ class_option :debug, type: :boolean, alias: '-d'
11
+ class_option :cache, type: :boolean, default: true, desc: 'Channels and Users are cached'
12
+
13
+ def self.exit_on_failure?
14
+ true
15
+ end
16
+
17
+ desc 'authorize', 'Authorize the CLI and get an access_token'
18
+ def authorize
19
+ SlackCLI.authorize
20
+ end
21
+
22
+ desc 'post', 'Post message to channel'
23
+ method_option :channel, required: true, aliases: '-c', desc: 'Channel to post the message'
24
+ def post(*text)
25
+ handle_class_options
26
+ SlackCLI.clear_cache unless options[:cache]
27
+ SlackCLI.post(channel: options[:channel], text: text.join(' '))
28
+ end
29
+
30
+ private
31
+
32
+ def handle_class_options
33
+ $DEBUG = options[:debug]
34
+ end
35
+ end
36
+
37
+ CLI.start
@@ -0,0 +1,88 @@
1
+ module SlackCLI
2
+ # Main Rack App for Callbacks
3
+ class Server
4
+
5
+ attr_accessor :webbrick
6
+
7
+ def call(env)
8
+ if env['REQUEST_PATH'] == '/callback/'
9
+ callback(env)
10
+ elsif env['REQUEST_PATH'] == '/token/'
11
+ save_token(env)
12
+ elsif env['REQUEST_PATH'] == '/'
13
+ index(env)
14
+ else
15
+ [404, {}, ['File not found']]
16
+ end
17
+ end
18
+
19
+ def index(env)
20
+ render_view(src: 'index.erb', binding: binding)
21
+ end
22
+
23
+ def callback(env)
24
+ request = Rack::Request.new(env)
25
+ slack_resp = fetch_token(code: request.params['code'])
26
+ [302, { 'Location' => "http://localhost:65187/token/?json=#{CGI.escape(slack_resp)}" }, ['redirecting...']]
27
+ end
28
+
29
+ def fetch_token(code:)
30
+ base_url = 'https://slack.com/api/oauth.access'
31
+ query = {
32
+ client_id: client_id,
33
+ client_secret: client_secret,
34
+ code: code
35
+ }
36
+ query_uri = query.map { |k, v| "#{k}=#{v}" }.join('&')
37
+ open("#{base_url}?#{query_uri}").read
38
+ end
39
+
40
+ def save_token(env)
41
+ request = Rack::Request.new(env)
42
+ json = JSON.parse(request['json'])
43
+ if json['error']
44
+ $stderr.puts "ERROR #{json['error']}"
45
+ FileUtils.rm_rf(authorization_path)
46
+ webbrick.shutdown if webbrick
47
+ raise Error, json['error']
48
+ end
49
+ FileUtils.mkdir_p(config_path)
50
+ File.open(authorization_path, 'w'){|f| f << request['json']}
51
+ render_view(src: 'save_token.erb', binding: binding).tap do
52
+ webbrick.shutdown if webbrick
53
+ end
54
+ end
55
+
56
+ def render_view(src:, binding:, status: 200)
57
+ full_src_path = File.expand_path(File.join(__dir__, '..', '..', 'views', src))
58
+ erb = ERB.new(File.read(full_src_path))
59
+ erb.filename = src
60
+ erb.result(binding)
61
+ [status, {'Content Type' => 'text/html'}, [erb.result(binding)]]
62
+ end
63
+
64
+ def authorization_path
65
+ File.join(config_path, 'authorization.json')
66
+ end
67
+
68
+ def config_path
69
+ File.expand_path(File.join('~', '.config', 'slack_cli'))
70
+ end
71
+
72
+ def save_token_url(token:)
73
+ "http://localhost:65187/token/#{token}"
74
+ end
75
+
76
+ def client_id
77
+ ENV['SLACK_CLIENT_ID'] || raise('SLACK_CLIENT_ID not found in ENV!')
78
+ end
79
+
80
+ def client_secret
81
+ ENV['SLACK_SECRET'] || raise('SLACK_SECRET not found in ENV!')
82
+ end
83
+
84
+ def scope
85
+ ENV['SLACK_SCOPE'] || raise('SLACK_SCOPE not found in ENV!')
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,3 @@
1
+ module SlackCLI
2
+ VERSION = '0.3.2'
3
+ end
data/lib/slack_cli.rb ADDED
@@ -0,0 +1,162 @@
1
+ require 'dotenv/load' # must come first
2
+
3
+ # The rest should be sorted unless there are dependencies
4
+ require 'cgi'
5
+ require 'digest/md5'
6
+ require 'erb'
7
+ require 'fileutils'
8
+ require 'json'
9
+ require 'logger'
10
+ require 'net/http'
11
+ require 'openssl'
12
+ require 'open-uri'
13
+ require 'rack'
14
+ require 'rack/handler/webrick'
15
+ require 'rack/request'
16
+
17
+ require 'slack_cli/server'
18
+
19
+ # Main module
20
+ module SlackCLI
21
+
22
+ class Error < StandardError; end
23
+
24
+ module_function
25
+
26
+ def string_logger
27
+ @logger ||= Logger.new(log_string_io)
28
+ end
29
+
30
+ def log_string_io
31
+ @log_string_io ||= StringIO.new
32
+ end
33
+
34
+ def clear_cache
35
+ FileUtils.rm_rf cache_path
36
+ end
37
+
38
+ def webrick_options
39
+ {
40
+ Port: 65187,
41
+ Logger: string_logger,
42
+ AccessLog: string_logger
43
+ }
44
+ end
45
+
46
+ def cli_app
47
+ @cli_app ||= SlackCLI::Server.new
48
+ end
49
+
50
+ def authorize
51
+ puts "Go to \e[1;32mhttp://localhost:65187/\e[0m to authenticate Slack CLI."
52
+ t = Thread.new do
53
+ Rack::Handler::WEBrick.run(cli_app, webrick_options) do |server|
54
+ cli_app.webbrick = server
55
+ end
56
+ end
57
+ t.join
58
+ if File.file?(cli_app.authorization_path)
59
+ puts "\e[32mSuccessfully authenticated.\e[0m"
60
+ else
61
+ puts 'There was a problem authorizing the token!'
62
+ puts log_string_io.read
63
+ end
64
+ end
65
+
66
+ def access_token
67
+ @access_token ||= begin
68
+ File.file?(cli_app.authorization_path) || raise("Application not authorized! Try calling `slack authorize`")
69
+ JSON.parse(File.read(cli_app.authorization_path))['access_token']
70
+ end
71
+ end
72
+
73
+ def bot_token
74
+ @access_token ||= begin
75
+ File.file?(cli_app.authorization_path) || raise("Application not authorized! Try calling `slack authorize`")
76
+ JSON.parse(File.read(cli_app.authorization_path))['bot']['bot_access_token']
77
+ end
78
+ end
79
+
80
+ def cache_path
81
+ @cache_path ||= begin
82
+ File.join(cli_app.config_path, 'cache').tap do |t|
83
+ FileUtils.mkdir_p(t)
84
+ end
85
+ end
86
+ end
87
+
88
+ def slack_get(path:, refresh: true)
89
+ url = "https://slack.com/api/#{path}"
90
+ cache_key = Digest::MD5.base64digest(url).gsub('=', '')
91
+ full_cache_path = File.join(cache_path, cache_key)
92
+ save = false
93
+ data = if refresh == true || !File.file?(full_cache_path)
94
+ save = true
95
+ open(
96
+ url,
97
+ 'Authorization' => "Bearer #{access_token}",
98
+ 'Content-type' => 'application/json'
99
+ ).read
100
+ else
101
+ File.read(full_cache_path)
102
+ end
103
+
104
+ json = JSON.parse(data)
105
+ if (error = json['error'])
106
+ raise Error, "[#{error['code']}] #{error['text']}"
107
+ end
108
+ File.open(full_cache_path, 'w'){|f| f << data} if save
109
+ json
110
+ end
111
+
112
+ # http.use_ssl = true
113
+ # http.verify_mode = OpenSSL::SSL::VERIFY_PEER
114
+ # http.set_debug_output($stdout) if $DEBUG
115
+ def slack_post(path:, json:)
116
+ uri = URI.parse("https://slack.com/api/#{path}")
117
+ http = Net::HTTP.new(uri.host, uri.port)
118
+ http.use_ssl = true
119
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
120
+ http.set_debug_output($stdout) if $DEBUG
121
+ header = {
122
+ 'Authorization' => "Bearer #{access_token}",
123
+ 'Content-Type' => 'application/json'
124
+ }
125
+ request = Net::HTTP::Post.new(uri, header)
126
+ request.body = json.to_json
127
+ resp = http.request(request)
128
+ JSON.parse(resp.body)
129
+ end
130
+
131
+ def channels(refresh: false)
132
+ slack_get(path: 'channels.list', refresh: refresh)
133
+ end
134
+
135
+ def channels_by_name
136
+ channels['channels'].each_with_object({}) do |row, agg|
137
+ agg[row['name']] = row
138
+ end
139
+ end
140
+
141
+ def users(refresh: false)
142
+ slack_get(path: 'users.list', refresh: refresh)
143
+ end
144
+
145
+ def users_by_name
146
+ users['members'].each_with_object({}) do |row, agg|
147
+ agg[row['name']] = row
148
+ end
149
+ end
150
+
151
+ def post(channel:, text:)
152
+ place = users_by_name[channel] || channels_by_name[channel]
153
+ if place
154
+ slack_post(
155
+ path: 'chat.postMessage',
156
+ json: { channel: place['id'], text: text }
157
+ )
158
+ else
159
+ raise "No channel or user found for `#{channel}`"
160
+ end
161
+ end
162
+ end
data/slack_cli.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'slack_cli/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'slack_cli'
8
+ spec.version = SlackCLI::VERSION
9
+ spec.authors = ['Scott Pierce']
10
+ spec.email = ['ddrscott@gmail.com']
11
+
12
+ spec.summary = 'Slack CLI'
13
+ spec.homepage = 'https://github.com/ddrscott/slack_cli'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.15'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.0'
26
+
27
+ spec.add_dependency 'dotenv', '~> 2.2'
28
+ spec.add_dependency 'puma', '~> 3.11'
29
+ spec.add_dependency 'rack', '~> 2.0'
30
+ spec.add_dependency 'thor', '~> 0.20'
31
+ end
data/views/index.erb ADDED
@@ -0,0 +1,49 @@
1
+ <html class="no-js" lang="">
2
+ <head>
3
+ <meta charset="utf-8">
4
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
5
+ <title>Slack CLI</title>
6
+ <meta name="viewport" content="width=device-width">
7
+ <style>
8
+ * {
9
+ box-sizing: border-box;
10
+ }
11
+ html, body {
12
+ height: 100%;
13
+ width: 100%;
14
+ }
15
+ html {
16
+ }
17
+ body {
18
+ padding: 1em;
19
+ margin: 0;
20
+ font-family: sans-serif;
21
+ }
22
+ .center-children {
23
+ margin: 0 auto;
24
+ display: flex;
25
+ justify-content: center;
26
+ align-items: center;
27
+ resize: both;
28
+ overflow: auto;
29
+ }
30
+ .center {
31
+ display: block;
32
+ margin-left: auto;
33
+ margin-right: auto;
34
+ text-align: center;
35
+ }
36
+ a,img {
37
+ display: block
38
+ }
39
+ </style>
40
+ </head>
41
+ <body class="center-children">
42
+ <div class="center">
43
+ <h1>Slack CLI</h1>
44
+ <img class="center" style="margin-top: -1em; width: 5em; height: 5em;" src="https://slack-files2.s3-us-west-2.amazonaws.com/avatars/2018-02-28/322540051491_005c180801b07413d867_512.png"></img>
45
+ <a href="https://slack.com/oauth/authorize?client_id=322607959140.322080377537&scope=bot,chat:write:user,channels:read,users:read"><img alt="Add to Slack" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x" /></a>
46
+ </div>
47
+ </body>
48
+ </html>
49
+
@@ -0,0 +1,14 @@
1
+ <html class="no-js" lang="">
2
+ <head>
3
+ <meta charset="utf-8">
4
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
5
+ <title>Slack CLI</title>
6
+ <meta name="viewport" content="width=device-width">
7
+ </head>
8
+ <body>
9
+ <h1>Authorization Complete</h1>
10
+ Token saved to: <pre><%= authorization_path %></pre>
11
+ <p>This window may be closed.</p>
12
+ </body>
13
+ </html>
14
+
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: slack_cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ platform: ruby
6
+ authors:
7
+ - Scott Pierce
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-03-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dotenv
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: puma
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.11'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.11'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: thor
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.20'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.20'
111
+ description:
112
+ email:
113
+ - ddrscott@gmail.com
114
+ executables:
115
+ - slack
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".ruby-version"
120
+ - Gemfile
121
+ - Gemfile.lock
122
+ - README.md
123
+ - Rakefile
124
+ - config.ru
125
+ - exe/slack
126
+ - lib/slack_cli.rb
127
+ - lib/slack_cli/server.rb
128
+ - lib/slack_cli/version.rb
129
+ - slack_cli.gemspec
130
+ - views/index.erb
131
+ - views/save_token.erb
132
+ homepage: https://github.com/ddrscott/slack_cli
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.5.2
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Slack CLI
156
+ test_files: []