rodbot 0.3.3 → 0.3.4
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
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +7 -1
- data/README.md +9 -1
- data/checksums/rodbot-0.3.4.gem.sha512 +1 -0
- data/lib/rodbot/message.rb +89 -0
- data/lib/rodbot/plugins/github_webhook/app.rb +2 -2
- data/lib/rodbot/plugins/gitlab_webhook/app.rb +2 -2
- data/lib/rodbot/plugins/matrix/README.matrix.md +12 -0
- data/lib/rodbot/plugins/matrix/relay.rb +4 -4
- data/lib/rodbot/plugins/slack/README.slack.md +14 -0
- data/lib/rodbot/plugins/slack/relay.rb +3 -4
- data/lib/rodbot/relay.rb +4 -3
- data/lib/rodbot/serializer.rb +65 -0
- data/lib/rodbot/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +5 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eac36ded2dc61a62f3a5e9c236987ff66f0aadf65f216b96932375af2cec3241
|
4
|
+
data.tar.gz: '099b12406aa01b8ec165efb25a3faef9fd1efeed3a8c9a7c0c0bb05febadf2e8'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06d6953cd85005effd3a54da59564e52a27d27cb42f61ccc2aeb0dfa32627a38d96f9e07fd9ba7d533fdf4210180d228582e5b39672a4124eba9831abb443016
|
7
|
+
data.tar.gz: cec0a4d433b447c34fa91ff7a7fd9df416648851ef5b8156fb3d01579ac2d1abb26e7738ee000b1bdb322575dc99c5f8ac635843e1ecfc07d439b3b3403fddb9
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
Nothing so far
|
4
4
|
|
5
|
+
## 0.3.4
|
6
|
+
|
7
|
+
#### Additions
|
8
|
+
* Support to post to secondary rooms with `Rodbot.say`
|
9
|
+
* `Rodbot::Message` container class for messages with meta data
|
10
|
+
|
5
11
|
## 0.3.3
|
6
12
|
|
7
13
|
#### Additions
|
8
14
|
* Support placeholders when using `Rodbot.say` and add `[[EVERYBODY]]`
|
9
|
-
placeholder to mention all hands in a room or channel
|
15
|
+
placeholder to mention all hands in a room or channel
|
10
16
|
|
11
17
|
## 0.3.2
|
12
18
|
|
data/README.md
CHANGED
@@ -178,7 +178,7 @@ The **relay service** act as glue between the **app service** and external commu
|
|
178
178
|
|
179
179
|
Each relay service does three things:
|
180
180
|
|
181
|
-
* **Proactive:** It creates and listens to a local TCP socket
|
181
|
+
* **Proactive:** It creates and listens to a local TCP socket which accepts and forwards messages. See below for more on this.
|
182
182
|
* **Reactive:** It reads messages, detects commands usually beginning with a `!`, forwards them to the **app service** and writes the HTTP response back as a message to the communication network.
|
183
183
|
* **Test:** It detects the `!ping` command and replies with "pong" *without* hitting the **app service**.
|
184
184
|
|
@@ -190,6 +190,14 @@ rodbot simulator
|
|
190
190
|
|
191
191
|
Enter the command `!pay EUR 123` and you see the request `GET /pay?argument=EUR+123` hitting the **app service**.
|
192
192
|
|
193
|
+
#### TCP Socket
|
194
|
+
|
195
|
+
The TCP socket is primarily used by other Rodbot services to forward messages to the corresponding external communication network. However, you can use these sockets for non-Rodbot processes as well e.g. to issue notifications when events happen on the host running Rodbot.
|
196
|
+
|
197
|
+
Simply connect to a socket and submit the message as plain text or Markdown in UTF-8. Multiple lines are allowed, to finish and post the message, append the EOT character (`\x04` alias Ctrl-D).
|
198
|
+
|
199
|
+
Such simple messages are always posted to the primary room (aka: channel, group etc) of the communication network. For more complex scenarios, please take a look at [message objects](https://www.rubydoc.info/gems/rodbot/Rodbot/Message) which may contain meta information as well.
|
200
|
+
|
193
201
|
### Schedule Service
|
194
202
|
|
195
203
|
The **schedule service** is a [Clockwork process](https://github.com/Rykian/clockwork) which triggers Ruby code asynchronously as configured in `config/schedule.rb`.
|
@@ -0,0 +1 @@
|
|
1
|
+
ab32834eec05a817fafa249e06330e30cb39e08b7234b46b9ae9787c03f6900ab7e6202a7c364f9ae93d5028669561ef31bcc1445f9e8990d6f2f992e60f4032
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Rodbot
|
4
|
+
|
5
|
+
# Generic serializable chat message container
|
6
|
+
#
|
7
|
+
# The primary purpose of message objects is their ability to contain both
|
8
|
+
# the actual message text as well as meta data such as the room in which the
|
9
|
+
# message was or will be posted.
|
10
|
+
#
|
11
|
+
# Furthermore, they can be serialized (+dump+) and then recreated (+new+)
|
12
|
+
# from that. You can also create a serialized message string outside of RodBot
|
13
|
+
# performing the following steps:
|
14
|
+
#
|
15
|
+
# 1. Create a JSON hash which contains the keys +class+ (with static value
|
16
|
+
# +Rodbot::Message+), +text+ and optionally +room+.
|
17
|
+
# 2. Encode it as Base64 without newlines.
|
18
|
+
# 3. Prefix the result with {Rodbot::Serializer::PRELUDE}.
|
19
|
+
#
|
20
|
+
# Example for Shell:
|
21
|
+
# string='{"class":"Rodbot::Message",text":"hello, world","room":"general"}'
|
22
|
+
# string=$(echo $string | base64)
|
23
|
+
# string="data:application/json;base64,$string"
|
24
|
+
# echo $string
|
25
|
+
class Message
|
26
|
+
|
27
|
+
# Raw message text
|
28
|
+
#
|
29
|
+
# @return [String, nil]
|
30
|
+
attr_reader :text
|
31
|
+
|
32
|
+
# Room (aka: channel, group etc depending on the chat service) in which
|
33
|
+
# the message was or will be posted
|
34
|
+
#
|
35
|
+
# @return [String, nil]
|
36
|
+
attr_accessor :room
|
37
|
+
|
38
|
+
# Initialize message from raw message text
|
39
|
+
#
|
40
|
+
# @param text [String] raw message text
|
41
|
+
# @param room [String, nil] room in which the message was or will be posted
|
42
|
+
def initialize(text, room: nil)
|
43
|
+
@text, @room = text, room
|
44
|
+
end
|
45
|
+
|
46
|
+
# Initialize message from either message object previously serialized with
|
47
|
+
# +dump+ or from raw message text
|
48
|
+
#
|
49
|
+
# @param string [String] string returned by +dump+ or raw message text
|
50
|
+
# @param room [String, nil] room in which the message was or will be posted
|
51
|
+
# @raise [ArgumentError] if the string is not valid Base64, JSON or does not
|
52
|
+
# contain the key +"class":"Rodbot::Message"+
|
53
|
+
def self.new(string, room: nil)
|
54
|
+
allocate.instance_eval do
|
55
|
+
serializer = Rodbot::Serializer.new(string)
|
56
|
+
if serializer.deserializable?
|
57
|
+
hash = serializer.hash
|
58
|
+
fail(ArgumentError, "not a dumped message") unless hash['class'] == self.class.to_s
|
59
|
+
initialize(hash['text'], room: room || hash['room'])
|
60
|
+
else
|
61
|
+
initialize(string.force_encoding('UTF-8'), room: room)
|
62
|
+
end
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Serialize the message
|
68
|
+
#
|
69
|
+
# @return [String] serialized and encoded +self+
|
70
|
+
def dump
|
71
|
+
Rodbot::Serializer.new(to_h).string
|
72
|
+
end
|
73
|
+
|
74
|
+
# Convert message to Hash
|
75
|
+
#
|
76
|
+
# @return [Hash]
|
77
|
+
def to_h
|
78
|
+
{ class: self.class.to_s, text: text, room: room }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Whether two messages are equal
|
82
|
+
#
|
83
|
+
# @return [Boolean]
|
84
|
+
def ==(other)
|
85
|
+
to_h == other.to_h
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -10,7 +10,7 @@ module Rodbot
|
|
10
10
|
r.post '' do
|
11
11
|
r.halt 200 if request.env['HTTP_X_GITHUB_EVENT'] == 'ping'
|
12
12
|
r.halt 400 unless request.env['HTTP_X_GITHUB_EVENT'] == 'workflow_run'
|
13
|
-
r.halt 401 unless authorized?
|
13
|
+
r.halt 401 unless authorized?
|
14
14
|
json = JSON.parse(request.body.read)
|
15
15
|
project = json.dig('repository', 'full_name')
|
16
16
|
status = json.dig('workflow_run', 'status')
|
@@ -22,7 +22,7 @@ module Rodbot
|
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
|
-
def authorized?
|
25
|
+
def authorized?
|
26
26
|
Rodbot.config(:plugin, :github_webhook, :secret_tokens).to_s.split(':').any? do |secret|
|
27
27
|
signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, request.body.read)
|
28
28
|
request.body.rewind
|
@@ -8,7 +8,7 @@ module Rodbot
|
|
8
8
|
class Routes < ::App
|
9
9
|
route do |r|
|
10
10
|
r.post '' do
|
11
|
-
r.halt 401 unless authorized?
|
11
|
+
r.halt 401 unless authorized?
|
12
12
|
json = JSON.parse(request.body.read)
|
13
13
|
r.halt 400 unless json['object_kind'] == 'pipeline'
|
14
14
|
project = json.dig('project', 'path_with_namespace')
|
@@ -20,7 +20,7 @@ module Rodbot
|
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
-
def authorized?
|
23
|
+
def authorized?
|
24
24
|
Rodbot.config(:plugin, :gitlab_webhook, :secret_tokens).to_s.split(':').include?(request.env['HTTP_X_GITLAB_TOKEN'])
|
25
25
|
end
|
26
26
|
|
@@ -40,3 +40,15 @@ You might want to use the credentials facilities of Rodbot to encrypt the token.
|
|
40
40
|
Once Rodbot is restarted, the Matrix relay will automatically accept the invitation and start listening. To check whether the relay works fine, just say +!ping+ in the room, you should receive a "pong" in reply.
|
41
41
|
|
42
42
|
Any room message beginning with "!" is considered a bot command.
|
43
|
+
|
44
|
+
To post messages to the primary channel configured with the plugin:
|
45
|
+
|
46
|
+
```
|
47
|
+
Rodbot.say('Hello, world!')
|
48
|
+
```
|
49
|
+
|
50
|
+
It's possible to post to other, secondary channels, provided you have previously added the bot app to those secondary channels as [described above](#preparation):
|
51
|
+
|
52
|
+
```
|
53
|
+
Rodbot.say('Hello, world!', room: '#general:matrix.org')
|
54
|
+
```
|
@@ -38,7 +38,7 @@ module Rodbot
|
|
38
38
|
end
|
39
39
|
|
40
40
|
memoize def client
|
41
|
-
MatrixSdk::Client.new(homeserver, access_token: access_token, client_cache: :some)
|
41
|
+
MatrixSdk::Client.new(homeserver, access_token: access_token, client_cache: :some).tap(&:reload_rooms!)
|
42
42
|
end
|
43
43
|
|
44
44
|
memoize def room
|
@@ -57,10 +57,10 @@ module Rodbot
|
|
57
57
|
server = TCPServer.new(*bind)
|
58
58
|
loop do
|
59
59
|
Thread.start(server.accept) do |remote|
|
60
|
-
|
60
|
+
message = Rodbot::Message.new(remote.gets("\x04").chop)
|
61
61
|
remote.close
|
62
|
-
|
63
|
-
|
62
|
+
target_room = message.room ? client.find_room(message.room) : room
|
63
|
+
target_room.send_html message.text.psub(placeholders).md_to_html
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -46,3 +46,17 @@ You might want to use the credentials facilities of Rodbot to encrypt the token.
|
|
46
46
|
Once Rodbot is restarted, the Slack relay starts listening. To check whether the relay works fine, just say +!ping+ in the channel, you should receive a "pong" in reply.
|
47
47
|
|
48
48
|
Any room message beginning with "!" is considered a bot command.
|
49
|
+
|
50
|
+
To post messages to the primary channel configured with the plugin:
|
51
|
+
|
52
|
+
```
|
53
|
+
Rodbot.say('Hello, world!')
|
54
|
+
```
|
55
|
+
|
56
|
+
It's possible to post to other, secondary channels, provided you have previously added the bot app to those secondary channels as [described above](#preparation):
|
57
|
+
|
58
|
+
```
|
59
|
+
Rodbot.say('Hello, world!', room: '#general')
|
60
|
+
```
|
61
|
+
|
62
|
+
Please note that Rodbot uses the term "room" for what is called a channel on Slack.
|
@@ -39,12 +39,11 @@ module Rodbot
|
|
39
39
|
server = TCPServer.new(*bind)
|
40
40
|
loop do
|
41
41
|
Thread.start(server.accept) do |remote|
|
42
|
-
|
42
|
+
message = Rodbot::Message.new(remote.gets("\x04").chop)
|
43
43
|
remote.close
|
44
|
-
md.force_encoding('UTF-8')
|
45
44
|
client.web_client.chat_postMessage(
|
46
|
-
channel: channel_id,
|
47
|
-
text: md_to_slack_text(
|
45
|
+
channel: message.room || channel_id,
|
46
|
+
text: md_to_slack_text(message.text.psub(placeholders)),
|
48
47
|
as_user: true
|
49
48
|
)
|
50
49
|
end
|
data/lib/rodbot/relay.rb
CHANGED
@@ -18,15 +18,16 @@ module Rodbot
|
|
18
18
|
# "say true" configured in their corresponding config blocks. To further
|
19
19
|
# narrow it to exactly one relay service, use the +on+ argument.
|
20
20
|
#
|
21
|
-
# @param
|
21
|
+
# @param text [String] message to post
|
22
22
|
# @param on [Symbol, nil] post via this relay service only
|
23
|
+
# @param room [String, nil] post to this room (aka: channel, group etc)
|
23
24
|
# @return [Boolean] +false+ if at least one relay refused the connection or
|
24
25
|
# +true+ otherwise
|
25
|
-
def say(
|
26
|
+
def say(text, on: nil, room: nil)
|
26
27
|
Rodbot.config(:plugin).select do |extension, config|
|
27
28
|
config[:say] == true && (!on || extension == on)
|
28
29
|
end.keys.inject(true) do |success, extension|
|
29
|
-
write(
|
30
|
+
write(Rodbot::Message.new(text, room: room).dump, extension) && success
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module Rodbot
|
7
|
+
|
8
|
+
# Simple yet safe Hash to String serializer
|
9
|
+
#
|
10
|
+
# Keep in mind that hash keys will always be deserialized as strings!
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# hash = { 'foo' => 'bar' }
|
14
|
+
# string = Serializer.new(hash).string
|
15
|
+
# # => "data:application/json;base64,eyJmb28iOiJiYXIifQ=="
|
16
|
+
# hash = Serializer.new(string).hash
|
17
|
+
# # => { 'foo' => 'bar' }
|
18
|
+
class Serializer
|
19
|
+
|
20
|
+
# Prelude string for serialized hash
|
21
|
+
PRELUDE = 'data:application/json;base64,'
|
22
|
+
|
23
|
+
# @params object [Hash, String] either a Hash (to serialize) or a String
|
24
|
+
# (to deserialize)
|
25
|
+
def initialize(object)
|
26
|
+
case object
|
27
|
+
when Hash then @hash = object
|
28
|
+
when String then @string = object
|
29
|
+
else fail ArgumentError, "must be either Hash or String"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [String] Hash serialized to String
|
34
|
+
def string
|
35
|
+
@string ||= begin
|
36
|
+
fail "object is not serializable" unless serializable?
|
37
|
+
@hash.to_json.then { PRELUDE + Base64.strict_encode64(_1) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Hash] String deserialized to Hash
|
42
|
+
# @raise [RuntimeError] when deserialization fails
|
43
|
+
def hash
|
44
|
+
@hash ||= begin
|
45
|
+
fail "object is not deserializable" unless deserializable?
|
46
|
+
JSON.load(Base64.strict_decode64(@string.delete_prefix(PRELUDE)))
|
47
|
+
end
|
48
|
+
rescue ArgumentError
|
49
|
+
raise "invalid Base64"
|
50
|
+
rescue JSON::ParserError
|
51
|
+
raise "invalid JSON"
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Boolean] whether the object passed with +new+ is serializable
|
55
|
+
def serializable?
|
56
|
+
!!@hash
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Boolean] whether the object passed with +new+ is deserializable
|
60
|
+
def deserializable?
|
61
|
+
@string && @string.match?(/\A#{PRELUDE}/)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
data/lib/rodbot/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sven Schwyn
|
@@ -29,7 +29,7 @@ cert_chain:
|
|
29
29
|
kAyiRqgxF4dJviwtqI7mZIomWL63+kXLgjOjMe1SHxfIPo/0ji6+r1p4KYa7o41v
|
30
30
|
fwIwU1MKlFBdsjkd
|
31
31
|
-----END CERTIFICATE-----
|
32
|
-
date: 2023-10-
|
32
|
+
date: 2023-10-24 00:00:00.000000000 Z
|
33
33
|
dependencies:
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: zeitwerk
|
@@ -453,6 +453,7 @@ files:
|
|
453
453
|
- checksums/rodbot-0.3.1.gem.sha512
|
454
454
|
- checksums/rodbot-0.3.2.gem.sha512
|
455
455
|
- checksums/rodbot-0.3.3.gem.sha512
|
456
|
+
- checksums/rodbot-0.3.4.gem.sha512
|
456
457
|
- doc/rodbot.afphoto
|
457
458
|
- doc/rodbot.avif
|
458
459
|
- exe/rodbot
|
@@ -483,6 +484,7 @@ files:
|
|
483
484
|
- lib/rodbot/generator.rb
|
484
485
|
- lib/rodbot/log.rb
|
485
486
|
- lib/rodbot/memoize.rb
|
487
|
+
- lib/rodbot/message.rb
|
486
488
|
- lib/rodbot/plugins.rb
|
487
489
|
- lib/rodbot/plugins/github_webhook/README.github_webhook.md
|
488
490
|
- lib/rodbot/plugins/github_webhook/app.rb
|
@@ -501,6 +503,7 @@ files:
|
|
501
503
|
- lib/rodbot/rack.rb
|
502
504
|
- lib/rodbot/refinements.rb
|
503
505
|
- lib/rodbot/relay.rb
|
506
|
+
- lib/rodbot/serializer.rb
|
504
507
|
- lib/rodbot/services.rb
|
505
508
|
- lib/rodbot/services/app.rb
|
506
509
|
- lib/rodbot/services/relay.rb
|
metadata.gz.sig
CHANGED
Binary file
|