pechkin 1.3.1 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|