telegrama 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: df42e5f329649ab529e46515818ec4ddce70388341455cd383a03b9852c84233
4
+ data.tar.gz: 1c7411979c5d2376aaa0b9307cde1e51f380548b86fa76ba04025f8e8f39b434
5
+ SHA512:
6
+ metadata.gz: e936c11b1532985345a06ee0724110f653dcaa621f2250f31c1ae74d59940471107e6022f3ba0887cc389c958fd33eb4e4a0d7cb2cb26245fb7ce098f8527fb8
7
+ data.tar.gz: 9bcbd009525da8d7cec77effb0cad2791da32beb2806bb321aaeedba73f94f1ffa3f34e89d1ee7e9d7ed29e583682be9c9d7297ed4ffdc974a48c3d1926d431f
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## [0.1.1] - 2025-02-18
2
+
3
+ - Rebranded `telegrams` to `telegrama`
4
+
5
+ ## [0.1.0] - 2025-02-18
6
+
7
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Javi R
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # 💬 `telegrama` – a tiny wrapper to send admin Telegram messages
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/telegrama.svg?v=0.1.0)](https://badge.fury.io/rb/telegrama?v=0.1.0)
4
+
5
+ Send quick, simple admin / logging Telegram messages via a Telegram bot.
6
+
7
+ I'm making this gem because I'm tired of copy-pasting the same Telegram wrapper from Rails project to Rails project just to send myself admin messages and notifications. The goal with this gem is to provide a straightforward, minimal API to send Telegram messages reliably. All I want to do is this:
8
+
9
+ ```ruby
10
+ Telegrama.send_message("Important admin notification!")
11
+ ```
12
+
13
+ This is useful for Rails developers using Telegram messages for notifications, admin alerts, errors, logs, daily summaries, and status updates.
14
+
15
+ ## Quick start
16
+
17
+ Add telegrama to your Gemfile:
18
+
19
+ ```ruby
20
+ gem 'telegrama'
21
+ ```
22
+
23
+ Then run:
24
+
25
+ ```bash
26
+ bundle install
27
+ ```
28
+
29
+ Then, create an initializer file under `config/initializers/telegrama.rb` and set your credentials:
30
+
31
+ ```ruby
32
+ Telegrama.configure do |config|
33
+ config.bot_token = Rails.application.credentials.dig(Rails.env.to_sym, :telegram, :bot_token)
34
+ config.chat_id = Rails.application.credentials.dig(Rails.env.to_sym, :telegram, :chat_id)
35
+ config.default_parse_mode = 'MarkdownV2'
36
+
37
+ # Default formatting options
38
+ config.formatting_options = {
39
+ escape_markdown: true, # Escape markdown special characters
40
+ obfuscate_emails: false, # Off by default, enable if needed (it anonymizes email addresses in the message to things like abc...d@gmail.com)
41
+ escape_html: false, # Optionally escape HTML characters
42
+ truncate: 4096 # Truncate if message exceeds Telegram's limit (or a custom limit)
43
+ }
44
+
45
+ config.deliver_message_async = false # Enable async message delivery with ActiveJob (enqueue the send_message call to offload message sending from the request cycle)
46
+ config.deliver_message_queue = 'default' # Use a custom ActiveJob queue
47
+ end
48
+ ```
49
+
50
+ Done!
51
+
52
+ You can now send Telegram messages using your bot:
53
+
54
+ ```ruby
55
+ Telegrama.send_message("Hey, this is your Rails app speaking via Telegram!")
56
+ ```
57
+
58
+ ## Advanced options
59
+
60
+ ### Obfuscate emails in the message
61
+
62
+ Sometimes you want to report user actions including a sufficiently identifiable but otherwise anonymous user email. For example, when someone makes gets a refund, you may want to send a message like `john.doe21@email.com got refunded $XX.XX` – but there may be other people / employees in the group chat, so instead of leaking personal, private information, just turn on the `obfuscate_emails` option and the message will automatically get formatted as: `joh...1@email.com got refunded $XX.XX`
63
+
64
+ ### Overriding defaults with options
65
+
66
+ You can pass an options hash to `Telegrama.send_message` to override default behavior on a per‑message basis:
67
+
68
+ - **`chat_id`**
69
+ *Override the default chat ID set in your configuration.*
70
+ **Usage Example:**
71
+ ```ruby
72
+ Telegrama.send_message("Hello, alternate group!", chat_id: alternate_chat_id)
73
+ ```
74
+
75
+ - **`parse_mode`**
76
+ *Override the default parse mode (default is `"MarkdownV2"`).*
77
+ **Usage Example:**
78
+ ```ruby
79
+ Telegrama.send_message("Hello, world!", parse_mode: "HTML")
80
+ ```
81
+
82
+ - **`disable_web_page_preview`**
83
+ *Enable or disable web page previews (default is `true`).*
84
+ **Usage Example:**
85
+ ```ruby
86
+ Telegrama.send_message("Check out this link: https://example.com", disable_web_page_preview: false)
87
+ ```
88
+
89
+ - **`formatting`**
90
+ *A hash that overrides the default formatting options provided in the configuration. Available keys include:*
91
+ - `escape_markdown` (Boolean): Automatically escape Telegram Markdown special characters.
92
+ - `obfuscate_emails` (Boolean): Obfuscate email addresses found in the message.
93
+ - `escape_html` (Boolean): Escape HTML entities.
94
+ - `truncate` (Integer): Maximum allowed message length (default is `4096`).
95
+
96
+ **Usage Example:**
97
+ ```ruby
98
+ Telegrama.send_message("Contact: john.doe@example.com", formatting: { obfuscate_emails: true })
99
+ ```
100
+
101
+ ### Asynchronous message delivery
102
+
103
+ For production environments or high-traffic applications, you might want to offload message delivery to a background job. Our gem supports asynchronous delivery via ActiveJob.
104
+
105
+ With `deliver_message_async` setting enabled, calling:
106
+ ```ruby
107
+ Telegrama.send_message("Hello asynchronously!")
108
+ ```
109
+
110
+ will enqueue a job on the specified queue (`deliver_message_queue`) rather than sending the message immediately.
111
+
112
+ ## Development
113
+
114
+ 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.
115
+
116
+ To install this gem onto your local machine, run `bundle exec rake install`.
117
+
118
+ ## Contributing
119
+
120
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rameerez/telegrama. Our code of conduct is: just be nice and make your mom proud of what you do and post online.
121
+
122
+ ## License
123
+
124
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -0,0 +1,49 @@
1
+ module Telegrama
2
+ class Client
3
+ def send_message(message, options = {})
4
+ # Allow chat ID override; fallback to config default
5
+ chat_id = options.delete(:chat_id) || Telegrama.configuration.chat_id
6
+
7
+ # Allow runtime formatting options, merging with configured defaults
8
+ formatting_opts = options.delete(:formatting) || {}
9
+ formatted_message = Formatter.format(message, formatting_opts)
10
+
11
+ payload = {
12
+ chat_id: chat_id,
13
+ text: formatted_message,
14
+ parse_mode: options[:parse_mode] || Telegrama.configuration.default_parse_mode,
15
+ disable_web_page_preview: options.fetch(:disable_web_page_preview, true)
16
+ }
17
+
18
+ perform_request(payload)
19
+ end
20
+
21
+ private
22
+
23
+ def perform_request(payload)
24
+ uri = URI("https://api.telegram.org/bot#{Telegrama.configuration.bot_token}/sendMessage")
25
+ request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
26
+ request.body = payload.to_json
27
+
28
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
29
+ http.request(request)
30
+ end
31
+
32
+ unless response.is_a?(Net::HTTPSuccess)
33
+ error_info = JSON.parse(response.body) rescue {}
34
+ error_description = error_info["description"] || response.body
35
+ logger.error("Telegrama API error for chat_id #{payload[:chat_id]}: #{error_description}")
36
+ raise Error, "Telegram API error for chat_id #{payload[:chat_id]}: #{error_description}"
37
+ end
38
+
39
+ response
40
+ rescue StandardError => e
41
+ logger.error("Failed to send Telegram message: #{e.message}")
42
+ raise Error, "Failed to send Telegram message: #{e.message}"
43
+ end
44
+
45
+ def logger
46
+ defined?(Rails) && Rails.respond_to?(:logger) ? Rails.logger : Logger.new($stdout)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Telegrama
4
+ class Configuration
5
+
6
+ # Your Telegram Bot API token
7
+ attr_accessor :bot_token
8
+
9
+ # Default chat ID for sending messages.
10
+ # You can override this on the fly when sending messages.
11
+ attr_accessor :chat_id
12
+
13
+ # Default parse mode for messages (e.g. "MarkdownV2" or "HTML").
14
+ attr_accessor :default_parse_mode
15
+
16
+ # Whether to disable web page previews by default.
17
+ attr_accessor :disable_web_page_preview
18
+
19
+ # =========================================
20
+ # Formatting Options
21
+ # =========================================
22
+
23
+ # Formatting options used by the Formatter module.
24
+ # Available keys:
25
+ # :escape_markdown (Boolean) - Escape Telegram markdown special characters.
26
+ # :obfuscate_emails (Boolean) - Obfuscate email addresses found in messages.
27
+ # :escape_html (Boolean) - Escape HTML entities (<, >, &).
28
+ # :truncate (Integer) - Maximum allowed message length.
29
+ attr_accessor :formatting_options
30
+
31
+ # Whether to deliver messages asynchronously via ActiveJob.
32
+ # Defaults to false
33
+ attr_accessor :deliver_message_async
34
+
35
+ # The ActiveJob queue name to use when enqueuing messages.
36
+ # Defaults to 'default'
37
+ attr_accessor :deliver_message_queue
38
+
39
+ def initialize
40
+ # Credentials (must be set via initializer)
41
+ @bot_token = nil
42
+ @chat_id = nil
43
+
44
+ # Defaults for message formatting
45
+ @default_parse_mode = 'MarkdownV2'
46
+ @disable_web_page_preview = true
47
+
48
+ # Sensible defaults for formatting options.
49
+ @formatting_options = {
50
+ escape_markdown: true,
51
+ obfuscate_emails: false,
52
+ escape_html: false,
53
+ truncate: 4096
54
+ }
55
+
56
+ @deliver_message_async = false
57
+ @deliver_message_queue = 'default'
58
+ end
59
+
60
+ # Validate the configuration.
61
+ # Raise descriptive errors if required settings are missing or invalid.
62
+ def validate!
63
+ validate_bot_token!
64
+ validate_default_parse_mode!
65
+ validate_formatting_options!
66
+ true
67
+ end
68
+
69
+ private
70
+
71
+ def validate_bot_token!
72
+ if bot_token.nil? || bot_token.strip.empty?
73
+ raise ArgumentError, "Telegrama configuration error: bot_token cannot be blank."
74
+ end
75
+ end
76
+
77
+ def validate_default_parse_mode!
78
+ allowed_modes = ['MarkdownV2', 'HTML', nil]
79
+ unless allowed_modes.include?(default_parse_mode)
80
+ raise ArgumentError, "Telegrama configuration error: default_parse_mode must be one of #{allowed_modes.inspect}."
81
+ end
82
+ end
83
+
84
+ def validate_formatting_options!
85
+ unless formatting_options.is_a?(Hash)
86
+ raise ArgumentError, "Telegrama configuration error: formatting_options must be a hash."
87
+ end
88
+
89
+ %i[escape_markdown obfuscate_emails escape_html].each do |key|
90
+ if formatting_options.key?(key) && ![true, false].include?(formatting_options[key])
91
+ raise ArgumentError, "Telegrama configuration error: formatting_options[:#{key}] must be true or false."
92
+ end
93
+ end
94
+
95
+ if formatting_options.key?(:truncate)
96
+ truncate_val = formatting_options[:truncate]
97
+ unless truncate_val.is_a?(Integer) && truncate_val.positive?
98
+ raise ArgumentError, "Telegrama configuration error: formatting_options[:truncate] must be a positive integer."
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,3 @@
1
+ module Telegrama
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,70 @@
1
+ module Telegrama
2
+ module Formatter
3
+ MARKDOWN_SPECIAL_CHARS = %w[_ * [ ] ( ) ~ ` > # + - = | { } . !].freeze
4
+ # Characters that should always be escaped in Telegram messages, even when Markdown is enabled
5
+ ALWAYS_ESCAPE_CHARS = %w[. !].freeze
6
+ # Characters used for Markdown formatting that need special handling
7
+ MARKDOWN_FORMAT_CHARS = %w[* _].freeze
8
+
9
+ def self.format(text, options = {})
10
+ # Merge defaults with any runtime overrides
11
+ defaults = Telegrama.configuration.formatting_options || {}
12
+ opts = defaults.merge(options)
13
+
14
+ text = text.to_s
15
+ text = obfuscate_emails(text) if opts[:obfuscate_emails]
16
+ text = escape_html(text) if opts[:escape_html]
17
+ if opts[:escape_markdown]
18
+ text = escape_markdown(text)
19
+ else
20
+ # When Markdown is enabled (escape_markdown: false), we still need to escape some special characters
21
+ text = escape_special_chars(text)
22
+ end
23
+ text = truncate(text, opts[:truncate]) if opts[:truncate]
24
+ text
25
+ end
26
+
27
+ def self.escape_markdown(text)
28
+ MARKDOWN_SPECIAL_CHARS.each do |char|
29
+ text = text.gsub(/(?<!\\)#{Regexp.escape(char)}/, "\\#{char}")
30
+ end
31
+ text
32
+ end
33
+
34
+ def self.escape_special_chars(text)
35
+ # First escape non-formatting special characters
36
+ ALWAYS_ESCAPE_CHARS.each do |char|
37
+ text = text.gsub(/(?<!\\)#{Regexp.escape(char)}/, "\\#{char}")
38
+ end
39
+
40
+ # Then handle formatting characters (* and _) by only escaping them when they're not paired
41
+ MARKDOWN_FORMAT_CHARS.each do |char|
42
+ # Count unescaped occurrences
43
+ count = text.scan(/(?<!\\)#{Regexp.escape(char)}/).count
44
+
45
+ if count.odd?
46
+ # If we have an odd count, escape all occurrences that aren't already escaped
47
+ text = text.gsub(/(?<!\\)#{Regexp.escape(char)}/, "\\#{char}")
48
+ end
49
+ end
50
+
51
+ text
52
+ end
53
+
54
+ def self.obfuscate_emails(text)
55
+ text.gsub(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/) do |email|
56
+ local, domain = email.split('@')
57
+ obfuscated_local = local.length > 4 ? "#{local[0..2]}...#{local[-1]}" : "#{local[0]}..."
58
+ "#{obfuscated_local}@#{domain}"
59
+ end
60
+ end
61
+
62
+ def self.escape_html(text)
63
+ text.gsub(/[<>&]/, '<' => '&lt;', '>' => '&gt;', '&' => '&amp;')
64
+ end
65
+
66
+ def self.truncate(text, max_length)
67
+ text.length > max_length ? text[0, max_length] : text
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,9 @@
1
+ module Telegrama
2
+ class SendMessageJob < ActiveJob::Base
3
+ # No default queue provided here -- it's passed from the job call instead
4
+
5
+ def perform(message, options = {})
6
+ Client.new.send_message(message, options)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Telegrama
4
+ VERSION = "0.1.1"
5
+ end
data/lib/telegrama.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Require standard libraries that our gem depends on
4
+ require "net/http"
5
+ require "uri"
6
+ require "json"
7
+ require "cgi"
8
+
9
+ # Require our gem files
10
+ require_relative "telegrama/error"
11
+ require_relative "telegrama/version"
12
+ require_relative "telegrama/configuration"
13
+ require_relative "telegrama/formatter"
14
+ require_relative "telegrama/client"
15
+ require_relative "telegrama/send_message_job"
16
+
17
+ module Telegrama
18
+ class << self
19
+ # Returns the configuration object.
20
+ def configuration
21
+ @configuration ||= Configuration.new
22
+ end
23
+
24
+ def configure
25
+ yield(configuration)
26
+ configuration.validate!
27
+ end
28
+
29
+ # Sends a message using the configured settings.
30
+ # Before sending, we validate the configuration.
31
+ # This way, if nothing’s been set up, we get a descriptive error instead of a low-level one.
32
+ def send_message(message, options = {})
33
+ configuration.validate!
34
+ if configuration.deliver_message_async
35
+ SendMessageJob.set(queue: configuration.deliver_message_queue).perform_later(message, options)
36
+ else
37
+ Client.new.send_message(message, options)
38
+ end
39
+ end
40
+
41
+ end
42
+ end
data/sig/telegrams.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Telegrama
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: telegrama
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Javi R
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-02-18 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Send quick, simple admin / logging Telegram messages via a Telegram bot.
13
+ Useful for Rails developers using Telegram messages for notifications, admin alerts,
14
+ daily summaries, and status updates. Integrates with the Telegram Bot API.
15
+ email:
16
+ - rubygems@rameerez.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - CHANGELOG.md
22
+ - LICENSE.txt
23
+ - README.md
24
+ - Rakefile
25
+ - lib/telegrama.rb
26
+ - lib/telegrama/client.rb
27
+ - lib/telegrama/configuration.rb
28
+ - lib/telegrama/error.rb
29
+ - lib/telegrama/formatter.rb
30
+ - lib/telegrama/send_message_job.rb
31
+ - lib/telegrama/version.rb
32
+ - sig/telegrams.rbs
33
+ homepage: https://github.com/rameerez/telegrama
34
+ licenses:
35
+ - MIT
36
+ metadata:
37
+ allowed_push_host: https://rubygems.org
38
+ homepage_uri: https://github.com/rameerez/telegrama
39
+ source_code_uri: https://github.com/rameerez/telegrama
40
+ changelog_uri: https://github.com/rameerez/telegrama/blob/main/CHANGELOG.md
41
+ rubygems_mfa_required: 'true'
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 3.1.0
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubygems_version: 3.6.2
57
+ specification_version: 4
58
+ summary: A tiny wrapper to send Telegram admin messages via the Telegram Bot API.
59
+ test_files: []