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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a091a95e5e3a04865deef8b71ecf50da3f947026c0a1ad411d7d5827a5b6cc34
4
- data.tar.gz: 2029971f6180699d8efba99814ed5305103f23a52507f2b0be3a452c0e8f0f9a
3
+ metadata.gz: 3ae6c6b2bb6c3da545107d0f91b63e490fbd6e7e5a6e6be633291346c9f6ebcb
4
+ data.tar.gz: 42b7590b77f6497a8b972a97a1bb8cd371f89bf5db322bcd779b7d0a7ae9a331
5
5
  SHA512:
6
- metadata.gz: '048dee76d2c78e1813abf08a19524388983edb40fee8a0f53f274d92aa37e8f032e0149cf3b0628edb24bc8e94a4d0428f96565570c2ac5ff30db52b7944ca6d'
7
- data.tar.gz: 1703bd66425bb79171a64626da8d08bad982f0d1536846c9bc2b215bd5e06e6c3086fe0366003ee1f5c5319c32e5e69fe22e368025eb99f776e43ed24936456d
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` – a tiny wrapper to send admin Telegram messages
1
+ # 💬 `telegrama` – Send Telegram admin notifications in your Rails app
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/telegrama.svg?v=0.1.1)](https://badge.fury.io/rb/telegrama?v=0.1.1)
3
+ [![Gem Version](https://badge.fury.io/rb/telegrama.svg)](https://badge.fury.io/rb/telegrama) [![Build Status](https://github.com/rameerez/telegrama/workflows/Tests/badge.svg)](https://github.com/rameerez/telegrama/actions)
4
4
 
5
- Send quick, simple admin / logging Telegram messages via a Telegram bot.
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
- ```ruby
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 comprehensive test suite.
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: "../"
@@ -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
- parse_mode = options[:parse_mode] || Telegrama.configuration.default_parse_mode
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
- formatting_opts = options.delete(:formatting) || {}
28
-
29
- # Add parse mode specific options
30
- if parse_mode == 'MarkdownV2'
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
- [:timeout, :retry_count, :retry_delay].each do |key|
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
@@ -179,8 +179,12 @@ module Telegrama
179
179
  @result += '*'
180
180
  advance
181
181
  elsif char == '_' && !escaped?
182
- enter_state(:italic)
183
- @result += '_'
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
- exit_state
260
- @result += '_'
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
- # Remove all markdown syntax for plain text delivery
477
- text.gsub(/[*_~`]|\[.*?\]\(.*?\)/, '')
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Telegrama
4
- VERSION = "0.1.3"
4
+ VERSION = "0.3.0"
5
5
  end
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.1.3
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: 2025-02-28 00:00:00.000000000 Z
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