feishu_notifier 0.1.1
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +178 -0
- data/bin/feishu-notifier +87 -0
- data/lib/feishu_notifier/card.rb +71 -0
- data/lib/feishu_notifier/client.rb +88 -0
- data/lib/feishu_notifier/config.rb +21 -0
- data/lib/feishu_notifier/env_file.rb +28 -0
- data/lib/feishu_notifier/version.rb +3 -0
- data/lib/feishu_notifier.rb +5 -0
- metadata +78 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 45fef57b6d5c69eed564a4625e58fa3a60c38808ec9de9526a5a0bc6e624ba1a
|
|
4
|
+
data.tar.gz: 2ed23eaceaf3595525b136070210c607e828ce1b6a2bbe6d74ba4fd17704672f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 80367f0000f9db8c102d07f7393b83e4b93a3b2176d1c545801c4c4bc291b1c158545fd16056b3e92eecb4a2fe2b0073e320aa56ed337008aa7c2261086e46bf
|
|
7
|
+
data.tar.gz: 6a0c3b51e0603ebff1c78c0e45f9dad169a12f0f669334ad46e4708860cca45ce04abbf8b1d70c651bf7932d2327432ceb12cee81873a59723831ab0673319d6
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Victor Yang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Feishu Notifier
|
|
2
|
+
|
|
3
|
+
Ruby gem and CLI for sending Feishu app notifications. It supports plain text messages, custom interactive cards, existing CardKit card IDs, and existing card template IDs.
|
|
4
|
+
|
|
5
|
+
Repository: https://github.com/Youngv/feishu-notifier-ruby
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Sends messages through Feishu `im/v1/messages`.
|
|
10
|
+
- Uses `receive_id_type=user_id`.
|
|
11
|
+
- Reads app credentials from `.env`, environment variables, or `--config`.
|
|
12
|
+
- Provides built-in card levels: `info`, `debug`, and `error`.
|
|
13
|
+
- Can be used as a CLI or as a Ruby library.
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- Ruby 3.2 or newer.
|
|
18
|
+
- This project is developed with Ruby 3.4.7.
|
|
19
|
+
- A Feishu app with permission to send IM messages.
|
|
20
|
+
- A reachable Feishu recipient `user_id`.
|
|
21
|
+
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
Create a `.env` file:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
cp .env.example .env
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Required variables:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
FEISHU_APP_ID=cli_xxx
|
|
34
|
+
FEISHU_APP_SECRET=your_app_secret
|
|
35
|
+
FEISHU_USER_ID=your_user_id
|
|
36
|
+
FEISHU_BASE_URL=https://open.feishu.cn
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The local `.env` file is ignored by git. Do not commit real app secrets.
|
|
40
|
+
|
|
41
|
+
## Development Setup
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd /root/feishu-notifier-ruby
|
|
45
|
+
source /usr/local/rvm/scripts/rvm
|
|
46
|
+
rvm use ruby-3.4.7
|
|
47
|
+
bundle install
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Run tests:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bundle exec rake test
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## CLI Usage
|
|
57
|
+
|
|
58
|
+
Run from this repository:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
bin/feishu-notifier --level info --text "Hello from Ruby"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run after installing the gem:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
feishu-notifier --config /root/feishu-notifier-ruby/.env --level error --text "Something failed"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If the target project has its own `.env`, run `feishu-notifier` from that directory without `--config`.
|
|
71
|
+
|
|
72
|
+
### Plain Text
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bin/feishu-notifier --text "Hello from Ruby"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Custom Cards
|
|
79
|
+
|
|
80
|
+
The `--level` option sends a custom interactive card. Available levels:
|
|
81
|
+
|
|
82
|
+
- `info`: blue header, default title `Info`
|
|
83
|
+
- `debug`: grey header, default title `Debug`
|
|
84
|
+
- `error`: red header, default title `Error`
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
bin/feishu-notifier --level info --text "Normal notification"
|
|
88
|
+
bin/feishu-notifier --level debug --text "Debug details"
|
|
89
|
+
bin/feishu-notifier --level error --text "Something failed"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Override the card title:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
bin/feishu-notifier --level error --title "Deploy failed" --text "Rollback required"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Existing Cards and Templates
|
|
99
|
+
|
|
100
|
+
Send an existing CardKit card by card ID:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
bin/feishu-notifier --card-id CARD_ID
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Send an existing card template by template ID:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
bin/feishu-notifier --template-id TEMPLATE_ID
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Recipient Override
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
bin/feishu-notifier --user-id your_user_id --level info --text "Hello"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Dry Run
|
|
119
|
+
|
|
120
|
+
Use `--dry-run` to print the request summary without calling Feishu:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
bin/feishu-notifier --dry-run --level error --title "Gem check" --text "Installed executable works"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Ruby Library Usage
|
|
127
|
+
|
|
128
|
+
Add the gem to another Ruby project, or install the local gem first.
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
require "feishu_notifier"
|
|
132
|
+
|
|
133
|
+
FeishuNotifier::EnvFile.load("/root/feishu-notifier-ruby/.env")
|
|
134
|
+
config = FeishuNotifier::Config.from_env
|
|
135
|
+
client = FeishuNotifier::Client.new(config: config)
|
|
136
|
+
|
|
137
|
+
card = FeishuNotifier::Card.custom(
|
|
138
|
+
level: "error",
|
|
139
|
+
title: "Task failed",
|
|
140
|
+
text: "Check logs"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
client.send_card(card)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Send plain text:
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
client.send_text("Hello from Ruby")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Build and Install
|
|
153
|
+
|
|
154
|
+
Build the gem:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
gem build feishu_notifier.gemspec
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Install the generated gem:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
gem install ./feishu_notifier-0.1.1.gem
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Project Layout
|
|
167
|
+
|
|
168
|
+
- `bin/feishu-notifier`: CLI executable.
|
|
169
|
+
- `lib/feishu_notifier/client.rb`: Feishu API client.
|
|
170
|
+
- `lib/feishu_notifier/card.rb`: Card content builders.
|
|
171
|
+
- `lib/feishu_notifier/config.rb`: Environment-based configuration.
|
|
172
|
+
- `lib/feishu_notifier/env_file.rb`: Minimal `.env` loader.
|
|
173
|
+
- `lib/feishu_notifier/version.rb`: Gem version.
|
|
174
|
+
- `test/`: Minitest coverage.
|
|
175
|
+
|
|
176
|
+
## Permissions
|
|
177
|
+
|
|
178
|
+
The Feishu app must be enabled in the target tenant and have permission to send IM messages. The recipient must be reachable by `user_id`.
|
data/bin/feishu-notifier
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "optparse"
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require "feishu_notifier"
|
|
8
|
+
rescue LoadError
|
|
9
|
+
require_relative "../lib/feishu_notifier"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
options = {
|
|
13
|
+
config: ENV.fetch("FEISHU_CONFIG", ".env"),
|
|
14
|
+
dry_run: false,
|
|
15
|
+
message_type: nil,
|
|
16
|
+
level: "info"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
parser = OptionParser.new do |opts|
|
|
20
|
+
opts.banner = "Usage: feishu-notifier --text MESSAGE | --card-id CARD_ID | --template-id TEMPLATE_ID [options]"
|
|
21
|
+
|
|
22
|
+
opts.on("-t", "--text MESSAGE", "Text message to send") { |value| options[:text] = value }
|
|
23
|
+
opts.on("--card", "Send an interactive card built from --text") { options[:message_type] = "interactive" }
|
|
24
|
+
opts.on("--level LEVEL", "Card level: info, debug, or error") do |value|
|
|
25
|
+
options[:level] = value
|
|
26
|
+
options[:message_type] = "interactive"
|
|
27
|
+
end
|
|
28
|
+
opts.on("--card-id CARD_ID", "Send an existing Feishu CardKit card") do |value|
|
|
29
|
+
options[:card_id] = value
|
|
30
|
+
options[:message_type] = "interactive"
|
|
31
|
+
end
|
|
32
|
+
opts.on("--template-id TEMPLATE_ID", "Send an existing Feishu card template") do |value|
|
|
33
|
+
options[:template_id] = value
|
|
34
|
+
options[:message_type] = "interactive"
|
|
35
|
+
end
|
|
36
|
+
opts.on("--title TITLE", "Card title when using --card") { |value| options[:title] = value }
|
|
37
|
+
opts.on("-u", "--user-id USER_ID", "Override FEISHU_USER_ID") { |value| options[:user_id] = value }
|
|
38
|
+
opts.on("-c", "--config PATH", "Load environment variables from PATH") { |value| options[:config] = value }
|
|
39
|
+
opts.on("--dry-run", "Print request summary without calling Feishu") { options[:dry_run] = true }
|
|
40
|
+
opts.on("-h", "--help", "Print help") do
|
|
41
|
+
puts opts
|
|
42
|
+
exit
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
parser.parse!
|
|
47
|
+
|
|
48
|
+
card_id = options[:card_id].to_s.strip
|
|
49
|
+
template_id = options[:template_id].to_s.strip
|
|
50
|
+
message = options[:text].to_s.strip
|
|
51
|
+
options[:message_type] ||= "text"
|
|
52
|
+
abort parser.to_s if card_id.empty? && template_id.empty? && message.empty?
|
|
53
|
+
|
|
54
|
+
FeishuNotifier::EnvFile.load(options[:config])
|
|
55
|
+
ENV["FEISHU_USER_ID"] = options[:user_id] if options[:user_id]
|
|
56
|
+
|
|
57
|
+
config = FeishuNotifier::Config.from_env
|
|
58
|
+
content = if !card_id.empty?
|
|
59
|
+
FeishuNotifier::Card.by_id(card_id)
|
|
60
|
+
elsif !template_id.empty?
|
|
61
|
+
FeishuNotifier::Card.template(template_id)
|
|
62
|
+
elsif options[:message_type] == "interactive"
|
|
63
|
+
FeishuNotifier::Card.custom(title: options[:title], text: message, level: options[:level])
|
|
64
|
+
else
|
|
65
|
+
{ text: message }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if options[:dry_run]
|
|
69
|
+
puts JSON.pretty_generate(
|
|
70
|
+
{
|
|
71
|
+
app_id: config.app_id,
|
|
72
|
+
user_id: config.user_id,
|
|
73
|
+
endpoint: "#{config.base_url}/open-apis/im/v1/messages?receive_id_type=user_id",
|
|
74
|
+
msg_type: options[:message_type],
|
|
75
|
+
content: content
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
exit
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
client = FeishuNotifier::Client.new(config: config)
|
|
82
|
+
if options[:message_type] == "interactive"
|
|
83
|
+
client.send_card(content)
|
|
84
|
+
else
|
|
85
|
+
client.send_text(message)
|
|
86
|
+
end
|
|
87
|
+
puts "#{options[:message_type]} message sent to #{config.user_id}"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module FeishuNotifier
|
|
2
|
+
module Card
|
|
3
|
+
LEVELS = {
|
|
4
|
+
"info" => {
|
|
5
|
+
title: "Info",
|
|
6
|
+
template: "blue"
|
|
7
|
+
},
|
|
8
|
+
"debug" => {
|
|
9
|
+
title: "Debug",
|
|
10
|
+
template: "grey"
|
|
11
|
+
},
|
|
12
|
+
"error" => {
|
|
13
|
+
title: "Error",
|
|
14
|
+
template: "red"
|
|
15
|
+
}
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
def by_id(card_id)
|
|
21
|
+
{
|
|
22
|
+
type: "card",
|
|
23
|
+
data: {
|
|
24
|
+
card_id: card_id
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def template(template_id, variables: {})
|
|
30
|
+
{
|
|
31
|
+
type: "template",
|
|
32
|
+
data: {
|
|
33
|
+
template_id: template_id,
|
|
34
|
+
template_variable: variables
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def custom(title: nil, text:, level: "info")
|
|
40
|
+
style = LEVELS.fetch(level) do
|
|
41
|
+
raise ArgumentError, "Unsupported card level: #{level}. Expected one of: #{LEVELS.keys.join(", ")}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
config: {
|
|
46
|
+
wide_screen_mode: true
|
|
47
|
+
},
|
|
48
|
+
header: {
|
|
49
|
+
template: style.fetch(:template),
|
|
50
|
+
title: {
|
|
51
|
+
tag: "plain_text",
|
|
52
|
+
content: title.to_s.empty? ? style.fetch(:title) : title
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
elements: [
|
|
56
|
+
{
|
|
57
|
+
tag: "div",
|
|
58
|
+
text: {
|
|
59
|
+
tag: "lark_md",
|
|
60
|
+
content: text
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def simple(title:, text:)
|
|
68
|
+
custom(title: title, text: text, level: "info")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "net/http"
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module FeishuNotifier
|
|
6
|
+
class Client
|
|
7
|
+
TOKEN_PATH = "/open-apis/auth/v3/tenant_access_token/internal".freeze
|
|
8
|
+
MESSAGE_PATH = "/open-apis/im/v1/messages".freeze
|
|
9
|
+
|
|
10
|
+
def initialize(config:, http: Net::HTTP)
|
|
11
|
+
@config = config
|
|
12
|
+
@http = http
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def send_text(text, user_id: @config.user_id)
|
|
16
|
+
send_message(
|
|
17
|
+
user_id: user_id,
|
|
18
|
+
msg_type: "text",
|
|
19
|
+
content: { text: text }
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def send_card(card, user_id: @config.user_id)
|
|
24
|
+
send_message(
|
|
25
|
+
user_id: user_id,
|
|
26
|
+
msg_type: "interactive",
|
|
27
|
+
content: card
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def tenant_access_token
|
|
32
|
+
response = post_json(
|
|
33
|
+
TOKEN_PATH,
|
|
34
|
+
{
|
|
35
|
+
app_id: @config.app_id,
|
|
36
|
+
app_secret: @config.app_secret
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
token = response["tenant_access_token"].to_s
|
|
41
|
+
raise ApiError, "Feishu token response did not include tenant_access_token" if token.empty?
|
|
42
|
+
|
|
43
|
+
token
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def send_message(user_id:, msg_type:, content:)
|
|
49
|
+
token = tenant_access_token
|
|
50
|
+
post_json(
|
|
51
|
+
"#{MESSAGE_PATH}?receive_id_type=user_id",
|
|
52
|
+
{
|
|
53
|
+
receive_id: user_id,
|
|
54
|
+
msg_type: msg_type,
|
|
55
|
+
content: JSON.generate(content)
|
|
56
|
+
},
|
|
57
|
+
headers: { "Authorization" => "Bearer #{token}" }
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def post_json(path, payload, headers: {})
|
|
62
|
+
uri = URI.join(@config.base_url, path)
|
|
63
|
+
request = Net::HTTP::Post.new(uri)
|
|
64
|
+
request["Content-Type"] = "application/json; charset=utf-8"
|
|
65
|
+
headers.each { |key, value| request[key] = value }
|
|
66
|
+
request.body = JSON.generate(payload)
|
|
67
|
+
|
|
68
|
+
response = @http.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |connection|
|
|
69
|
+
connection.request(request)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
parse_response(response)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parse_response(response)
|
|
76
|
+
body = JSON.parse(response.body)
|
|
77
|
+
code = body.fetch("code", 0)
|
|
78
|
+
return body if response.is_a?(Net::HTTPSuccess) && code.to_i.zero?
|
|
79
|
+
|
|
80
|
+
message = body["msg"] || body["message"] || response.message
|
|
81
|
+
raise ApiError, "Feishu API error: HTTP #{response.code}, code #{code}, #{message}"
|
|
82
|
+
rescue JSON::ParserError
|
|
83
|
+
raise ApiError, "Feishu API returned non-JSON response: HTTP #{response.code}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class ApiError < StandardError; end
|
|
88
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module FeishuNotifier
|
|
2
|
+
Config = Data.define(:app_id, :app_secret, :user_id, :base_url) do
|
|
3
|
+
DEFAULT_BASE_URL = "https://open.feishu.cn".freeze
|
|
4
|
+
|
|
5
|
+
def self.from_env(env = ENV)
|
|
6
|
+
new(
|
|
7
|
+
app_id: fetch_required(env, "FEISHU_APP_ID"),
|
|
8
|
+
app_secret: fetch_required(env, "FEISHU_APP_SECRET"),
|
|
9
|
+
user_id: fetch_required(env, "FEISHU_USER_ID"),
|
|
10
|
+
base_url: env.fetch("FEISHU_BASE_URL", DEFAULT_BASE_URL)
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.fetch_required(env, key)
|
|
15
|
+
value = env[key].to_s.strip
|
|
16
|
+
raise ArgumentError, "Missing required environment variable: #{key}" if value.empty?
|
|
17
|
+
|
|
18
|
+
value
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module FeishuNotifier
|
|
2
|
+
module EnvFile
|
|
3
|
+
module_function
|
|
4
|
+
|
|
5
|
+
def load(path)
|
|
6
|
+
return unless File.file?(path)
|
|
7
|
+
|
|
8
|
+
File.readlines(path, chomp: true).each do |line|
|
|
9
|
+
stripped = line.strip
|
|
10
|
+
next if stripped.empty? || stripped.start_with?("#")
|
|
11
|
+
|
|
12
|
+
key, value = stripped.split("=", 2)
|
|
13
|
+
next if key.nil? || value.nil? || key.empty?
|
|
14
|
+
|
|
15
|
+
ENV[key] = unquote(value.strip)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def unquote(value)
|
|
20
|
+
if (value.start_with?('"') && value.end_with?('"')) ||
|
|
21
|
+
(value.start_with?("'") && value.end_with?("'"))
|
|
22
|
+
value[1...-1]
|
|
23
|
+
else
|
|
24
|
+
value
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: feishu_notifier
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Victor Yang
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: minitest
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '5.25'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '5.25'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.2'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.2'
|
|
40
|
+
description: A small Ruby client and CLI for sending Feishu app notifications with
|
|
41
|
+
text and custom card levels.
|
|
42
|
+
executables:
|
|
43
|
+
- feishu-notifier
|
|
44
|
+
extensions: []
|
|
45
|
+
extra_rdoc_files: []
|
|
46
|
+
files:
|
|
47
|
+
- LICENSE
|
|
48
|
+
- README.md
|
|
49
|
+
- bin/feishu-notifier
|
|
50
|
+
- lib/feishu_notifier.rb
|
|
51
|
+
- lib/feishu_notifier/card.rb
|
|
52
|
+
- lib/feishu_notifier/client.rb
|
|
53
|
+
- lib/feishu_notifier/config.rb
|
|
54
|
+
- lib/feishu_notifier/env_file.rb
|
|
55
|
+
- lib/feishu_notifier/version.rb
|
|
56
|
+
homepage: https://github.com/Youngv/feishu-notifier-ruby
|
|
57
|
+
licenses:
|
|
58
|
+
- MIT
|
|
59
|
+
metadata:
|
|
60
|
+
rubygems_mfa_required: 'true'
|
|
61
|
+
rdoc_options: []
|
|
62
|
+
require_paths:
|
|
63
|
+
- lib
|
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 3.2.0
|
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubygems_version: 3.6.9
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: Send Feishu text and card notifications from Ruby.
|
|
78
|
+
test_files: []
|