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 +4 -4
- data/lib/pechkin/app/app.rb +9 -4
- data/lib/pechkin/app/request_handler.rb +1 -1
- data/lib/pechkin/command/check.rb +30 -1
- data/lib/pechkin/command/list.rb +2 -2
- data/lib/pechkin/configuration/configuration_loader.rb +13 -1
- data/lib/pechkin/configuration/configuration_loader_bots.rb +2 -2
- data/lib/pechkin/configuration/configuration_loader_channels.rb +2 -2
- data/lib/pechkin/connector/base.rb +1 -1
- data/lib/pechkin/connector/slack.rb +31 -1
- data/lib/pechkin/connector/telegram.rb +1 -1
- data/lib/pechkin/handler.rb +3 -2
- data/lib/pechkin/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 220e70064255c62b8a08934d7f515ea38257e4bfa9017843ad6c833b064e321f
|
|
4
|
+
data.tar.gz: 01de55a62af0c4dd1a699bb968d80cf9e518c2a8b3026b01fd7262472dcd8c21
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e0325c8a2aa487600af752edd5a222a2ae06821404346f94a681717322957b3e8dc25ab7c2de5566638c9b175993339a0b5728c172b4302f40bef69e723929c
|
|
7
|
+
data.tar.gz: 8ccc09a94034687aaf8192724dcd6675977c4e4e3b6c9777fb10b74392dcd8dd25b9f3780ab2b664ec7c84c98c6396bcbb281d651871f2f664c14997411b63bd
|
data/lib/pechkin/app/app.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
35
|
+
def process_app_error(req, err)
|
|
36
36
|
data = { status: 'error', message: err.message }
|
|
37
|
-
req.body
|
|
38
|
-
|
|
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
|
|
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
|
data/lib/pechkin/command/list.rb
CHANGED
|
@@ -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 = ' %-
|
|
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.
|
|
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
|
|
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 =
|
|
32
|
-
connector =
|
|
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 =
|
|
44
|
-
chat_ids =
|
|
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
|
|
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
|
|
data/lib/pechkin/handler.rb
CHANGED
|
@@ -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"
|
data/lib/pechkin/version.rb
CHANGED
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
|
|
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:
|
|
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: []
|