pechkin 1.3.1 → 1.3.2
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.rb +0 -2
- data/lib/pechkin/app/app.rb +2 -1
- data/lib/pechkin/configuration/configuration_loader.rb +2 -2
- data/lib/pechkin/connector.rb +3 -23
- data/lib/pechkin/connector/base.rb +29 -0
- data/lib/pechkin/connector/slack.rb +30 -0
- data/lib/pechkin/connector/telegram.rb +26 -0
- data/lib/pechkin/handler.rb +2 -2
- data/lib/pechkin/message_matcher.rb +30 -16
- data/lib/pechkin/version.rb +1 -1
- metadata +5 -4
- data/lib/pechkin/connector_slack.rb +0 -28
- data/lib/pechkin/connector_telegram.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a2ecda5dd85e6cb2dd48ccb18eb1966559fa8d6539239b1e6def0377324c765
|
4
|
+
data.tar.gz: 9be0c8e01ceec5f0e19f2c44a9a7d10a2c09c32ddcec26d5fba5d43f808eaec2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bdf5bd548de267c3cfc9a7a45be9a451df75bf288e9403dfe1ed9e3f748318fd1acd85f5070f310890b5947b3f478ea4cdab817b8cba9902cf5f23ad35cd431
|
7
|
+
data.tar.gz: c18c214ef31484ccc3dd79af5c55270f1fdcea84ee198e7ef5c3adc39b76496914ba703bc2d5e7f45beec62fb25dd71942cdf323a82b02a35d641a6aec420d6c
|
data/lib/pechkin.rb
CHANGED
@@ -14,8 +14,6 @@ require_relative 'pechkin/handler'
|
|
14
14
|
require_relative 'pechkin/message_matcher'
|
15
15
|
require_relative 'pechkin/message_template'
|
16
16
|
require_relative 'pechkin/connector'
|
17
|
-
require_relative 'pechkin/connector_slack'
|
18
|
-
require_relative 'pechkin/connector_telegram'
|
19
17
|
require_relative 'pechkin/channel'
|
20
18
|
require_relative 'pechkin/configuration'
|
21
19
|
require_relative 'pechkin/substitute'
|
data/lib/pechkin/app/app.rb
CHANGED
@@ -34,9 +34,10 @@ module Pechkin
|
|
34
34
|
response(err.code, data)
|
35
35
|
end
|
36
36
|
|
37
|
-
def process_unhandled_error(
|
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
|
@@ -13,9 +13,9 @@ module Pechkin
|
|
13
13
|
def create_connector(bot)
|
14
14
|
case bot.connector
|
15
15
|
when 'tg', 'telegram'
|
16
|
-
|
16
|
+
Connector::Telegram.new(bot.token, bot.name)
|
17
17
|
when 'slack'
|
18
|
-
|
18
|
+
Connector::Slack.new(bot.token, bot.name)
|
19
19
|
else
|
20
20
|
raise 'Unknown connector ' + bot.connector + ' for ' + bot.name
|
21
21
|
end
|
data/lib/pechkin/connector.rb
CHANGED
@@ -4,26 +4,6 @@ require 'uri'
|
|
4
4
|
require 'json'
|
5
5
|
require 'cgi'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
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'
|
@@ -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,30 @@
|
|
1
|
+
module Pechkin
|
2
|
+
module Connector
|
3
|
+
class Slack < Connector::Base # :nodoc:
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(bot_token, name)
|
7
|
+
@headers = { 'Authorization' => "Bearer #{bot_token}" }
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def send_message(channel, message, message_desc)
|
12
|
+
text = CGI.unescape_html(message)
|
13
|
+
|
14
|
+
attachments = message_desc['slack_attachments'] || {}
|
15
|
+
|
16
|
+
if text.strip.empty? && attachments.empty?
|
17
|
+
return { channel: channel, code: 400,
|
18
|
+
response: 'Internal error: message is empty' }
|
19
|
+
end
|
20
|
+
|
21
|
+
params = { channel: channel, text: text, attachments: attachments }
|
22
|
+
|
23
|
+
url = 'https://slack.com/api/chat.postMessage'
|
24
|
+
response = post_data(url, params, headers: @headers)
|
25
|
+
|
26
|
+
{ channel: channel, code: response.code.to_i, response: response.body }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Pechkin
|
2
|
+
module Connector
|
3
|
+
class Telegram < Connector::Base # :nodoc:
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(bot_token, name)
|
7
|
+
@bot_token = bot_token
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def send_message(chat_id, message, message_desc)
|
12
|
+
options = { parse_mode: message_desc['telegram_parse_mode'] || 'HTML' }
|
13
|
+
params = options.update(chat_id: chat_id, text: message)
|
14
|
+
|
15
|
+
response = post_data(method_url('sendMessage'), params)
|
16
|
+
{ chat_id: chat_id, code: response.code.to_i, response: response.body }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def method_url(method)
|
22
|
+
"https://api.telegram.org/bot#{@bot_token}/#{method}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/pechkin/handler.rb
CHANGED
@@ -11,7 +11,7 @@ module Pechkin
|
|
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.
|
38
|
+
logger.warn "#{channel_id}/#{msg_id}: " \
|
39
39
|
"Skip sending message. Because it's not allowed"
|
40
40
|
[]
|
41
41
|
end
|
@@ -10,6 +10,13 @@ module Pechkin
|
|
10
10
|
class MessageMatcher
|
11
11
|
KEY_ALLOW = 'allow'.freeze
|
12
12
|
KEY_FORBID = 'forbid'.freeze
|
13
|
+
|
14
|
+
attr_reader :logger
|
15
|
+
|
16
|
+
def initialize(logger)
|
17
|
+
@logger = logger
|
18
|
+
end
|
19
|
+
|
13
20
|
# Checks data object against allow / forbid rule sets in message
|
14
21
|
# configuration. If data object matches rules it means we can process this
|
15
22
|
# data and send message.
|
@@ -23,10 +30,10 @@ module Pechkin
|
|
23
30
|
|
24
31
|
if message_config.key?(KEY_ALLOW)
|
25
32
|
rules = message_config[KEY_ALLOW]
|
26
|
-
rules.any? { |r| check_rule(r, data) }
|
33
|
+
rules.any? { |r| check_rule(r, r, data) }
|
27
34
|
elsif message_config.key?(KEY_FORBID)
|
28
35
|
rules = message_config[KEY_FORBID]
|
29
|
-
rules.all? { |r| !check_rule(r, data) }
|
36
|
+
rules.all? { |r| !check_rule(r, r, data) }
|
30
37
|
else
|
31
38
|
true
|
32
39
|
end
|
@@ -43,35 +50,42 @@ module Pechkin
|
|
43
50
|
# Check rule object against data. Rules are checked recursievly, i.e. we
|
44
51
|
# can take one field in rule and check it separately as new rule. If all
|
45
52
|
# fields are passed check then whole rule passed.
|
46
|
-
def check_rule(
|
47
|
-
if
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
def check_rule(top_rule, sub_rule, data)
|
54
|
+
result = if sub_rule.is_a?(Hash)
|
55
|
+
check_hash_rule(top_rule, sub_rule, data)
|
56
|
+
elsif sub_rule.is_a?(Array)
|
57
|
+
check_array_rule(top_rule, sub_rule, data)
|
58
|
+
elsif sub_rule.is_a?(String)
|
59
|
+
check_string_rule(top_rule, sub_rule, data)
|
60
|
+
else
|
61
|
+
sub_rule.eql?(data)
|
62
|
+
end
|
63
|
+
|
64
|
+
unless result
|
65
|
+
logger.info "Expected #{sub_rule.to_json} got #{data.to_json} when" \
|
66
|
+
" checking #{top_rule.to_json}"
|
55
67
|
end
|
68
|
+
|
69
|
+
result
|
56
70
|
end
|
57
71
|
|
58
|
-
def check_hash_rule(hash, data)
|
72
|
+
def check_hash_rule(top_rule, hash, data)
|
59
73
|
return false unless data.is_a?(Hash)
|
60
74
|
|
61
75
|
hash.all? do |key, value|
|
62
|
-
data.key?(key) && check_rule(value, data[key])
|
76
|
+
data.key?(key) && check_rule(top_rule, value, data[key])
|
63
77
|
end
|
64
78
|
end
|
65
79
|
|
66
|
-
def check_array_rule(array, data)
|
80
|
+
def check_array_rule(top_rule, array, data)
|
67
81
|
return false unless data.is_a?(Array)
|
68
82
|
|
69
83
|
# Deep array check needs to be done against all elements so we zip arrays
|
70
84
|
# to pair each rule with data element
|
71
|
-
array.zip(data).all? { |r, d| check_rule(r, d) }
|
85
|
+
array.zip(data).all? { |r, d| check_rule(top_rule, r, d) }
|
72
86
|
end
|
73
87
|
|
74
|
-
def check_string_rule(str, data)
|
88
|
+
def check_string_rule(_top_rule, str, data)
|
75
89
|
str.eql? data
|
76
90
|
end
|
77
91
|
end
|
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: 1.3.
|
4
|
+
version: 1.3.2
|
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-
|
11
|
+
date: 2020-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: htauth
|
@@ -97,8 +97,9 @@ files:
|
|
97
97
|
- lib/pechkin/configuration/configuration_loader_views.rb
|
98
98
|
- lib/pechkin/configuration/model.rb
|
99
99
|
- lib/pechkin/connector.rb
|
100
|
-
- lib/pechkin/
|
101
|
-
- lib/pechkin/
|
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
|
104
105
|
- lib/pechkin/message_matcher.rb
|
@@ -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
|