pechkin 1.3.1 → 1.6.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 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