telegrama 0.1.3 → 0.3.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/.simplecov +35 -0
- data/AGENTS.md +5 -0
- data/Appraisals +20 -0
- data/CHANGELOG.md +30 -0
- data/CLAUDE.md +5 -0
- data/README.md +33 -9
- data/gemfiles/rails_7.2.gemfile +19 -0
- data/gemfiles/rails_8.0.gemfile +19 -0
- data/gemfiles/rails_8.1.gemfile +19 -0
- data/lib/telegrama/client.rb +57 -13
- data/lib/telegrama/configuration.rb +24 -1
- data/lib/telegrama/formatter.rb +68 -6
- data/lib/telegrama/version.rb +1 -1
- data/lib/telegrama.rb +10 -1
- metadata +23 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ae6c6b2bb6c3da545107d0f91b63e490fbd6e7e5a6e6be633291346c9f6ebcb
|
|
4
|
+
data.tar.gz: 42b7590b77f6497a8b972a97a1bb8cd371f89bf5db322bcd779b7d0a7ae9a331
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 596ad2ca19bb16c3c50d83f0ee7891e4a0ffe44218f34de187497ddb23a58862695564183402abe3b13e290801dc3bcaf033e00330e26b5faf5da2d97eb6e3bd
|
|
7
|
+
data.tar.gz: 810fe478ac180741ca83d2210eb179776ad815e647297cb037ad1d33a49cf0a81630bc27ac11a4393309ef4be23b23f330f0b75754c60cacaf37b78668adb658
|
data/.simplecov
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SimpleCov configuration file (auto-loaded before test suite)
|
|
4
|
+
# This keeps test_helper.rb clean and follows best practices
|
|
5
|
+
|
|
6
|
+
SimpleCov.start do
|
|
7
|
+
# Use SimpleFormatter for terminal-only output (no HTML generation)
|
|
8
|
+
formatter SimpleCov::Formatter::SimpleFormatter
|
|
9
|
+
|
|
10
|
+
# Track coverage for the lib directory (gem source code)
|
|
11
|
+
add_filter "/test/"
|
|
12
|
+
|
|
13
|
+
# Track the lib and app directories
|
|
14
|
+
track_files "{lib,app}/**/*.rb"
|
|
15
|
+
|
|
16
|
+
# Enable branch coverage for more detailed metrics
|
|
17
|
+
enable_coverage :branch
|
|
18
|
+
|
|
19
|
+
# Set minimum coverage threshold to prevent coverage regression
|
|
20
|
+
minimum_coverage line: 80, branch: 75
|
|
21
|
+
|
|
22
|
+
# Disambiguate parallel test runs
|
|
23
|
+
command_name "Job #{ENV['TEST_ENV_NUMBER']}" if ENV['TEST_ENV_NUMBER']
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Print coverage summary to terminal after tests complete
|
|
27
|
+
SimpleCov.at_exit do
|
|
28
|
+
SimpleCov.result.format!
|
|
29
|
+
puts "\n" + "=" * 60
|
|
30
|
+
puts "COVERAGE SUMMARY"
|
|
31
|
+
puts "=" * 60
|
|
32
|
+
puts "Line Coverage: #{SimpleCov.result.covered_percent.round(2)}%"
|
|
33
|
+
puts "Branch Coverage: #{SimpleCov.result.coverage_statistics[:branch]&.percent&.round(2) || 'N/A'}%"
|
|
34
|
+
puts "=" * 60
|
|
35
|
+
end
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to AI Agents (like OpenAI's Codex, Cursor Agent, Claude Code, etc) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
|
data/Appraisals
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Note: Rails < 7.2 is not compatible with Ruby 3.4
|
|
4
|
+
# (Logger became a bundled gem in Ruby 3.4, and only Rails 7.2+ handles this)
|
|
5
|
+
# See: https://stdgems.org/logger/
|
|
6
|
+
|
|
7
|
+
# Test against Rails 7.2 (minimum version compatible with Ruby 3.4)
|
|
8
|
+
appraise "rails-7.2" do
|
|
9
|
+
gem "rails", "~> 7.2.0"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Test against Rails 8.0
|
|
13
|
+
appraise "rails-8.0" do
|
|
14
|
+
gem "rails", "~> 8.0.0"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Test against Rails 8.1 (latest)
|
|
18
|
+
appraise "rails-8.1" do
|
|
19
|
+
gem "rails", "~> 8.1.0"
|
|
20
|
+
end
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [0.3.0] - 2026-05-22
|
|
4
|
+
|
|
5
|
+
- Added support for sending messages to Telegram forum topics with `message_thread_id`
|
|
6
|
+
- Added `config.message_thread_id` as an optional default forum topic for all messages
|
|
7
|
+
- Added per-message `message_thread_id:` overrides, including `message_thread_id: nil`
|
|
8
|
+
to bypass a configured default topic for one send
|
|
9
|
+
- Preserved `message_thread_id` through async ActiveJob delivery, string-key serialized
|
|
10
|
+
options, and Markdown-to-HTML-to-plain-text fallback retries
|
|
11
|
+
- Added validation so `message_thread_id` must be a positive integer when present
|
|
12
|
+
- Fixed `parse_mode: nil` to omit the `parse_mode` parameter from Telegram API
|
|
13
|
+
requests instead of sending `null`
|
|
14
|
+
- Fixed plain-text sends so `parse_mode: nil` does not inherit MarkdownV2 or HTML
|
|
15
|
+
escaping from configured formatting defaults
|
|
16
|
+
- Fixed HTML sends so they do not inherit MarkdownV2 escaping from configured
|
|
17
|
+
formatting defaults
|
|
18
|
+
- Fixed MarkdownV2 escaping for underscores inside identifiers such as `is_bot`,
|
|
19
|
+
`message_thread_id`, and `send_message`
|
|
20
|
+
|
|
21
|
+
## [0.2.0] - 2026-01-17
|
|
22
|
+
|
|
23
|
+
- Added Minitest test suite
|
|
24
|
+
- Fixed duplicate Error class definition causing conflicts
|
|
25
|
+
- Fixed missing `log_info` method
|
|
26
|
+
- Fixed `parse_mode: nil` handling to properly respect explicit nil override
|
|
27
|
+
- Fixed `strip_markdown` method to correctly strip markdown characters
|
|
28
|
+
- Fixed `retry_delay` validation to accept float values (e.g., 0.5 seconds)
|
|
29
|
+
- Added Ruby 3.5+ compatibility with `ostruct` dependency
|
|
30
|
+
|
|
1
31
|
## [0.1.3] - 2025-02-28
|
|
2
32
|
|
|
3
33
|
- Added client options for retries and timeout
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
|
data/README.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
# 💬 `telegrama` –
|
|
1
|
+
# 💬 `telegrama` – Send Telegram admin notifications in your Rails app
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/rb/telegrama) [](https://github.com/rameerez/telegrama/actions)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> [!TIP]
|
|
6
|
+
> **🚀 Ship your next Rails app 10x faster!** I've built **[RailsFast](https://railsfast.com/?ref=telegrama)**, a production-ready Rails boilerplate template that comes with everything you need to launch a software business in days, not weeks. Go [check it out](https://railsfast.com/?ref=telegrama)!
|
|
7
|
+
|
|
8
|
+
`telegrama` lets you send quick, simple admin / logging Telegram messages via a Telegram bot.
|
|
6
9
|
|
|
7
10
|
Useful for Rails developers using Telegram messages for notifications, admin alerts, errors, logs, daily summaries, and status updates, like:
|
|
8
11
|
|
|
@@ -50,6 +53,12 @@ Telegrama.send_message(a_general_message, chat_id: general_chat_id)
|
|
|
50
53
|
Telegrama.send_message(marketing_message, chat_id: marketing_chat_id)
|
|
51
54
|
```
|
|
52
55
|
|
|
56
|
+
You can also send messages to a specific topic inside a forum-enabled Telegram group:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
Telegrama.send_message(deploy_message, chat_id: engineering_group_id, message_thread_id: deployments_topic_id)
|
|
60
|
+
```
|
|
61
|
+
|
|
53
62
|
The goal with this gem is to provide a straightforward, no-frills, minimal API to send Telegram messages reliably for admin purposes, without you having to write your own wrapper over the Telegram API.
|
|
54
63
|
|
|
55
64
|
## Quick start
|
|
@@ -72,6 +81,7 @@ Then, create an initializer file under `config/initializers/telegrama.rb` and se
|
|
|
72
81
|
Telegrama.configure do |config|
|
|
73
82
|
config.bot_token = Rails.application.credentials.dig(Rails.env.to_sym, :telegram, :bot_token)
|
|
74
83
|
config.chat_id = Rails.application.credentials.dig(Rails.env.to_sym, :telegram, :chat_id)
|
|
84
|
+
config.message_thread_id = nil # Optional default Telegram forum topic ID
|
|
75
85
|
config.default_parse_mode = 'MarkdownV2'
|
|
76
86
|
|
|
77
87
|
# Optional prefix/suffix for all messages (useful to identify messages from different apps or environments)
|
|
@@ -160,6 +170,22 @@ Both `message_prefix` and `message_suffix` are optional and can be used independ
|
|
|
160
170
|
Telegrama.send_message("Hello, alternate group!", chat_id: alternate_chat_id)
|
|
161
171
|
```
|
|
162
172
|
|
|
173
|
+
- **`message_thread_id`**
|
|
174
|
+
*Send to a specific Telegram forum topic inside a group. Telegram's Bot API calls topic IDs `message_thread_id` values.*
|
|
175
|
+
**Usage Example:**
|
|
176
|
+
```ruby
|
|
177
|
+
Telegrama.send_message("Deploy finished!", chat_id: engineering_group_id, message_thread_id: deployments_topic_id)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
You can get this ID from an incoming Telegram update sent inside the topic, or from the `ForumTopic` returned by Telegram's `createForumTopic` API.
|
|
181
|
+
|
|
182
|
+
You can also configure a default topic:
|
|
183
|
+
```ruby
|
|
184
|
+
config.message_thread_id = deployments_topic_id
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
To bypass a configured default topic for one message, pass `message_thread_id: nil`.
|
|
188
|
+
|
|
163
189
|
- **`parse_mode`**
|
|
164
190
|
*Override the default parse mode (default is `"MarkdownV2"`).*
|
|
165
191
|
**Usage Example:**
|
|
@@ -264,7 +290,7 @@ Telegrama includes a sophisticated state machine-based markdown formatter that p
|
|
|
264
290
|
|
|
265
291
|
The formatter is designed to be robust even with complex inputs, ensuring your messages always look great in Telegram:
|
|
266
292
|
|
|
267
|
-
|
|
293
|
+
````ruby
|
|
268
294
|
# Complex formatting example that works perfectly
|
|
269
295
|
message = <<~MSG
|
|
270
296
|
📊 *Monthly Report*
|
|
@@ -287,20 +313,18 @@ message = <<~MSG
|
|
|
287
313
|
MSG
|
|
288
314
|
|
|
289
315
|
Telegrama.send_message(message)
|
|
316
|
+
````
|
|
290
317
|
|
|
291
318
|
## Testing
|
|
292
319
|
|
|
293
|
-
The gem includes a
|
|
320
|
+
The gem includes a Minitest test suite.
|
|
294
321
|
|
|
295
322
|
To run the tests:
|
|
296
323
|
|
|
297
324
|
```bash
|
|
298
|
-
bundle install
|
|
299
325
|
bundle exec rake test
|
|
300
326
|
```
|
|
301
327
|
|
|
302
|
-
The test suite uses SQLite3 in-memory database and requires no additional setup.
|
|
303
|
-
|
|
304
328
|
## Development
|
|
305
329
|
|
|
306
330
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
@@ -313,4 +337,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/rameer
|
|
|
313
337
|
|
|
314
338
|
## License
|
|
315
339
|
|
|
316
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
340
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# This file was generated by Appraisal
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gem "irb"
|
|
6
|
+
gem "rake", "~> 13.0"
|
|
7
|
+
gem "rails", "~> 7.2.0"
|
|
8
|
+
|
|
9
|
+
group :test do
|
|
10
|
+
gem "appraisal"
|
|
11
|
+
gem "minitest", "~> 6.0"
|
|
12
|
+
gem "minitest-mock"
|
|
13
|
+
gem "minitest-reporters", "~> 1.6"
|
|
14
|
+
gem "rack-test"
|
|
15
|
+
gem "simplecov", require: false
|
|
16
|
+
gem "webmock", "~> 3.23"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
gemspec path: "../"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# This file was generated by Appraisal
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gem "irb"
|
|
6
|
+
gem "rake", "~> 13.0"
|
|
7
|
+
gem "rails", "~> 8.0.0"
|
|
8
|
+
|
|
9
|
+
group :test do
|
|
10
|
+
gem "appraisal"
|
|
11
|
+
gem "minitest", "~> 6.0"
|
|
12
|
+
gem "minitest-mock"
|
|
13
|
+
gem "minitest-reporters", "~> 1.6"
|
|
14
|
+
gem "rack-test"
|
|
15
|
+
gem "simplecov", require: false
|
|
16
|
+
gem "webmock", "~> 3.23"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
gemspec path: "../"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# This file was generated by Appraisal
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gem "irb"
|
|
6
|
+
gem "rake", "~> 13.0"
|
|
7
|
+
gem "rails", "~> 8.1.0"
|
|
8
|
+
|
|
9
|
+
group :test do
|
|
10
|
+
gem "appraisal"
|
|
11
|
+
gem "minitest", "~> 6.0"
|
|
12
|
+
gem "minitest-mock"
|
|
13
|
+
gem "minitest-reporters", "~> 1.6"
|
|
14
|
+
gem "rack-test"
|
|
15
|
+
gem "simplecov", require: false
|
|
16
|
+
gem "webmock", "~> 3.23"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
gemspec path: "../"
|
data/lib/telegrama/client.rb
CHANGED
|
@@ -13,25 +13,33 @@ module Telegrama
|
|
|
13
13
|
|
|
14
14
|
# Send a message with built-in error handling and fallbacks
|
|
15
15
|
def send_message(message, options = {})
|
|
16
|
+
options = normalize_option_keys(options)
|
|
17
|
+
|
|
16
18
|
# Allow chat ID override; fallback to config default
|
|
17
19
|
chat_id = options.delete(:chat_id) || Telegrama.configuration.chat_id
|
|
18
20
|
|
|
21
|
+
# Allow topic/thread override; fallback to config default.
|
|
22
|
+
# Explicit nil means "send to the chat normally", even when a default topic is configured.
|
|
23
|
+
message_thread_id = if options.key?(:message_thread_id)
|
|
24
|
+
options.delete(:message_thread_id)
|
|
25
|
+
else
|
|
26
|
+
Telegrama.configuration.message_thread_id
|
|
27
|
+
end
|
|
28
|
+
validate_message_thread_id!(message_thread_id)
|
|
29
|
+
|
|
19
30
|
# Get client options from config
|
|
20
31
|
client_opts = Telegrama.configuration.client_options || {}
|
|
21
32
|
client_opts = client_opts.merge(@config)
|
|
22
33
|
|
|
23
34
|
# Default to MarkdownV2 parse mode unless explicitly overridden
|
|
24
|
-
|
|
35
|
+
# Use key? to allow explicit nil override (for plain text without formatting)
|
|
36
|
+
parse_mode = options.key?(:parse_mode) ? options[:parse_mode] : Telegrama.configuration.default_parse_mode
|
|
25
37
|
|
|
26
|
-
# Allow runtime formatting options, merging with configured defaults
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
formatting_opts[:escape_markdown] = true unless formatting_opts.key?(:escape_markdown)
|
|
32
|
-
elsif parse_mode == 'HTML'
|
|
33
|
-
formatting_opts[:escape_html] = true unless formatting_opts.key?(:escape_html)
|
|
34
|
-
end
|
|
38
|
+
# Allow runtime formatting options, merging with configured defaults.
|
|
39
|
+
# Escape behavior must match the outgoing Telegram parse mode; otherwise
|
|
40
|
+
# plain-text sends can display MarkdownV2 escape characters literally.
|
|
41
|
+
formatting_opts = normalize_option_keys(options.delete(:formatting) || {})
|
|
42
|
+
formatting_opts = formatting_options_for_parse_mode(parse_mode, formatting_opts)
|
|
35
43
|
|
|
36
44
|
# Format the message text with our formatter
|
|
37
45
|
formatted_message = Formatter.format(message, formatting_opts)
|
|
@@ -45,10 +53,11 @@ module Telegrama
|
|
|
45
53
|
payload = {
|
|
46
54
|
chat_id: chat_id,
|
|
47
55
|
text: formatted_message,
|
|
48
|
-
parse_mode: parse_mode,
|
|
49
56
|
disable_web_page_preview: options.fetch(:disable_web_page_preview,
|
|
50
57
|
Telegrama.configuration.disable_web_page_preview)
|
|
51
58
|
}
|
|
59
|
+
payload[:parse_mode] = parse_mode unless parse_mode.nil?
|
|
60
|
+
payload[:message_thread_id] = message_thread_id unless message_thread_id.nil?
|
|
52
61
|
|
|
53
62
|
# Additional options such as reply_markup can be added here
|
|
54
63
|
payload.merge!(options.select { |k, _| [:reply_markup, :reply_to_message_id].include?(k) })
|
|
@@ -114,6 +123,43 @@ module Telegrama
|
|
|
114
123
|
|
|
115
124
|
private
|
|
116
125
|
|
|
126
|
+
def normalize_option_keys(options)
|
|
127
|
+
return {} if options.nil?
|
|
128
|
+
|
|
129
|
+
options.to_h.each_with_object({}) do |(key, value), normalized|
|
|
130
|
+
normalized[key.respond_to?(:to_sym) ? key.to_sym : key] = value
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def validate_message_thread_id!(message_thread_id)
|
|
135
|
+
return if message_thread_id.nil?
|
|
136
|
+
|
|
137
|
+
unless message_thread_id.is_a?(Integer) && message_thread_id.positive?
|
|
138
|
+
raise ArgumentError, "Telegrama send_message option error: message_thread_id must be a positive integer."
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def formatting_options_for_parse_mode(parse_mode, formatting_opts)
|
|
143
|
+
case parse_mode
|
|
144
|
+
when 'MarkdownV2'
|
|
145
|
+
{
|
|
146
|
+
escape_html: false
|
|
147
|
+
}.merge(formatting_opts).tap do |opts|
|
|
148
|
+
opts[:escape_markdown] = true unless opts.key?(:escape_markdown)
|
|
149
|
+
end
|
|
150
|
+
when 'HTML'
|
|
151
|
+
{
|
|
152
|
+
escape_markdown: false
|
|
153
|
+
}.merge(formatting_opts).tap do |opts|
|
|
154
|
+
opts[:escape_html] = true unless opts.key?(:escape_html)
|
|
155
|
+
end
|
|
156
|
+
when nil
|
|
157
|
+
formatting_opts.merge(escape_markdown: false, escape_html: false)
|
|
158
|
+
else
|
|
159
|
+
formatting_opts
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
117
163
|
def perform_request(payload, options = {})
|
|
118
164
|
uri = URI("https://api.telegram.org/bot#{Telegrama.configuration.bot_token}/sendMessage")
|
|
119
165
|
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
|
@@ -159,6 +205,4 @@ module Telegrama
|
|
|
159
205
|
defined?(Rails) && Rails.respond_to?(:logger) ? Rails.logger : Logger.new($stdout)
|
|
160
206
|
end
|
|
161
207
|
end
|
|
162
|
-
|
|
163
|
-
class Error < StandardError; end
|
|
164
208
|
end
|
|
@@ -10,6 +10,10 @@ module Telegrama
|
|
|
10
10
|
# You can override this on the fly when sending messages.
|
|
11
11
|
attr_accessor :chat_id
|
|
12
12
|
|
|
13
|
+
# Default message thread ID for sending messages to a forum topic.
|
|
14
|
+
# You can override this on the fly when sending messages.
|
|
15
|
+
attr_accessor :message_thread_id
|
|
16
|
+
|
|
13
17
|
# Default parse mode for messages (e.g. "MarkdownV2" or "HTML").
|
|
14
18
|
attr_accessor :default_parse_mode
|
|
15
19
|
|
|
@@ -57,6 +61,7 @@ module Telegrama
|
|
|
57
61
|
# Credentials (must be set via initializer)
|
|
58
62
|
@bot_token = nil
|
|
59
63
|
@chat_id = nil
|
|
64
|
+
@message_thread_id = nil
|
|
60
65
|
|
|
61
66
|
# Defaults for message formatting
|
|
62
67
|
@default_parse_mode = 'MarkdownV2'
|
|
@@ -90,6 +95,7 @@ module Telegrama
|
|
|
90
95
|
def validate!
|
|
91
96
|
validate_bot_token!
|
|
92
97
|
validate_default_parse_mode!
|
|
98
|
+
validate_message_thread_id!
|
|
93
99
|
validate_formatting_options!
|
|
94
100
|
validate_client_options!
|
|
95
101
|
true
|
|
@@ -110,6 +116,14 @@ module Telegrama
|
|
|
110
116
|
end
|
|
111
117
|
end
|
|
112
118
|
|
|
119
|
+
def validate_message_thread_id!
|
|
120
|
+
return if message_thread_id.nil?
|
|
121
|
+
|
|
122
|
+
unless message_thread_id.is_a?(Integer) && message_thread_id.positive?
|
|
123
|
+
raise ArgumentError, "Telegrama configuration error: message_thread_id must be a positive integer."
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
113
127
|
def validate_formatting_options!
|
|
114
128
|
unless formatting_options.is_a?(Hash)
|
|
115
129
|
raise ArgumentError, "Telegrama configuration error: formatting_options must be a hash."
|
|
@@ -134,7 +148,8 @@ module Telegrama
|
|
|
134
148
|
raise ArgumentError, "Telegrama configuration error: client_options must be a hash."
|
|
135
149
|
end
|
|
136
150
|
|
|
137
|
-
|
|
151
|
+
# timeout and retry_count must be positive integers
|
|
152
|
+
[:timeout, :retry_count].each do |key|
|
|
138
153
|
if client_options.key?(key)
|
|
139
154
|
val = client_options[key]
|
|
140
155
|
unless val.is_a?(Integer) && val.positive?
|
|
@@ -142,6 +157,14 @@ module Telegrama
|
|
|
142
157
|
end
|
|
143
158
|
end
|
|
144
159
|
end
|
|
160
|
+
|
|
161
|
+
# retry_delay can be a positive integer or float (e.g., 0.5 seconds)
|
|
162
|
+
if client_options.key?(:retry_delay)
|
|
163
|
+
val = client_options[:retry_delay]
|
|
164
|
+
unless val.is_a?(Numeric) && val.positive?
|
|
165
|
+
raise ArgumentError, "Telegrama configuration error: client_options[:retry_delay] must be a positive number."
|
|
166
|
+
end
|
|
167
|
+
end
|
|
145
168
|
end
|
|
146
169
|
end
|
|
147
170
|
end
|
data/lib/telegrama/formatter.rb
CHANGED
|
@@ -179,8 +179,12 @@ module Telegrama
|
|
|
179
179
|
@result += '*'
|
|
180
180
|
advance
|
|
181
181
|
elsif char == '_' && !escaped?
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
if italic_opening_delimiter?
|
|
183
|
+
enter_state(:italic)
|
|
184
|
+
@result += '_'
|
|
185
|
+
else
|
|
186
|
+
@result += '\\_'
|
|
187
|
+
end
|
|
184
188
|
advance
|
|
185
189
|
elsif char == '[' && !escaped?
|
|
186
190
|
if looking_at_markdown_link?
|
|
@@ -256,8 +260,12 @@ module Telegrama
|
|
|
256
260
|
char = current_char
|
|
257
261
|
|
|
258
262
|
if char == '_' && !escaped?
|
|
259
|
-
|
|
260
|
-
|
|
263
|
+
if italic_closing_delimiter?
|
|
264
|
+
exit_state
|
|
265
|
+
@result += '_'
|
|
266
|
+
else
|
|
267
|
+
@result += '\\_'
|
|
268
|
+
end
|
|
261
269
|
advance
|
|
262
270
|
elsif char == '\\' && !escaped?
|
|
263
271
|
handle_escape_sequence
|
|
@@ -389,6 +397,34 @@ module Telegrama
|
|
|
389
397
|
@chars[@position + 1]
|
|
390
398
|
end
|
|
391
399
|
|
|
400
|
+
# Get the previous character
|
|
401
|
+
def previous_char
|
|
402
|
+
@position.positive? ? @chars[@position - 1] : nil
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Underscores inside identifiers should render literally, not start italics.
|
|
406
|
+
def italic_opening_delimiter?
|
|
407
|
+
following = next_char
|
|
408
|
+
return false if following.nil? || whitespace?(following)
|
|
409
|
+
|
|
410
|
+
!word_char?(previous_char)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def italic_closing_delimiter?
|
|
414
|
+
previous = previous_char
|
|
415
|
+
return false if previous.nil? || whitespace?(previous)
|
|
416
|
+
|
|
417
|
+
!word_char?(next_char)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def word_char?(char)
|
|
421
|
+
!!(char =~ /[[:alnum:]]/)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def whitespace?(char)
|
|
425
|
+
!!(char =~ /\s/)
|
|
426
|
+
end
|
|
427
|
+
|
|
392
428
|
# Check if next character is one of the given characters
|
|
393
429
|
def next_char_is?(*chars)
|
|
394
430
|
has_chars_ahead?(1) && chars.include?(next_char)
|
|
@@ -473,8 +509,34 @@ module Telegrama
|
|
|
473
509
|
# @param text [String] The text with markdown formatting
|
|
474
510
|
# @return [String] The text with markdown formatting removed
|
|
475
511
|
def self.strip_markdown(text)
|
|
476
|
-
|
|
477
|
-
|
|
512
|
+
result = text.dup
|
|
513
|
+
|
|
514
|
+
# Remove markdown links [text](url) -> text
|
|
515
|
+
result.gsub!(/\[([^\]]*)\]\([^)]*\)/, '\1')
|
|
516
|
+
|
|
517
|
+
# Remove triple backtick code blocks (preserve content)
|
|
518
|
+
result.gsub!(/```[a-z]*\n?(.*?)```/m, '\1')
|
|
519
|
+
|
|
520
|
+
# Remove inline code backticks (preserve content)
|
|
521
|
+
result.gsub!(/`([^`]*)`/, '\1')
|
|
522
|
+
|
|
523
|
+
# Remove bold formatting (both ** and *)
|
|
524
|
+
result.gsub!(/\*\*([^*]*)\*\*/, '\1')
|
|
525
|
+
result.gsub!(/\*([^*]*)\*/, '\1')
|
|
526
|
+
|
|
527
|
+
# Remove italic formatting (both __ and _)
|
|
528
|
+
result.gsub!(/__([^_]*)__/, '\1')
|
|
529
|
+
result.gsub!(/(?<![\\])_([^_]*)_/, '\1')
|
|
530
|
+
|
|
531
|
+
# Remove strikethrough
|
|
532
|
+
result.gsub!(/~~([^~]*)~~/, '\1')
|
|
533
|
+
result.gsub!(/~([^~]*)~/, '\1')
|
|
534
|
+
|
|
535
|
+
# Remove any remaining unmatched formatting characters at word boundaries
|
|
536
|
+
# but preserve them in the middle of words (like file_name)
|
|
537
|
+
result.gsub!(/(?<=\s)[*_~`]+|[*_~`]+(?=\s|$)/, '')
|
|
538
|
+
|
|
539
|
+
result
|
|
478
540
|
end
|
|
479
541
|
|
|
480
542
|
# Convert HTML to Telegram MarkdownV2 format
|
data/lib/telegrama/version.rb
CHANGED
data/lib/telegrama.rb
CHANGED
|
@@ -40,11 +40,20 @@ module Telegrama
|
|
|
40
40
|
|
|
41
41
|
# Helper method for logging errors
|
|
42
42
|
def log_error(message)
|
|
43
|
-
if defined?(Rails)
|
|
43
|
+
if defined?(Rails) && Rails.respond_to?(:logger)
|
|
44
44
|
Rails.logger.error("[Telegrama] #{message}")
|
|
45
45
|
else
|
|
46
46
|
warn("[Telegrama] #{message}")
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
|
+
|
|
50
|
+
# Helper method for logging info messages
|
|
51
|
+
def log_info(message)
|
|
52
|
+
if defined?(Rails) && Rails.respond_to?(:logger)
|
|
53
|
+
Rails.logger.info("[Telegrama] #{message}")
|
|
54
|
+
else
|
|
55
|
+
puts("[Telegrama] #{message}")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
49
58
|
end
|
|
50
59
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: telegrama
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Javi R
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-05-22 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rails
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 6.0.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ostruct
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.5'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.5'
|
|
26
40
|
description: Send quick, simple admin / logging Telegram messages via a Telegram bot.
|
|
27
41
|
Useful for Rails developers using Telegram messages for notifications, admin alerts,
|
|
28
42
|
daily summaries, and status updates. Parses and escapes Markdown for beautifully
|
|
@@ -34,10 +48,17 @@ executables: []
|
|
|
34
48
|
extensions: []
|
|
35
49
|
extra_rdoc_files: []
|
|
36
50
|
files:
|
|
51
|
+
- ".simplecov"
|
|
52
|
+
- AGENTS.md
|
|
53
|
+
- Appraisals
|
|
37
54
|
- CHANGELOG.md
|
|
55
|
+
- CLAUDE.md
|
|
38
56
|
- LICENSE.txt
|
|
39
57
|
- README.md
|
|
40
58
|
- Rakefile
|
|
59
|
+
- gemfiles/rails_7.2.gemfile
|
|
60
|
+
- gemfiles/rails_8.0.gemfile
|
|
61
|
+
- gemfiles/rails_8.1.gemfile
|
|
41
62
|
- lib/telegrama.rb
|
|
42
63
|
- lib/telegrama/client.rb
|
|
43
64
|
- lib/telegrama/configuration.rb
|