telegrams 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -1
- data/README.md +2 -123
- data/lib/telegrams/version.rb +1 -1
- data/lib/telegrams.rb +2 -36
- metadata +18 -11
- data/lib/telegrams/client.rb +0 -49
- data/lib/telegrams/configuration.rb +0 -103
- data/lib/telegrams/error.rb +0 -3
- data/lib/telegrams/formatter.rb +0 -70
- data/lib/telegrams/send_message_job.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07f64a28ab74c341a147501d87ca332f32d7f7b17ae43525248a8b0761013ff5
|
4
|
+
data.tar.gz: 46362b2dab6ae4609988360dd7afbd2a98ce910cf360db1205f5318b3fe4316e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 235e0542ccb49d34b4751118b2f809a4c4dabf641e68b11a5f9f20641cd51122aa9446811f11de6b6d943edcae4bc426e3e6b4532fdd8a0857112514f14dc69c
|
7
|
+
data.tar.gz: 29f8aa12a72cd6d64fc8b2620b228ffdcefd688867bb389015a9781e30a68ca280c79b111cf48c7b75b6ccde3e726df8a9184b3cce35bca5b19fd9d1ea44f523
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,124 +1,3 @@
|
|
1
|
-
#
|
1
|
+
# You're probably looking for the `telegrama` gem
|
2
2
|
|
3
|
-
[
|
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
|
-
Telegrams.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 telegrams to your Gemfile:
|
18
|
-
|
19
|
-
```ruby
|
20
|
-
gem 'telegrams'
|
21
|
-
```
|
22
|
-
|
23
|
-
Then run:
|
24
|
-
|
25
|
-
```bash
|
26
|
-
bundle install
|
27
|
-
```
|
28
|
-
|
29
|
-
Then, create an initializer file under `config/initializers/telegrams.rb` and set your credentials:
|
30
|
-
|
31
|
-
```ruby
|
32
|
-
Telegrams.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
|
-
Telegrams.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 `Telegrams.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
|
-
Telegrams.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
|
-
Telegrams.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
|
-
Telegrams.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
|
-
Telegrams.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
|
-
Telegrams.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/telegrams. 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).
|
3
|
+
Head to [`telegrama`](https://github.com/rameerez/telegrama)
|
data/lib/telegrams/version.rb
CHANGED
data/lib/telegrams.rb
CHANGED
@@ -1,42 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
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 "telegrams/error"
|
11
3
|
require_relative "telegrams/version"
|
12
|
-
require_relative "telegrams/configuration"
|
13
|
-
require_relative "telegrams/formatter"
|
14
|
-
require_relative "telegrams/client"
|
15
|
-
require_relative "telegrams/send_message_job"
|
16
4
|
|
17
5
|
module Telegrams
|
18
|
-
class
|
19
|
-
|
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
|
6
|
+
class Error < StandardError; end
|
7
|
+
# Your code goes here...
|
42
8
|
end
|
metadata
CHANGED
@@ -1,17 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: telegrams
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Javi R
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
10
|
date: 2025-02-18 00:00:00.000000000 Z
|
11
|
-
dependencies:
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: telegrama
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 0.1.1
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.1.1
|
26
|
+
description: You're probably looking for the telegrama gem instead
|
15
27
|
email:
|
16
28
|
- rubygems@rameerez.com
|
17
29
|
executables: []
|
@@ -23,11 +35,6 @@ files:
|
|
23
35
|
- README.md
|
24
36
|
- Rakefile
|
25
37
|
- lib/telegrams.rb
|
26
|
-
- lib/telegrams/client.rb
|
27
|
-
- lib/telegrams/configuration.rb
|
28
|
-
- lib/telegrams/error.rb
|
29
|
-
- lib/telegrams/formatter.rb
|
30
|
-
- lib/telegrams/send_message_job.rb
|
31
38
|
- lib/telegrams/version.rb
|
32
39
|
- sig/telegrams.rbs
|
33
40
|
homepage: https://github.com/rameerez/telegrams
|
@@ -55,5 +62,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
62
|
requirements: []
|
56
63
|
rubygems_version: 3.6.2
|
57
64
|
specification_version: 4
|
58
|
-
summary:
|
65
|
+
summary: You're probably looking for the telegrama gem instead
|
59
66
|
test_files: []
|
data/lib/telegrams/client.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
module Telegrams
|
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) || Telegrams.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] || Telegrams.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#{Telegrams.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("Telegrams 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
|
@@ -1,103 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Telegrams
|
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, "Telegrams 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, "Telegrams 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, "Telegrams 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, "Telegrams 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, "Telegrams configuration error: formatting_options[:truncate] must be a positive integer."
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
data/lib/telegrams/error.rb
DELETED
data/lib/telegrams/formatter.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
module Telegrams
|
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 = Telegrams.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(/[<>&]/, '<' => '<', '>' => '>', '&' => '&')
|
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
|