codebot 1.2.0
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 +7 -0
- data/.github/ISSUE_TEMPLATE.md +32 -0
- data/.github/ISSUE_TEMPLATE/formatter_issue.md +20 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +26 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +15 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +75 -0
- data/LICENSE +21 -0
- data/README.md +230 -0
- data/Rakefile +29 -0
- data/bin/console +8 -0
- data/codebot.gemspec +49 -0
- data/exe/codebot +7 -0
- data/lib/codebot.rb +8 -0
- data/lib/codebot/channel.rb +134 -0
- data/lib/codebot/command_error.rb +17 -0
- data/lib/codebot/config.rb +125 -0
- data/lib/codebot/configuration_error.rb +17 -0
- data/lib/codebot/core.rb +76 -0
- data/lib/codebot/cryptography.rb +38 -0
- data/lib/codebot/event.rb +62 -0
- data/lib/codebot/ext/cinch/ssl_extensions.rb +37 -0
- data/lib/codebot/formatter.rb +242 -0
- data/lib/codebot/formatters.rb +109 -0
- data/lib/codebot/formatters/.rubocop.yml +2 -0
- data/lib/codebot/formatters/commit_comment.rb +43 -0
- data/lib/codebot/formatters/fork.rb +40 -0
- data/lib/codebot/formatters/gitlab_issue_hook.rb +56 -0
- data/lib/codebot/formatters/gitlab_job_hook.rb +77 -0
- data/lib/codebot/formatters/gitlab_merge_request_hook.rb +57 -0
- data/lib/codebot/formatters/gitlab_note_hook.rb +119 -0
- data/lib/codebot/formatters/gitlab_pipeline_hook.rb +51 -0
- data/lib/codebot/formatters/gitlab_push_hook.rb +83 -0
- data/lib/codebot/formatters/gitlab_wiki_page_hook.rb +56 -0
- data/lib/codebot/formatters/gollum.rb +67 -0
- data/lib/codebot/formatters/issue_comment.rb +41 -0
- data/lib/codebot/formatters/issues.rb +41 -0
- data/lib/codebot/formatters/ping.rb +79 -0
- data/lib/codebot/formatters/public.rb +30 -0
- data/lib/codebot/formatters/pull_request.rb +71 -0
- data/lib/codebot/formatters/pull_request_review_comment.rb +49 -0
- data/lib/codebot/formatters/push.rb +172 -0
- data/lib/codebot/formatters/watch.rb +38 -0
- data/lib/codebot/integration.rb +195 -0
- data/lib/codebot/integration_manager.rb +225 -0
- data/lib/codebot/ipc_client.rb +83 -0
- data/lib/codebot/ipc_server.rb +79 -0
- data/lib/codebot/irc_client.rb +102 -0
- data/lib/codebot/irc_connection.rb +156 -0
- data/lib/codebot/message.rb +37 -0
- data/lib/codebot/metadata.rb +15 -0
- data/lib/codebot/network.rb +240 -0
- data/lib/codebot/network_manager.rb +181 -0
- data/lib/codebot/options.rb +49 -0
- data/lib/codebot/options/base.rb +55 -0
- data/lib/codebot/options/core.rb +126 -0
- data/lib/codebot/options/integration.rb +101 -0
- data/lib/codebot/options/network.rb +109 -0
- data/lib/codebot/payload.rb +32 -0
- data/lib/codebot/request.rb +51 -0
- data/lib/codebot/sanitizers.rb +130 -0
- data/lib/codebot/serializable.rb +101 -0
- data/lib/codebot/shortener.rb +43 -0
- data/lib/codebot/thread_controller.rb +70 -0
- data/lib/codebot/user_error.rb +13 -0
- data/lib/codebot/validation_error.rb +17 -0
- data/lib/codebot/web_listener.rb +107 -0
- data/lib/codebot/web_server.rb +58 -0
- data/webhook.png +0 -0
- metadata +249 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'codebot/command_error'
|
4
|
+
|
5
|
+
module Codebot
|
6
|
+
# This class manages the networks associated with a configuration.
|
7
|
+
class NetworkManager
|
8
|
+
# @return [Config] the configuration managed by this class
|
9
|
+
attr_reader :config
|
10
|
+
|
11
|
+
# Constructs a new network manager for a specified configuration.
|
12
|
+
#
|
13
|
+
# @param config [Config] the configuration to manage
|
14
|
+
def initialize(config)
|
15
|
+
@config = config
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a new network from the given parameters.
|
19
|
+
#
|
20
|
+
# @param params [Hash] the parameters to initialize the network with
|
21
|
+
def create(params)
|
22
|
+
network = Network.new(params.merge(config: {}))
|
23
|
+
@config.transaction do
|
24
|
+
check_name_available!(network.name)
|
25
|
+
@config.networks << network
|
26
|
+
network_feedback(network, :created) unless params[:quiet]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Updates a network with the given parameters.
|
31
|
+
#
|
32
|
+
# @param name [String] the current name of the network to update
|
33
|
+
# @param params [Hash] the parameters to update the network with
|
34
|
+
def update(name, params)
|
35
|
+
@config.transaction do
|
36
|
+
network = find_network!(name)
|
37
|
+
unless params[:name].nil?
|
38
|
+
check_name_available_except!(params[:name], network)
|
39
|
+
end
|
40
|
+
network.update!(params)
|
41
|
+
network_feedback(network, :updated) unless params[:quiet]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Destroys a network.
|
46
|
+
#
|
47
|
+
# @param name [String] the name of the network to destroy
|
48
|
+
# @param params [Hash] the command-line options
|
49
|
+
def destroy(name, params)
|
50
|
+
@config.transaction do
|
51
|
+
network = find_network!(name)
|
52
|
+
@config.networks.delete network
|
53
|
+
network_feedback(network, :destroyed) unless params[:quiet]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Lists all networks, or networks with names containing the given search
|
58
|
+
# term.
|
59
|
+
#
|
60
|
+
# @param search [String, nil] an optional search term
|
61
|
+
def list(search)
|
62
|
+
@config.transaction do
|
63
|
+
networks = @config.networks.dup
|
64
|
+
unless search.nil?
|
65
|
+
networks.select! { |net| net.name.downcase.include? search.downcase }
|
66
|
+
end
|
67
|
+
puts 'No networks found' if networks.empty?
|
68
|
+
networks.each { |net| show_network net }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Finds a network given its name.
|
73
|
+
#
|
74
|
+
# @param name [String] the name to search for
|
75
|
+
# @return [Network, nil] the network, or +nil+ if none was found
|
76
|
+
def find_network(name)
|
77
|
+
@config.networks.find { |net| net.name_eql? name }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Finds a network given its name.
|
81
|
+
#
|
82
|
+
# @param name [String] the name to search for
|
83
|
+
# @raise [CommandError] if no network with the given name exists
|
84
|
+
# @return [Network] the network
|
85
|
+
def find_network!(name)
|
86
|
+
network = find_network(name)
|
87
|
+
return network unless network.nil?
|
88
|
+
|
89
|
+
raise CommandError, "a network with the name #{name.inspect} " \
|
90
|
+
'does not exist'
|
91
|
+
end
|
92
|
+
|
93
|
+
# Checks that all channels associated with an integration belong to a valid
|
94
|
+
# network.
|
95
|
+
#
|
96
|
+
# @param integration [Integration] the integration to check
|
97
|
+
def check_channels!(integration)
|
98
|
+
integration.channels.map(&:network).map(&:name).each do |network|
|
99
|
+
find_network!(network)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# Checks that the specified name is available for use.
|
106
|
+
#
|
107
|
+
# @param name [String] the name to check for
|
108
|
+
# @raise [CommandError] if the name is already taken
|
109
|
+
def check_name_available!(name)
|
110
|
+
return if name.nil? || !find_network(name)
|
111
|
+
|
112
|
+
raise CommandError, "a network with the name #{name.inspect} " \
|
113
|
+
'already exists'
|
114
|
+
end
|
115
|
+
|
116
|
+
# Checks that the specified name is available for use by the specified
|
117
|
+
# network.
|
118
|
+
#
|
119
|
+
# @param name [String] the name to check for
|
120
|
+
# @param network [Network] the network to ignore
|
121
|
+
# @raise [CommandError] if the name is already taken
|
122
|
+
def check_name_available_except!(name, network)
|
123
|
+
return if name.nil? || network.name_eql?(name) || !find_network(name)
|
124
|
+
|
125
|
+
raise CommandError, "a network with the name #{name.inspect} " \
|
126
|
+
'already exists'
|
127
|
+
end
|
128
|
+
|
129
|
+
# Displays feedback about a change made to a network.
|
130
|
+
#
|
131
|
+
# @param network [Network] the network
|
132
|
+
# @param action [#to_s] the action (+:created+, +:updated+ or +:destroyed+)
|
133
|
+
def network_feedback(network, action)
|
134
|
+
puts "Network was successfully #{action}"
|
135
|
+
show_network(network)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Prints information about a network.
|
139
|
+
#
|
140
|
+
# @param network [Network] the network
|
141
|
+
def show_network(network) # rubocop:disable Metrics/AbcSize
|
142
|
+
puts "Network: #{network.name}"
|
143
|
+
security = "#{network.secure ? 'secure' : 'insecure'} connection"
|
144
|
+
password = network.server_password
|
145
|
+
puts "\tServer: #{network.host}:#{network.real_port} (#{security})"
|
146
|
+
puts "\tPassword: #{'*' * password.length}" unless password.to_s.empty?
|
147
|
+
puts "\tNickname: #{network.nick}"
|
148
|
+
puts "\tBind to: #{network.bind}" unless network.bind.to_s.empty?
|
149
|
+
puts "\tUser modes: #{network.modes}" unless network.modes.to_s.empty?
|
150
|
+
show_network_sasl(network)
|
151
|
+
show_network_nickserv(network)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Prints information about the SASL authentication settings for a network.
|
155
|
+
#
|
156
|
+
# @param network [Network] the network
|
157
|
+
def show_network_sasl(network)
|
158
|
+
puts "\tSASL authentication #{network.sasl? ? 'enabled' : 'disabled'}"
|
159
|
+
return unless network.sasl?
|
160
|
+
|
161
|
+
puts "\t\tUsername: #{network.sasl_username}"
|
162
|
+
puts "\t\tPassword: #{'*' * network.sasl_password.to_s.length}"
|
163
|
+
end
|
164
|
+
|
165
|
+
def nickserv_status(network)
|
166
|
+
network.nickserv? ? 'enabled' : 'disabled'
|
167
|
+
end
|
168
|
+
|
169
|
+
# Prints information about the NickServ authentication
|
170
|
+
# settings for a network.
|
171
|
+
#
|
172
|
+
# @param network [Network] the network
|
173
|
+
def show_network_nickserv(network)
|
174
|
+
puts "\tNickServ authentication #{nickserv_status(network)}"
|
175
|
+
return unless network.nickserv?
|
176
|
+
|
177
|
+
puts "\t\tUsername: #{network.nickserv_username}"
|
178
|
+
puts "\t\tPassword: #{'*' * network.nickserv_password.to_s.length}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
require 'codebot/options/base'
|
6
|
+
|
7
|
+
module Codebot
|
8
|
+
# This module provides functionality for parsing command-line options.
|
9
|
+
module Options
|
10
|
+
# Creates a new {Core} from the specified command-line options. Errors of
|
11
|
+
# type {UserError} are handled if they occur in the given block.
|
12
|
+
#
|
13
|
+
# @param opts [Hash] the options to initialize the core with
|
14
|
+
# @param rehash [Boolean] whether to ask a running instance to rehash its
|
15
|
+
# configuration after invoking the block
|
16
|
+
# @yield [Core] the newly created {Core}
|
17
|
+
def self.with_core(opts, rehash = false)
|
18
|
+
core = ::Codebot::Core.new(
|
19
|
+
config_file: opts[:config],
|
20
|
+
ipc_pipe: opts[:pipe]
|
21
|
+
)
|
22
|
+
with_errors { yield core }
|
23
|
+
return unless rehash
|
24
|
+
|
25
|
+
with_ipc_client(opts) do |ipc|
|
26
|
+
break unless ipc.send_rehash(!opts[:pipe].nil?)
|
27
|
+
|
28
|
+
puts 'Rehashing the running instance...' unless opts[:quiet]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Invokes the given block, handling {UserError} errors.
|
33
|
+
def self.with_errors
|
34
|
+
yield
|
35
|
+
rescue UserError => e
|
36
|
+
STDERR.puts "Error: #{e.message}"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
40
|
+
# Creates a new {IPCClient} from the specified command-line options.
|
41
|
+
# Errors of type {UserError} are handled if they occur in the given block.
|
42
|
+
#
|
43
|
+
# @param opts [Hash] the options to initialize the client with
|
44
|
+
# @yield [IPCClient] the newly created {IPCClient}
|
45
|
+
def self.with_ipc_client(opts)
|
46
|
+
with_errors { yield IPCClient.new(opts[:pipe]) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
require 'codebot/options/core'
|
6
|
+
require 'codebot/options/network'
|
7
|
+
require 'codebot/options/integration'
|
8
|
+
require 'codebot/user_error'
|
9
|
+
|
10
|
+
module Codebot
|
11
|
+
module Options
|
12
|
+
# A class that handles the +codebot+ command. This class delegates handling
|
13
|
+
# of any commands to the respective class for the appropriate subcommand.
|
14
|
+
class Base < Thor
|
15
|
+
check_unknown_options!
|
16
|
+
|
17
|
+
class_option :config,
|
18
|
+
banner: 'FILE',
|
19
|
+
aliases: '-C',
|
20
|
+
desc: 'Use the specified alternate configuration file'
|
21
|
+
class_option :pipe,
|
22
|
+
banner: 'FILE',
|
23
|
+
aliases: '-P',
|
24
|
+
desc: 'Use the specified alternate named pipe'
|
25
|
+
class_option :quiet,
|
26
|
+
type: :boolean,
|
27
|
+
default: false,
|
28
|
+
aliases: '-q',
|
29
|
+
desc: 'Hide status information'
|
30
|
+
|
31
|
+
desc 'core [OPTIONS]', 'Manage a Codebot core'
|
32
|
+
subcommand 'core', Core
|
33
|
+
|
34
|
+
desc 'network [OPTIONS]', 'Manage IRC networks'
|
35
|
+
subcommand 'network', Network
|
36
|
+
|
37
|
+
desc 'integration [OPTIONS]', 'Manage integrations'
|
38
|
+
subcommand 'integration', Integration
|
39
|
+
|
40
|
+
# Ensures that thor uses the correct exit code.
|
41
|
+
#
|
42
|
+
# @return true
|
43
|
+
def self.exit_on_failure?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
if Process.uid.zero? || Process.euid.zero?
|
48
|
+
STDERR.puts 'Running Codebot as root is extremely dangerous; ' \
|
49
|
+
"if you're trying to listen on a privileged port " \
|
50
|
+
'please use a gateway server instead'
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'codebot/ipc_client'
|
4
|
+
|
5
|
+
module Codebot
|
6
|
+
module Options
|
7
|
+
# A class that handles the +codebot core+ command.
|
8
|
+
class Core < Thor
|
9
|
+
check_unknown_options!
|
10
|
+
|
11
|
+
desc 'interactive', 'Run Codebot interactively'
|
12
|
+
|
13
|
+
# Runs Codebot interactively.
|
14
|
+
def interactive
|
15
|
+
run_core(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'start', 'Start a new Codebot instance in the background'
|
19
|
+
|
20
|
+
# Starts a new Codebot instance in the background.
|
21
|
+
def start
|
22
|
+
Options.with_errors { check_fork_supported! }
|
23
|
+
check_not_running!(options)
|
24
|
+
fork { run_core(false) }
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'stop', 'Stop a running Codebot instance'
|
28
|
+
|
29
|
+
# Stops a running Codebot instance.
|
30
|
+
def stop
|
31
|
+
Options.with_ipc_client(options, &:send_stop)
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'rehash', 'Reload the configuration of a running Codebot instance'
|
35
|
+
|
36
|
+
# Reloads the configuration of a running Codebot instance.
|
37
|
+
def rehash
|
38
|
+
Options.with_ipc_client(options, &:send_rehash)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Ensures that thor uses the correct exit code.
|
42
|
+
#
|
43
|
+
# @return true
|
44
|
+
def self.exit_on_failure?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Ensures that a Codebot instance using the same pipe is not already
|
51
|
+
# running.
|
52
|
+
#
|
53
|
+
# @param opts [Hash] a hash containing the options that would be used for
|
54
|
+
# initializing a new core; specifically, a hash
|
55
|
+
# containing the +:pipe+ key to indicate the path to
|
56
|
+
# the named pipe used by the IPC server.
|
57
|
+
# @raise [CommandError] if the named pipe already exists
|
58
|
+
def check_not_running!(opts)
|
59
|
+
Options.with_ipc_client(opts) do |ipc|
|
60
|
+
break unless ipc.pipe_exist?
|
61
|
+
|
62
|
+
raise CommandError, 'named pipe already exists; if you are sure a ' \
|
63
|
+
'Codebot instance is not already running, you ' \
|
64
|
+
"can delete #{ipc.pipe.inspect}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Ensures that the current platform supports the Process::fork method,
|
69
|
+
# raising an error if it does not.
|
70
|
+
#
|
71
|
+
# @raise [CommandError] if forking is not supported
|
72
|
+
def check_fork_supported!
|
73
|
+
return if Process.respond_to?(:fork)
|
74
|
+
|
75
|
+
raise CommandError, 'this feature is not available on ' \
|
76
|
+
"#{RUBY_PLATFORM}; please use the " \
|
77
|
+
"'interactive' command instead"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Reopens the standard file descriptors to prevent the forked process
|
81
|
+
# from inheriting the file descriptors of the parent process. This method
|
82
|
+
# reopens streams using a method similar to the Unix dup2 function.
|
83
|
+
#
|
84
|
+
# @param sin [File] the file to redirect into the standard input stream,
|
85
|
+
# or +nil+ to detach and immediately close the stream.
|
86
|
+
# @param sout [File] the file to redirect the standard output stream to,
|
87
|
+
# or +nil+ to discard any data written to the stream.
|
88
|
+
# @param serr [File] the file to redirect the standard error stream to,
|
89
|
+
# or +nil+ to discard any data written to the stream.
|
90
|
+
def dup2_fds(sin: nil, sout: nil, serr: nil)
|
91
|
+
$stdin.reopen(sin || null_file('r'))
|
92
|
+
$stdout.reopen(sout || null_file('w'))
|
93
|
+
$stderr.reopen(serr || null_file('w'))
|
94
|
+
end
|
95
|
+
|
96
|
+
# Creates a new null file.
|
97
|
+
#
|
98
|
+
# @param mode [String] the mode to open the file in
|
99
|
+
# @return [File] the created file
|
100
|
+
def null_file(mode)
|
101
|
+
File.new(File::NULL, mode)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Initializes any missing environment variables to their default values.
|
105
|
+
def initialize_environment
|
106
|
+
ENV['CODEBOT_PORT'] ||= 4567.to_s
|
107
|
+
ENV['RACK_ENV'] ||= 'production'
|
108
|
+
end
|
109
|
+
|
110
|
+
# Starts the bot. Unless started in interactive mode, file descriptors
|
111
|
+
# are reopened from a null file.
|
112
|
+
#
|
113
|
+
# @param interactive [Boolean] whether to start the bot in the foreground
|
114
|
+
def run_core(interactive)
|
115
|
+
initialize_environment
|
116
|
+
check_not_running!(options)
|
117
|
+
dup2_fds unless interactive
|
118
|
+
Options.with_core(options) do |core|
|
119
|
+
core.trap_signals
|
120
|
+
core.start
|
121
|
+
core.join
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'codebot/integration_manager'
|
4
|
+
|
5
|
+
module Codebot
|
6
|
+
module Options
|
7
|
+
# A class that handles the +codebot integration+ command.
|
8
|
+
class Integration < Thor
|
9
|
+
check_unknown_options!
|
10
|
+
|
11
|
+
# Sets shared options for specifying properties belonging to the
|
12
|
+
# {::Codebot::Integration} class.
|
13
|
+
def self.shared_propery_options
|
14
|
+
option :endpoint, aliases: '-e',
|
15
|
+
desc: 'Set the endpoint for incoming webhooks'
|
16
|
+
option :secret, aliases: '-s',
|
17
|
+
desc: 'Set the secret for verifying webhook payloads'
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'create NAME', 'Add a new integration'
|
21
|
+
shared_propery_options
|
22
|
+
option :channels, aliases: '-c', type: :array,
|
23
|
+
desc: 'Set the channels to deliver notifications to'
|
24
|
+
|
25
|
+
# Creates a new integration with the specified name.
|
26
|
+
#
|
27
|
+
# @param name [String] the name of the new integration
|
28
|
+
def create(name)
|
29
|
+
Options.with_core(parent_options, true) do |core|
|
30
|
+
map_channels!(options, :channels)
|
31
|
+
IntegrationManager.new(core.config).create(options.merge(name: name))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'update NAME', 'Edit an integration'
|
36
|
+
option :name, aliases: '-n',
|
37
|
+
banner: 'NEW-NAME',
|
38
|
+
desc: 'Rename this integration'
|
39
|
+
shared_propery_options
|
40
|
+
option :add_channel, aliases: '-a', type: :array,
|
41
|
+
desc: 'Add a channel to this integration'
|
42
|
+
option :clear_channels, aliases: '-c', type: :boolean,
|
43
|
+
desc: 'Clear the channel list ' \
|
44
|
+
'(default: false)'
|
45
|
+
option :delete_channel, aliases: '-d', type: :array,
|
46
|
+
desc: 'Delete a channel from this integration'
|
47
|
+
|
48
|
+
# Updates the integration with the specified name.
|
49
|
+
#
|
50
|
+
# @param name [String] the name of the integration
|
51
|
+
def update(name)
|
52
|
+
Options.with_core(parent_options, true) do |core|
|
53
|
+
map_channels!(options, :add_channel)
|
54
|
+
IntegrationManager.new(core.config).update(name, options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'destroy NAME', 'Delete an integration'
|
59
|
+
|
60
|
+
# Destroys the integration with the specified name.
|
61
|
+
#
|
62
|
+
# @param name [String] the name of the integration
|
63
|
+
def destroy(name)
|
64
|
+
Options.with_core(parent_options, true) do |core|
|
65
|
+
IntegrationManager.new(core.config).destroy(name, options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'list [SEARCH]', 'List integrations'
|
70
|
+
|
71
|
+
# Lists all integrations, or integrations with names containing the given
|
72
|
+
# search term.
|
73
|
+
#
|
74
|
+
# @param search [String, nil] an optional search term
|
75
|
+
def list(search = nil)
|
76
|
+
Options.with_core(parent_options, true) do |core|
|
77
|
+
IntegrationManager.new(core.config).list(search)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Ensures that thor uses the correct exit code.
|
82
|
+
#
|
83
|
+
# @return true
|
84
|
+
def self.exit_on_failure?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Destructively converts an array of channel identifiers contained in a
|
91
|
+
# hash into the serialized form of the channels contained in the array.
|
92
|
+
# If the value +hash[key]+ is +nil+, no action is taken.
|
93
|
+
#
|
94
|
+
# @param hash [Hash] the hash containing the array of identifiers
|
95
|
+
# @param key [Object] the key corresponding to the array of identifiers
|
96
|
+
def map_channels!(hash, key)
|
97
|
+
hash[key] = hash[key].map { |id| [id, {}] }.to_h unless hash[key].nil?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|