pechkin 1.3.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21153645eb45d32b113d2c95191e79ac5568a10829b3989ccc9f75da7b53bf9e
4
- data.tar.gz: d80b4bbbeb7e4bdfeb251e358bb0a9c99d50eae96c5078b688bce5e12a3c73e3
3
+ metadata.gz: fdd7554b43eb0ac8fa2fdb0fc9d02d31b4acf859c1b66a9470c1fdbd524d0eef
4
+ data.tar.gz: 224c8857ae9b72a48b91c8342040f9866d30a285a7f6e8595bf27e577f44bdd2
5
5
  SHA512:
6
- metadata.gz: 30e9654efb5c1a11c4816feb51fc494be1251e106a2fac682be9cb68738faec6e8b45c747329f90cff226ef1fe9df2c097bb0083c79d57d55b020982773dd0b8
7
- data.tar.gz: 62d6113f5ef1aedfe36c3eb86bc619d7cbac2cd9e54a37f64cbd293b1856efe47ba572bdf25efcc2a20f7b1affbbffe8cc2e74ad9784091cc69f7fa232ebe19b
6
+ metadata.gz: 623290e1546eb766cb844203af31e8f3708016199f4b1771e55c468cb252d21ecd874452795dc8417ba06d3be3e096526b3ad6c96826c84fa8189cd522e61ecb
7
+ data.tar.gz: 61f56c2dc2b400cced01460d977ee216d2c546be2935b3ce968f08c0266ad29d5915b14ea6601ca31f1497f984aba8eb9a1b4cfbbb27f5d358d44f49bd81157a
@@ -34,9 +34,10 @@ module Pechkin
34
34
  response(err.code, data)
35
35
  end
36
36
 
37
- def process_unhandled_error(_req, err)
37
+ def process_unhandled_error(req, err)
38
38
  data = { status: 'error', message: err.message }
39
39
  logger.error("#{err.message}\n\t" + err.backtrace.join("\n\t"))
40
+ logger.error(req.body.read)
40
41
  response(503, data)
41
42
  end
42
43
  end
@@ -15,9 +15,7 @@ module Pechkin
15
15
  use Prometheus::Middleware::Collector, registry: prometheus
16
16
  # Add Auth check if found htpasswd file or it was excplicitly provided
17
17
  # See CLI class for configuration details
18
- if options.htpasswd
19
- use Pechkin::Auth::Middleware, auth_file: options.htpasswd
20
- end
18
+ use Pechkin::Auth::Middleware, auth_file: options.htpasswd if options.htpasswd
21
19
  use Prometheus::Middleware::Exporter, registry: prometheus
22
20
 
23
21
  run app
@@ -34,7 +32,7 @@ module Pechkin
34
32
  file = File.open(log_file, File::WRONLY | File::APPEND)
35
33
  Logger.new(file)
36
34
  else
37
- Logger.new(STDOUT)
35
+ Logger.new($stdout)
38
36
  end
39
37
  end
40
38
  end
@@ -2,7 +2,7 @@ module Pechkin
2
2
  # Http requests handler. We need fresh instance per each request. To keep
3
3
  # internal state isolated
4
4
  class RequestHandler
5
- REQ_PATH_PATTERN = %r{^/(.+)/([^/]+)/?$}
5
+ REQ_PATH_PATTERN = %r{^/(.+)/([^/]+)/?$}.freeze
6
6
 
7
7
  attr_reader :req, :handler,
8
8
  :channel_id, :message_id,
data/lib/pechkin/auth.rb CHANGED
@@ -5,6 +5,7 @@ module Pechkin
5
5
  # Utility class for altering htpasswd files
6
6
  class Manager
7
7
  attr_reader :htpasswd
8
+
8
9
  def initialize(htpasswd)
9
10
  @htpasswd = htpasswd
10
11
  end
@@ -1,4 +1,3 @@
1
1
  module Pechkin
2
2
  Bot = Struct.new(:token, :connector, :name, keyword_init: true)
3
- Channel = Struct.new(:chat_ids, :connector, :messages, keyword_init: true)
4
3
  end
@@ -1,59 +1,3 @@
1
1
  module Pechkin
2
- # Creates object which can send messages to assigned chanels
3
- class Chanel
4
- attr_accessor :logger
5
-
6
- def initialize(connector, channel_list, logger = ::Logger.new(STDOUT))
7
- @connector = connector
8
- @channel_list = channel_list
9
- @channel_list = [channel_list] unless channel_list.is_a?(Array)
10
- @logger = logger
11
- end
12
-
13
- def send_message(message, data, message_desc)
14
- text = message.nil? ? '' : Message.new(data).render(message)
15
-
16
- message_desc = substitute(data, message_desc)
17
-
18
- logger.warn 'Resulting text is empty' if text.empty?
19
- results = @channel_list.map do |id|
20
- @connector.send_message(id, text, message_desc)
21
- end
22
-
23
- process_results(message, results)
24
- end
25
-
26
- private
27
-
28
- def substitute(data, message_desc)
29
- substitute_recursive(Substitute.new(data), message_desc)
30
- end
31
-
32
- def substitute_recursive(substitutions, object)
33
- case object
34
- when String
35
- substitutions.process(object)
36
- when Array
37
- object.map { |o| substitute_recursive(substitutions, o) }
38
- when Hash
39
- r = {}
40
- object.each { |k, v| r[k] = substitute_recursive(substitutions, v) }
41
- r
42
- else
43
- object
44
- end
45
- end
46
-
47
- def process_results(message, results)
48
- success, error = results.partition { |_chat, code, _body| code < 400 }
49
- error.each do |chat, code, body|
50
- logger.error "#{message} => #{chat}[HTTP #{code}]: #{body}"
51
- end
52
-
53
- {
54
- successful: success.map(&:first),
55
- errors: error
56
- }
57
- end
58
- end
2
+ Channel = Struct.new(:chat_ids, :connector, :messages, keyword_init: true)
59
3
  end
data/lib/pechkin/cli.rb CHANGED
@@ -13,7 +13,7 @@ module Pechkin
13
13
  # @opt names [Array<String>] list of command line keys
14
14
  # @opt desc [String] option description
15
15
  # @opt type [Class] argument type to parse from command line, e.g. Integer
16
- def opt(name, default: nil, names:, desc: '', type: nil)
16
+ def opt(name, names:, default: nil, desc: '', type: nil)
17
17
  @cli_options ||= []
18
18
 
19
19
  # raise ':names is nil or empty' if names.nil? || names.empty?
@@ -9,7 +9,7 @@ module Pechkin
9
9
  # command behaviour
10
10
  # @opt stdout [IO] IO object which represents STDOUT
11
11
  # @opt stderr [IO] IO object which represents STDERR
12
- def initialize(options, stdout: STDOUT, stderr: STDERR)
12
+ def initialize(options, stdout: $stdout, stderr: $stderr)
13
13
  @options = options
14
14
  @stdout = stdout
15
15
  @stderr = stderr
@@ -29,6 +29,7 @@ module Pechkin
29
29
  d = if data.start_with?('@')
30
30
  file = data[1..-1]
31
31
  raise "File not found #{file}" unless File.exist?(file)
32
+
32
33
  IO.read(file)
33
34
  else
34
35
  data
@@ -13,11 +13,11 @@ module Pechkin
13
13
  def create_connector(bot)
14
14
  case bot.connector
15
15
  when 'tg', 'telegram'
16
- TelegramConnector.new(bot.token, bot.name)
16
+ Connector::Telegram.new(bot.token, bot.name)
17
17
  when 'slack'
18
- SlackConnector.new(bot.token, bot.name)
18
+ Connector::Slack.new(bot.token, bot.name)
19
19
  else
20
- raise 'Unknown connector ' + bot.connector + ' for ' + bot.name
20
+ raise "Unknown connector #{bot.connector} for #{bot.name}"
21
21
  end
22
22
  end
23
23
 
@@ -15,9 +15,7 @@ module Pechkin
15
15
  def load_bots_configuration(working_dir, bots)
16
16
  bots_dir = File.join(working_dir, 'bots')
17
17
 
18
- unless File.directory?(bots_dir)
19
- raise ConfigurationError, "'#{bots_dir}' is not a directory"
20
- end
18
+ raise ConfigurationError, "'#{bots_dir}' is not a directory" unless File.directory?(bots_dir)
21
19
 
22
20
  Dir["#{bots_dir}/*.yml"].each do |bot_file|
23
21
  name = File.basename(bot_file, '.yml')
@@ -22,9 +22,7 @@ module Pechkin
22
22
  def load_channels_configuration(working_dir, channels)
23
23
  channels_dir = File.join(working_dir, 'channels')
24
24
 
25
- unless File.directory?(channels_dir)
26
- raise ConfigurationError, "'#{channels_dir}' is not a directory"
27
- end
25
+ raise ConfigurationError, "'#{channels_dir}' is not a directory" unless File.directory?(channels_dir)
28
26
 
29
27
  Dir["#{channels_dir}/*"].each do |channel_dir|
30
28
  next unless File.directory?(channel_dir)
@@ -63,16 +61,53 @@ module Pechkin
63
61
  message_config = YAML.safe_load(IO.read(file))
64
62
  name = File.basename(file, '.yml')
65
63
 
64
+ # Dirty workaround. I need to recursively load templates. When doing it
65
+ # we looking for {'template': '...path to template..' } objects. But we
66
+ # don't want to force user write something like:
67
+ # text:
68
+ # template: '... path to main template...'
69
+ # because it's too mouthful for such common case.
70
+ #
71
+ # So now we pull main template out, then load everyting else. Then put
72
+ # it back.
73
+ template = nil
66
74
  if message_config.key?('template')
67
- message_config['template'] = get_template(message_config['template'])
75
+ template = get_template(message_config['template'])
76
+ message_config.delete('template')
68
77
  end
69
78
 
70
- messages[name] = message_config
79
+ message_config = load_templates(message_config)
80
+ message_config['template'] = template unless template.nil?
81
+
82
+ messages[name] = Message.new(message_config)
71
83
  end
72
84
 
73
85
  messages
74
86
  end
75
87
 
88
+ def load_templates(object)
89
+ case object
90
+ when String
91
+ object
92
+ when Array
93
+ object.map { |o| load_templates(o) }
94
+ when Hash
95
+ if object.key?('template')
96
+ msg = 'When using template only 1 KV pair allowed'
97
+ raise ConfigurationError, msg unless object.size == 1
98
+
99
+ # Replace whole object with created template.
100
+ get_template(object['template'])
101
+ else
102
+ r = {}
103
+ object.each { |k, v| r[k] = load_templates(v) }
104
+ r
105
+ end
106
+ else
107
+ object
108
+ end
109
+ end
110
+
76
111
  def get_template(path)
77
112
  msg = "Can't find template: #{path}"
78
113
  raise ConfigurationError, msg unless @views.key?(path)
@@ -15,9 +15,7 @@ module Pechkin
15
15
  def load_views_configuration(working_dir, views)
16
16
  views_dir = File.join(working_dir, 'views')
17
17
 
18
- unless File.directory?(views_dir)
19
- raise ConfigurationError, "'#{views_dir}' is not a directory"
20
- end
18
+ raise ConfigurationError, "'#{views_dir}' is not a directory" unless File.directory?(views_dir)
21
19
 
22
20
  Dir["#{views_dir}/**/*.erb"].each do |f|
23
21
  relative_path = f["#{views_dir}/".length..-1]
@@ -1,6 +1,5 @@
1
1
  require 'yaml'
2
2
 
3
- require_relative 'configuration/model'
4
3
  require_relative 'configuration/configuration_loader'
5
4
  require_relative 'configuration/configuration_loader_bots'
6
5
  require_relative 'configuration/configuration_loader_channels'
@@ -0,0 +1,29 @@
1
+ module Pechkin
2
+ module Connector
3
+ # Base connector
4
+ class Base
5
+ DEFAULT_HEADERS = {
6
+ 'Content-Type' => 'application/json; charset=UTF-8'
7
+ }.freeze
8
+
9
+ def send_message(chat, message, message_desc); end
10
+
11
+ def preview(chats, message, _message_desc)
12
+ "Connector: #{self.class.name}; Chats: #{chats.join(', ')}\n" \
13
+ "Message:\n#{message}"
14
+ end
15
+
16
+ def post_data(url, data, headers: {})
17
+ uri = URI.parse(url)
18
+ headers = DEFAULT_HEADERS.merge(headers)
19
+ http = Net::HTTP.new(uri.host, uri.port)
20
+ http.use_ssl = url.start_with?('https://')
21
+
22
+ request = Net::HTTP::Post.new(uri.request_uri, headers)
23
+ request.body = data.to_json
24
+
25
+ http.request(request)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ module Pechkin
2
+ module Connector
3
+ class Slack < Connector::Base # :nodoc:
4
+ attr_reader :name
5
+
6
+ def initialize(bot_token, name)
7
+ super()
8
+
9
+ @headers = { 'Authorization' => "Bearer #{bot_token}" }
10
+ @name = name
11
+ end
12
+
13
+ def send_message(channel, message, message_desc)
14
+ text = CGI.unescape_html(message)
15
+
16
+ attachments = message_desc['slack_attachments'] || {}
17
+
18
+ if text.strip.empty? && attachments.empty?
19
+ return { channel: channel, code: 400,
20
+ response: 'Internal error: message is empty' }
21
+ end
22
+
23
+ params = { channel: channel, text: text, attachments: attachments }
24
+
25
+ url = 'https://slack.com/api/chat.postMessage'
26
+ response = post_data(url, params, headers: @headers)
27
+
28
+ { channel: channel, code: response.code.to_i, response: response.body }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ module Pechkin
2
+ module Connector
3
+ class Telegram < Connector::Base # :nodoc:
4
+ attr_reader :name
5
+
6
+ def initialize(bot_token, name)
7
+ super()
8
+
9
+ @bot_token = bot_token
10
+ @name = name
11
+ end
12
+
13
+ def send_message(chat_id, message, message_desc)
14
+ options = { parse_mode: message_desc['telegram_parse_mode'] || 'HTML' }
15
+ params = options.update(chat_id: chat_id, text: message)
16
+
17
+ response = post_data(method_url('sendMessage'), params)
18
+ { chat_id: chat_id, code: response.code.to_i, response: response.body }
19
+ end
20
+
21
+ private
22
+
23
+ def method_url(method)
24
+ "https://api.telegram.org/bot#{@bot_token}/#{method}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -4,26 +4,6 @@ require 'uri'
4
4
  require 'json'
5
5
  require 'cgi'
6
6
 
7
- module Pechkin
8
- # Base connector
9
- class Connector
10
- def send_message(chat, message, message_desc); end
11
-
12
- def preview(chats, message, _message_desc)
13
- "Connector: #{self.class.name}; Chats: #{chats.join(', ')}\n" \
14
- "Message:\n#{message}"
15
- end
16
-
17
- def post_data(url, data, headers: {})
18
- uri = URI.parse(url)
19
- headers = { 'Content-Type' => 'application/json' }.merge(headers)
20
- http = Net::HTTP.new(uri.host, uri.port)
21
- http.use_ssl = url.start_with?('https://')
22
-
23
- request = Net::HTTP::Post.new(uri.request_uri, headers)
24
- request.body = data.to_json
25
-
26
- http.request(request)
27
- end
28
- end
29
- end
7
+ require_relative 'connector/base'
8
+ require_relative 'connector/slack'
9
+ require_relative 'connector/telegram'
@@ -4,7 +4,10 @@ module Pechkin
4
4
  super("No such channel #{channel_name}")
5
5
  end
6
6
  end
7
+
7
8
  class MessageNotFoundError < StandardError; end
9
+
8
10
  class MessageContentIsEmptyError < StandardError; end
11
+
9
12
  class ConfigurationError < StandardError; end
10
13
  end
@@ -5,13 +5,13 @@ module Pechkin
5
5
  attr_reader :channels, :message_matcher
6
6
  attr_accessor :logger
7
7
 
8
- def initialize(channels, stdout = STDOUT, stderr = STDERR)
8
+ def initialize(channels, stdout = $stdout, stderr = $stderr)
9
9
  @channels = channels
10
10
  # Create empty logger by default
11
11
  @logger = Logger.new(IO::NULL)
12
12
  @stdout = stdout
13
13
  @stderr = stderr
14
- @message_matcher = MessageMatcher.new
14
+ @message_matcher = MessageMatcher.new(@logger)
15
15
  end
16
16
 
17
17
  # Handles message request. Each request has three parameters: channel id,
@@ -35,7 +35,7 @@ module Pechkin
35
35
  if message_allowed?(message_config, data)
36
36
  chats.map { |chat| connector.send_message(chat, text, message_config) }
37
37
  else
38
- logger.info "#{channel_id}/#{msg_id}: " \
38
+ logger.warn "#{channel_id}/#{msg_id}: " \
39
39
  "Skip sending message. Because it's not allowed"
40
40
  []
41
41
  end
@@ -95,13 +95,8 @@ module Pechkin
95
95
  def prepare_message(channel_id, msg_id, data)
96
96
  channel_config = fetch_channel(channel_id)
97
97
  # Find message and try substitute values to message parameters.
98
- message_config = substitute(data, fetch_message(channel_config, msg_id))
99
-
100
- data = (message_config['variables'] || {}).merge(data)
101
- template = message_config['template']
102
-
103
- text = ''
104
- text = template.render(data) unless template.nil?
98
+ message = fetch_message(channel_config, msg_id)
99
+ message_config, text = message.prepare(data)
105
100
 
106
101
  [channel_config, message_config, text]
107
102
  end
@@ -0,0 +1,64 @@
1
+ module Pechkin
2
+ # Message object
3
+ #
4
+ # TBD
5
+ class Message
6
+ def initialize(message)
7
+ @message = message
8
+ end
9
+
10
+ def prepare(data)
11
+ data = (@message['variables'] || {}).merge(data)
12
+ # Find message and try substitute values to message parameters.
13
+ message_config = render(data, substitute(data, @message))
14
+ text = ''
15
+ text = message_config.delete('template') if message_config.key?('template')
16
+
17
+ [message_config, text]
18
+ end
19
+
20
+ def to_h
21
+ Marshal.load(Marshal.dump(@message))
22
+ end
23
+
24
+ private
25
+
26
+ def substitute(data, message_desc)
27
+ substitute_recursive(Substitute.new(data), message_desc)
28
+ end
29
+
30
+ def substitute_recursive(substitutions, object)
31
+ case object
32
+ when String
33
+ substitutions.process(object)
34
+ when Array
35
+ object.map { |o| substitute_recursive(substitutions, o) }
36
+ when Hash
37
+ r = {}
38
+ object.each { |k, v| r[k] = substitute_recursive(substitutions, v) }
39
+ r
40
+ else
41
+ object
42
+ end
43
+ end
44
+
45
+ def render(data, message_desc)
46
+ render_recursive(data, message_desc)
47
+ end
48
+
49
+ def render_recursive(data, object)
50
+ case object
51
+ when MessageTemplate
52
+ object.render(data)
53
+ when Array
54
+ object.map { |o| render_recursive(data, o) }
55
+ when Hash
56
+ r = {}
57
+ object.each { |k, v| r[k] = render_recursive(data, v) }
58
+ r
59
+ else
60
+ object
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,5 +1,6 @@
1
1
  module Pechkin
2
2
  class MessageMatchError < StandardError; end
3
+
3
4
  # Allows to match message configuration against received data.
4
5
  #
5
6
  # Data is checked againts either allow or forbid rules. But not both at the
@@ -10,6 +11,13 @@ module Pechkin
10
11
  class MessageMatcher
11
12
  KEY_ALLOW = 'allow'.freeze
12
13
  KEY_FORBID = 'forbid'.freeze
14
+
15
+ attr_reader :logger
16
+
17
+ def initialize(logger)
18
+ @logger = logger
19
+ end
20
+
13
21
  # Checks data object against allow / forbid rule sets in message
14
22
  # configuration. If data object matches rules it means we can process this
15
23
  # data and send message.
@@ -23,10 +31,10 @@ module Pechkin
23
31
 
24
32
  if message_config.key?(KEY_ALLOW)
25
33
  rules = message_config[KEY_ALLOW]
26
- rules.any? { |r| check_rule(r, data) }
34
+ rules.any? { |r| check_rule(r, r, data) }
27
35
  elsif message_config.key?(KEY_FORBID)
28
36
  rules = message_config[KEY_FORBID]
29
- rules.all? { |r| !check_rule(r, data) }
37
+ rules.all? { |r| !check_rule(r, r, data) }
30
38
  else
31
39
  true
32
40
  end
@@ -43,35 +51,43 @@ module Pechkin
43
51
  # Check rule object against data. Rules are checked recursievly, i.e. we
44
52
  # can take one field in rule and check it separately as new rule. If all
45
53
  # fields are passed check then whole rule passed.
46
- def check_rule(rule, data)
47
- if rule.is_a?(Hash)
48
- check_hash_rule(rule, data)
49
- elsif rule.is_a?(Array)
50
- check_array_rule(rule, data)
51
- elsif rule.is_a?(String)
52
- check_string_rule(rule, data)
53
- else
54
- rule.eql?(data)
54
+ def check_rule(top_rule, sub_rule, data)
55
+ result = case sub_rule
56
+ when Hash
57
+ check_hash_rule(top_rule, sub_rule, data)
58
+ when Array
59
+ check_array_rule(top_rule, sub_rule, data)
60
+ when String
61
+ check_string_rule(top_rule, sub_rule, data)
62
+ else
63
+ sub_rule.eql?(data)
64
+ end
65
+
66
+ unless result
67
+ logger.info "Expected #{sub_rule.to_json} got #{data.to_json} when" \
68
+ " checking #{top_rule.to_json}"
55
69
  end
70
+
71
+ result
56
72
  end
57
73
 
58
- def check_hash_rule(hash, data)
74
+ def check_hash_rule(top_rule, hash, data)
59
75
  return false unless data.is_a?(Hash)
60
76
 
61
77
  hash.all? do |key, value|
62
- data.key?(key) && check_rule(value, data[key])
78
+ data.key?(key) && check_rule(top_rule, value, data[key])
63
79
  end
64
80
  end
65
81
 
66
- def check_array_rule(array, data)
82
+ def check_array_rule(top_rule, array, data)
67
83
  return false unless data.is_a?(Array)
68
84
 
69
85
  # Deep array check needs to be done against all elements so we zip arrays
70
86
  # to pair each rule with data element
71
- array.zip(data).all? { |r, d| check_rule(r, d) }
87
+ array.zip(data).all? { |r, d| check_rule(top_rule, r, d) }
72
88
  end
73
89
 
74
- def check_string_rule(str, data)
90
+ def check_string_rule(_top_rule, str, data)
75
91
  str.eql? data
76
92
  end
77
93
  end
@@ -5,6 +5,12 @@ module Pechkin
5
5
  registry = ::Prometheus::Client.registry
6
6
  registry.gauge(:pechkin_start_time_seconds,
7
7
  docstring: 'Startup timestamp').set(Time.now.to_i)
8
+
9
+ version_labels = { version: Pechkin::Version.version_string }
10
+ registry.gauge(:pechkin_version,
11
+ docstring: 'Pechkin version', labels: [:version])
12
+ .set(1, labels: version_labels)
13
+
8
14
  registry
9
15
  end
10
16
  end
@@ -1,7 +1,7 @@
1
1
  module Pechkin
2
2
  # Keeps actual version
3
3
  module Version
4
- VERSION = [1, 3, 1].freeze
4
+ VERSION = [1, 6, 0].freeze
5
5
  class << self
6
6
  def version_string
7
7
  VERSION.join('.')
data/lib/pechkin.rb CHANGED
@@ -8,15 +8,15 @@ require 'htauth'
8
8
  require 'base64'
9
9
 
10
10
  require_relative 'pechkin/cli'
11
+ require_relative 'pechkin/bot'
12
+ require_relative 'pechkin/channel'
13
+ require_relative 'pechkin/message'
11
14
  require_relative 'pechkin/command'
12
15
  require_relative 'pechkin/exceptions'
13
16
  require_relative 'pechkin/handler'
14
17
  require_relative 'pechkin/message_matcher'
15
18
  require_relative 'pechkin/message_template'
16
19
  require_relative 'pechkin/connector'
17
- require_relative 'pechkin/connector_slack'
18
- require_relative 'pechkin/connector_telegram'
19
- require_relative 'pechkin/channel'
20
20
  require_relative 'pechkin/configuration'
21
21
  require_relative 'pechkin/substitute'
22
22
  require_relative 'pechkin/prometheus_utils'
@@ -30,8 +30,8 @@ module Pechkin # :nodoc:
30
30
  cmd = Command::Dispatcher.new(options).dispatch
31
31
  cmd.execute
32
32
  rescue StandardError => e
33
- warn 'Error: ' + e.message
34
- warn "\t" + e.backtrace.reverse.join("\n\t") if options.debug?
33
+ warn "Error: #{e.message}"
34
+ warn "\t#{e.backtrace.reverse.join("\n\t")}" if options.debug?
35
35
  exit 2
36
36
  end
37
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pechkin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Arkhanhelsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-07 00:00:00.000000000 Z
11
+ date: 2021-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: htauth
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.0
19
+ version: 2.1.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.0
26
+ version: 2.1.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: powerpack
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 2.0.8
61
+ version: 2.2.3
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: 2.0.8
68
+ version: 2.2.3
69
69
  description:
70
70
  email: ilya.arkhanhelsky at gmail.com
71
71
  executables:
@@ -81,6 +81,7 @@ files:
81
81
  - lib/pechkin/app/app_error.rb
82
82
  - lib/pechkin/app/request_handler.rb
83
83
  - lib/pechkin/auth.rb
84
+ - lib/pechkin/bot.rb
84
85
  - lib/pechkin/channel.rb
85
86
  - lib/pechkin/cli.rb
86
87
  - lib/pechkin/command.rb
@@ -95,12 +96,13 @@ files:
95
96
  - lib/pechkin/configuration/configuration_loader_bots.rb
96
97
  - lib/pechkin/configuration/configuration_loader_channels.rb
97
98
  - lib/pechkin/configuration/configuration_loader_views.rb
98
- - lib/pechkin/configuration/model.rb
99
99
  - lib/pechkin/connector.rb
100
- - lib/pechkin/connector_slack.rb
101
- - lib/pechkin/connector_telegram.rb
100
+ - lib/pechkin/connector/base.rb
101
+ - lib/pechkin/connector/slack.rb
102
+ - lib/pechkin/connector/telegram.rb
102
103
  - lib/pechkin/exceptions.rb
103
104
  - lib/pechkin/handler.rb
105
+ - lib/pechkin/message.rb
104
106
  - lib/pechkin/message_matcher.rb
105
107
  - lib/pechkin/message_template.rb
106
108
  - lib/pechkin/prometheus_utils.rb
@@ -116,16 +118,16 @@ require_paths:
116
118
  - lib
117
119
  required_ruby_version: !ruby/object:Gem::Requirement
118
120
  requirements:
119
- - - ">="
121
+ - - ">"
120
122
  - !ruby/object:Gem::Version
121
- version: '0'
123
+ version: '2.5'
122
124
  required_rubygems_version: !ruby/object:Gem::Requirement
123
125
  requirements:
124
126
  - - ">="
125
127
  - !ruby/object:Gem::Version
126
128
  version: '0'
127
129
  requirements: []
128
- rubygems_version: 3.0.3
130
+ rubygems_version: 3.2.22
129
131
  signing_key:
130
132
  specification_version: 4
131
133
  summary: Web service to proxy webhooks to Telegram Bot API
@@ -1,28 +0,0 @@
1
- module Pechkin # :nodoc:
2
- class SlackConnector < Connector # :nodoc:
3
- attr_reader :name
4
-
5
- def initialize(bot_token, name)
6
- @headers = { 'Authorization' => "Bearer #{bot_token}" }
7
- @name = name
8
- end
9
-
10
- def send_message(channel, message, message_desc)
11
- text = CGI.unescape_html(message)
12
-
13
- attachments = message_desc['slack_attachments'] || {}
14
-
15
- if text.strip.empty? && attachments.empty?
16
- return { channel: channel, code: 400,
17
- response: 'Internal error: message is empty' }
18
- end
19
-
20
- params = { channel: channel, text: text, attachments: attachments }
21
-
22
- url = 'https://slack.com/api/chat.postMessage'
23
- response = post_data(url, params, headers: @headers)
24
-
25
- { channel: channel, code: response.code.to_i, response: response.body }
26
- end
27
- end
28
- end
@@ -1,24 +0,0 @@
1
- module Pechkin
2
- class TelegramConnector < Connector #:nodoc:
3
- attr_reader :name
4
-
5
- def initialize(bot_token, name)
6
- @bot_token = bot_token
7
- @name = name
8
- end
9
-
10
- def send_message(chat_id, message, message_desc)
11
- options = { parse_mode: message_desc['telegram_parse_mode'] || 'HTML' }
12
- params = options.update(chat_id: chat_id, text: message)
13
-
14
- response = post_data(method_url('sendMessage'), params)
15
- { chat_id: chat_id, code: response.code.to_i, response: response.body }
16
- end
17
-
18
- private
19
-
20
- def method_url(method)
21
- "https://api.telegram.org/bot#{@bot_token}/#{method}"
22
- end
23
- end
24
- end