slack_cli 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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: []