pechkin 2.0.2 → 2.1.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: 2ec4615e84c716a8145cc2eb23ffce1a1cb252b3b19af4bc2ede902ad9d93b8f
4
- data.tar.gz: 28203bd469d71e8c9ab0c294635ce190b11805b022857603d10a0d955d488ad1
3
+ metadata.gz: 220e70064255c62b8a08934d7f515ea38257e4bfa9017843ad6c833b064e321f
4
+ data.tar.gz: 01de55a62af0c4dd1a699bb968d80cf9e518c2a8b3026b01fd7262472dcd8c21
5
5
  SHA512:
6
- metadata.gz: a9388a19f538d3c60b07bae725cf20bf8a42e6cd25a1aa3904d9b716717e938e0bec81b81933ceddabeb525e64393480fa5b88a85cabd1fc927574d8cad5a821
7
- data.tar.gz: 4307463861177b312d58839e9dd011ddf832093a615b1014d929f1ccc265cc7feee4dc499de76d16838cac4658e2a56cd4ada513e69eaf488de9bc9e7e24e9fb
6
+ metadata.gz: 8e0325c8a2aa487600af752edd5a222a2ae06821404346f94a681717322957b3e8dc25ab7c2de5566638c9b175993339a0b5728c172b4302f40bef69e723929c
7
+ data.tar.gz: 8ccc09a94034687aaf8192724dcd6675977c4e4e3b6c9777fb10b74392dcd8dd25b9f3780ab2b664ec7c84c98c6396bcbb281d651871f2f664c14997411b63bd
@@ -21,7 +21,7 @@ module Pechkin
21
21
  result = RequestHandler.new(handler, req, logger).handle
22
22
  response(200, result)
23
23
  rescue AppError => e
24
- proces_app_error(req, e)
24
+ process_app_error(req, e)
25
25
  rescue StandardError => e
26
26
  process_unhandled_error(req, e)
27
27
  end
@@ -32,10 +32,15 @@ module Pechkin
32
32
  [code.to_s, DEFAULT_HEADERS.dup, [body.to_json]]
33
33
  end
34
34
 
35
- def proces_app_error(req, err)
35
+ def process_app_error(req, err)
36
36
  data = { status: 'error', message: err.message }
37
- req.body.rewind
38
- body = req.body.read
37
+ if req.body
38
+ req.body.rewind
39
+ body = req.body.read
40
+ else
41
+ body = ''
42
+ end
43
+
39
44
  logger.error "Can't process message: #{err.message}. Body: '#{body}'"
40
45
  response(err.code, data)
41
46
  end
@@ -24,7 +24,7 @@ module Pechkin
24
24
 
25
25
  data = parse_data(req.body.read)
26
26
  # handler.handle() requires keyword arguments
27
- handler.handle(channel_id, message_id, **data).each do |i|
27
+ handler.handle(channel_id, message_id, logger, **data).each do |i|
28
28
  logger.info "Sent #{channel_id}/#{message_id}: #{i.to_json}"
29
29
  end
30
30
  end
@@ -1,3 +1,5 @@
1
+ require_relative 'list'
2
+
1
3
  module Pechkin
2
4
  module Command
3
5
  # Check configuration consistency and exit.
@@ -7,7 +9,34 @@ module Pechkin
7
9
  end
8
10
 
9
11
  def execute
10
- configuration # load configuration from disk and do nothing more
12
+ cfg = configuration # load configuration from disk
13
+
14
+ # If list option is also provided, print the channels
15
+ return unless options.list?
16
+
17
+ puts "Working dir: #{cfg.working_dir}"
18
+ print_bots(cfg.bots)
19
+ print_channels(cfg.channels)
20
+ end
21
+
22
+ private
23
+
24
+ def print_bots(bots)
25
+ puts "\nBots:"
26
+ puts format(BOT_ENTRY_FORMAT, 'NAME', 'CONNECTOR', 'TOKEN')
27
+ bots.each do |name, bot|
28
+ puts format(BOT_ENTRY_FORMAT, name, bot.connector, '*hidden*')
29
+ end
30
+ end
31
+
32
+ def print_channels(channels)
33
+ puts "\nChannels:"
34
+ puts format(CHAT_ENTRY_FORMAT, 'CHANNEL', 'MESSAGE', 'BOT')
35
+ channels.each do |channel_name, channel|
36
+ channel.messages.each_key do |message_name|
37
+ puts format(CHAT_ENTRY_FORMAT, channel_name, message_name, channel.connector.name)
38
+ end
39
+ end
11
40
  end
12
41
  end
13
42
  end
@@ -1,7 +1,7 @@
1
1
  module Pechkin
2
2
  module Command
3
3
  BOT_ENTRY_FORMAT = ' %-25s %-10s %-60s '.freeze
4
- CHAT_ENTRY_FORMAT = ' %-40s %-40s %-30s '.freeze
4
+ CHAT_ENTRY_FORMAT = ' %-60s %-40s %-30s '.freeze
5
5
 
6
6
  # List channels configuration
7
7
  class List < Base
@@ -31,7 +31,7 @@ module Pechkin
31
31
  puts "\nChannels:"
32
32
  puts format(CHAT_ENTRY_FORMAT, 'CHANNEL', 'MESSAGE', 'BOT')
33
33
  channels.each do |channel_name, channel|
34
- channel.messages.each do |message_name, _message|
34
+ channel.messages.each_key do |message_name|
35
35
  puts format(CHAT_ENTRY_FORMAT,
36
36
  channel_name, message_name, channel.connector.name)
37
37
  end
@@ -2,7 +2,7 @@ module Pechkin
2
2
  # Common code for all configuration loaders. To use this code just include
3
3
  # module in user class.
4
4
  module ConfigurationLoader
5
- def check_field(object, field, file)
5
+ def fetch_field(object, field, file)
6
6
  contains = object.key?(field)
7
7
 
8
8
  raise ConfigurationError, "#{file}: '#{field}' is missing" unless contains
@@ -10,6 +10,18 @@ module Pechkin
10
10
  object[field]
11
11
  end
12
12
 
13
+ # Fetch token from environment variable defined in configuration file.
14
+ def fetch_value_from_env(object, token_field, file)
15
+ raise ConfigurationError, "#{file}: '#{token_field}' is missing in configuration" unless object.key?(token_field)
16
+
17
+ env_var = object[token_field]
18
+ token = ENV.fetch(env_var, nil)
19
+
20
+ raise ConfigurationError, "#{file}: environment var '#{token_field}' is missing" if token.to_s.strip.empty?
21
+
22
+ token
23
+ end
24
+
13
25
  def create_connector(bot)
14
26
  case bot.connector
15
27
  when 'tg', 'telegram'
@@ -28,8 +28,8 @@ module Pechkin
28
28
  def load_bot_configuration(bot_file)
29
29
  bot_configuration = yaml_load(bot_file)
30
30
 
31
- token = check_field(bot_configuration, 'token', bot_file)
32
- connector = check_field(bot_configuration, 'connector', bot_file)
31
+ token = fetch_value_from_env(bot_configuration, 'token_env', bot_file)
32
+ connector = fetch_field(bot_configuration, 'connector', bot_file)
33
33
 
34
34
  Bot.new(token: token, connector: connector)
35
35
  end
@@ -40,8 +40,8 @@ module Pechkin
40
40
 
41
41
  channel_config = yaml_load(channel_file)
42
42
 
43
- bot = check_field(channel_config, 'bot', channel_file)
44
- chat_ids = check_field(channel_config, 'chat_ids', channel_file)
43
+ bot = fetch_field(channel_config, 'bot', channel_file)
44
+ chat_ids = fetch_field(channel_config, 'chat_ids', channel_file)
45
45
  chat_ids = [chat_ids] unless chat_ids.is_a?(Array)
46
46
  messages = load_messages_configuration(channel_dir)
47
47
 
@@ -6,7 +6,7 @@ module Pechkin
6
6
  'Content-Type' => 'application/json; charset=UTF-8'
7
7
  }.freeze
8
8
 
9
- def send_message(chat, message, message_desc); end
9
+ def send_message(chat, email, message, message_desc, logger); end
10
10
 
11
11
  def preview(chats, message, _message_desc)
12
12
  "Connector: #{self.class.name}; Chats: #{chats.join(', ')}\n" \
@@ -1,5 +1,7 @@
1
1
  module Pechkin
2
2
  module Connector
3
+ class SlackApiRequestError < StandardError; end
4
+
3
5
  class Slack < Connector::Base # :nodoc:
4
6
  attr_reader :name
5
7
 
@@ -10,9 +12,35 @@ module Pechkin
10
12
  @name = name
11
13
  end
12
14
 
13
- def send_message(channel, message, message_desc)
15
+ def resolve_user_id(email, logger)
16
+ url = "https://slack.com/api/users.lookupByEmail?email=#{email}"
17
+
18
+ uri = URI.parse(url)
19
+ http = Net::HTTP.new(uri.host, uri.port)
20
+ http.use_ssl = url.start_with?('https://')
21
+
22
+ request = Net::HTTP::Get.new(uri.request_uri, @headers)
23
+
24
+ response = http.request(request)
25
+
26
+ if response.is_a?(Net::HTTPSuccess)
27
+ logger.warn "#{email}HTTPSuccess"
28
+ resp = JSON.parse(response.body)
29
+ else
30
+ logger.warn "#{email}Sending failed#{response.message}"
31
+ raise SlackApiRequestError, response.message
32
+ end
33
+
34
+ logger.warn "#{email} RESPONSE:#{resp['user']['id']}"
35
+
36
+ resp['user']['id']
37
+ end
38
+
39
+ def send_message(channel, email, message, message_desc, logger)
14
40
  text = CGI.unescape_html(message)
15
41
 
42
+ channel = resolve_user_id(email, logger) if channel == 'email'
43
+
16
44
  attachments = message_desc['slack_attachments'] || {}
17
45
 
18
46
  if text.strip.empty? && attachments.empty?
@@ -25,6 +53,8 @@ module Pechkin
25
53
  url = 'https://slack.com/api/chat.postMessage'
26
54
  response = post_data(url, params, headers: @headers)
27
55
 
56
+ logger.warn "#{email} RESPONSE: #{response.body}"
57
+
28
58
  { channel: channel, code: response.code.to_i, response: response.body }
29
59
  end
30
60
  end
@@ -10,7 +10,7 @@ module Pechkin
10
10
  @name = name
11
11
  end
12
12
 
13
- def send_message(chat_id, message, message_desc)
13
+ def send_message(chat_id, _email, message, message_desc, _logger)
14
14
  options = { parse_mode: message_desc['telegram_parse_mode'] || 'HTML' }
15
15
  params = options.update(chat_id: chat_id, text: message)
16
16
 
@@ -26,14 +26,15 @@ module Pechkin
26
26
  # @param data [Object] data object to render via template. This is usualy
27
27
  # deserialized json.
28
28
  # @see Configuration
29
- def handle(channel_id, msg_id, data)
29
+ def handle(channel_id, msg_id, logger, data)
30
30
  channel_config, message_config, text =
31
31
  prepare_message(channel_id, msg_id, data)
32
32
  chats = channel_config.chat_ids
33
33
  connector = channel_config.connector
34
+ email = data['email']
34
35
 
35
36
  if message_allowed?(message_config, data)
36
- chats.map { |chat| connector.send_message(chat, text, message_config) }
37
+ chats.map { |chat| connector.send_message(chat, email, text, message_config, logger) }
37
38
  else
38
39
  logger.warn "#{channel_id}/#{msg_id}: " \
39
40
  "Skip sending message. Because it's not allowed"
@@ -1,7 +1,7 @@
1
1
  module Pechkin
2
2
  # Keeps actual version
3
3
  module Version
4
- VERSION = [2, 0, 2].freeze
4
+ VERSION = [2, 1, 0].freeze
5
5
  class << self
6
6
  def version_string
7
7
  VERSION.join('.')
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: 2.0.2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Arkhanhelsky
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-02 00:00:00.000000000 Z
11
+ date: 2025-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: htauth
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
82
  version: 3.0.9.1
83
- description:
83
+ description:
84
84
  email: ilya.arkhanhelsky at gmail.com
85
85
  executables:
86
86
  - pechkin
@@ -126,7 +126,7 @@ homepage: https://github.com/iarkhanhelsky/pechkin
126
126
  licenses:
127
127
  - MIT
128
128
  metadata: {}
129
- post_install_message:
129
+ post_install_message:
130
130
  rdoc_options: []
131
131
  require_paths:
132
132
  - lib
@@ -142,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
142
  version: '0'
143
143
  requirements: []
144
144
  rubygems_version: 3.4.10
145
- signing_key:
145
+ signing_key:
146
146
  specification_version: 4
147
147
  summary: Web service to proxy webhooks to Telegram Bot API
148
148
  test_files: []