noticed-telegram 0.1.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 +7 -0
- data/CHANGELOG.md +12 -0
- data/MIT-LICENSE +20 -0
- data/README.md +107 -0
- data/SECURITY.md +8 -0
- data/lib/noticed/bulk_delivery_methods/telegram.rb +12 -0
- data/lib/noticed/delivery_methods/telegram.rb +12 -0
- data/lib/noticed/telegram/api.rb +84 -0
- data/lib/noticed/telegram/version.rb +7 -0
- data/lib/noticed/telegram.rb +22 -0
- metadata +79 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 818bb29f28c5721aa2464aa330b78da51bb99015da403fc14d0688b73d2878f2
|
|
4
|
+
data.tar.gz: 5f290a6330badbe90b0747bc36f1b04bd1b6cfedd416ddec38983eb064bc018c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: '084477fff5171b9a82b34d020dacba55745955646240aac56518f7168f256a293000b1b3aeaf8cdcdb71a23981d417d2c4675160fa940d275b93fde93ca5a8aa'
|
|
7
|
+
data.tar.gz: c10396e7c40435ffde0770cef47ad4b94c5327ef01a22c865f9d5ca304d8a8f6ac5a07fc4cb41691a3a015816169672cbeb6f2933356dddd04711540f81d4d65
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-11-14
|
|
6
|
+
|
|
7
|
+
- `deliver_by :telegram` and `bulk_deliver_by :telegram` for Noticed 2.x and 3.x.
|
|
8
|
+
- Supports forum topics (`message_thread_id`), formatting, link previews,
|
|
9
|
+
silent sends, inline keyboards, and any other `sendMessage` field via `json`.
|
|
10
|
+
- A send counts only when Telegram returns 2xx and `"ok": true`; otherwise
|
|
11
|
+
`Noticed::ResponseUnsuccessful` is raised.
|
|
12
|
+
- The bot token is kept out of logs and error messages.
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2026 Yaroslav Shmarov
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# noticed-telegram
|
|
2
|
+
|
|
3
|
+
Telegram delivery methods for [Noticed](https://github.com/excid3/noticed).
|
|
4
|
+
|
|
5
|
+
- `bulk_deliver_by :telegram` sends **one message per event**, to a group, a forum topic, or a channel. Use it for team alerts like "new signup" or "payment failed".
|
|
6
|
+
- `deliver_by :telegram` sends **one message per recipient**, to each user's private chat with your bot.
|
|
7
|
+
|
|
8
|
+
Both call the Bot API's [`sendMessage`](https://core.telegram.org/bots/api#sendmessage). The bot token is part of Telegram's URL, so this gem keeps it out of Noticed's logs and out of `Noticed::ResponseUnsuccessful` messages, which usually end up in your error tracker.
|
|
9
|
+
|
|
10
|
+
Works with Noticed 2.x and 3.x on Rails 7.1+.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# Gemfile
|
|
16
|
+
gem "noticed-telegram"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Nothing else: once the gem is in the bundle, Noticed finds `:telegram`.
|
|
20
|
+
|
|
21
|
+
## Set up a bot
|
|
22
|
+
|
|
23
|
+
1. Message [@BotFather](https://t.me/botfather), send `/newbot`, and copy the token.
|
|
24
|
+
2. Store it in credentials, e.g. `telegram: { bot_token: "..." }`.
|
|
25
|
+
3. Find the chat ID:
|
|
26
|
+
- **Group or forum:** add the bot to the group, send a message there, then open `https://api.telegram.org/bot<TOKEN>/getUpdates` and read `message.chat.id` (it starts with `-100`). In a forum topic, `message.message_thread_id` is the topic ID.
|
|
27
|
+
- **Channel:** add the bot as an admin and use `@channelname` or the numeric `-100…` ID.
|
|
28
|
+
- **A user:** the user must message your bot first; their `chat.id` then appears in `getUpdates` (or in your webhook). Store it on the user.
|
|
29
|
+
|
|
30
|
+
## Team alerts: `bulk_deliver_by :telegram`
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
class NewSignupNotifier < Noticed::Event
|
|
34
|
+
bulk_deliver_by :telegram do |config|
|
|
35
|
+
config.bot_token = Rails.application.credentials.dig(:telegram, :bot_token)
|
|
36
|
+
config.chat_id = "-1001234567890"
|
|
37
|
+
config.message_thread_id = 42 # optional: post into one forum topic
|
|
38
|
+
config.text = -> { "New signup: #{params[:user].email}" }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
NewSignupNotifier.with(user: user).deliver
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Procs run in the context of the event, so `params` and `record` are available.
|
|
46
|
+
|
|
47
|
+
## Per-user messages: `deliver_by :telegram`
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
class CommentNotifier < Noticed::Event
|
|
51
|
+
deliver_by :telegram do |config|
|
|
52
|
+
config.bot_token = Rails.application.credentials.dig(:telegram, :bot_token)
|
|
53
|
+
config.chat_id = -> { recipient.telegram_chat_id }
|
|
54
|
+
config.text = -> { "<b>#{params[:comment].author_name}</b> replied to you" }
|
|
55
|
+
config.parse_mode = "HTML"
|
|
56
|
+
config.if = -> { recipient.telegram_chat_id.present? }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Procs run in the context of the notification, so `recipient` is available too.
|
|
62
|
+
|
|
63
|
+
## Options
|
|
64
|
+
|
|
65
|
+
| Option | Required | |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `bot_token` | yes | From @BotFather. |
|
|
68
|
+
| `chat_id` | yes | Numeric ID or `@channelname`. |
|
|
69
|
+
| `text` | | The message. Telegram rejects a send without text, so set this or put `text` in `json`. |
|
|
70
|
+
| `message_thread_id` | | Forum topic to post into. |
|
|
71
|
+
| `parse_mode` | | `"HTML"`, `"MarkdownV2"`, or `"Markdown"`. Escape user input to match. |
|
|
72
|
+
| `link_preview_options` | | e.g. `{ is_disabled: true }`. |
|
|
73
|
+
| `disable_web_page_preview` | | Older form of the above; Telegram still accepts it. |
|
|
74
|
+
| `disable_notification` | | `true` sends silently. |
|
|
75
|
+
| `protect_content` | | `true` blocks forwarding and saving. |
|
|
76
|
+
| `entities`, `reply_markup` | | Passed through as given. |
|
|
77
|
+
| `json` | | A hash merged over everything above, for any other `sendMessage` field. |
|
|
78
|
+
| `raise_if_not_ok` | | Defaults to `true`. Set `false` to ignore failed sends instead of raising. |
|
|
79
|
+
|
|
80
|
+
Every option accepts a value, a proc, or a symbol naming a method on the notifier, as in the rest of Noticed. Noticed's own `if`, `unless`, `wait`, and `queue` options work as usual.
|
|
81
|
+
|
|
82
|
+
## Failures
|
|
83
|
+
|
|
84
|
+
A send counts as delivered only when Telegram answers with a 2xx status **and** `"ok": true`. Anything else raises `Noticed::ResponseUnsuccessful`, with Telegram's error description in the message and the token shown as `bot[FILTERED]`. Common causes:
|
|
85
|
+
|
|
86
|
+
- `403 Forbidden: bot was blocked by the user`: the user stopped the bot. Clear their stored chat ID.
|
|
87
|
+
- `400 Bad Request: chat not found`: wrong ID, or the user never messaged the bot.
|
|
88
|
+
- `429 Too Many Requests`: Telegram's [rate limits](https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this). Retry with backoff:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
# config/initializers/noticed.rb
|
|
92
|
+
ActiveSupport.on_load(:noticed_delivery_methods_telegram) do
|
|
93
|
+
retry_on Noticed::ResponseUnsuccessful, wait: :polynomially_longer, attempts: 5
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
ActiveSupport.on_load(:noticed_bulk_delivery_methods_telegram) do
|
|
97
|
+
retry_on Noticed::ResponseUnsuccessful, wait: :polynomially_longer, attempts: 5
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Why a separate gem
|
|
102
|
+
|
|
103
|
+
This was [proposed to Noticed](https://github.com/excid3/noticed/pull/568). The maintainer preferred that Telegram support live in its own gem, maintained by someone who uses Telegram.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Security fixes are released for the latest version.
|
|
4
|
+
|
|
5
|
+
Please do not open a public issue for a vulnerability. Use
|
|
6
|
+
[GitHub private vulnerability reporting](https://github.com/yshmarov/noticed-telegram/security/advisories/new)
|
|
7
|
+
with the affected version, a minimal reproduction, and the impact. Never include
|
|
8
|
+
a real bot token.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Noticed
|
|
4
|
+
module BulkDeliveryMethods
|
|
5
|
+
# One message per event, e.g. to a team group chat or a channel.
|
|
6
|
+
class Telegram < BulkDeliveryMethod
|
|
7
|
+
include Noticed::Telegram::Api
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
ActiveSupport.run_load_hooks :noticed_bulk_delivery_methods_telegram, Noticed::BulkDeliveryMethods::Telegram
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Noticed
|
|
4
|
+
module DeliveryMethods
|
|
5
|
+
# One message per recipient, e.g. to each user's private chat with your bot.
|
|
6
|
+
class Telegram < DeliveryMethod
|
|
7
|
+
include Noticed::Telegram::Api
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
ActiveSupport.run_load_hooks :noticed_delivery_methods_telegram, Noticed::DeliveryMethods::Telegram
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
|
|
6
|
+
module Noticed
|
|
7
|
+
module Telegram
|
|
8
|
+
# sendMessage over the Telegram Bot API, shared by the individual and bulk
|
|
9
|
+
# delivery methods.
|
|
10
|
+
#
|
|
11
|
+
# The request is made here rather than with Noticed's `post_request`
|
|
12
|
+
# because the bot token is part of the URL path, and `post_request` puts
|
|
13
|
+
# the full URL into the ResponseUnsuccessful message (and so into error
|
|
14
|
+
# trackers). Every URL this module logs or raises with has the token
|
|
15
|
+
# replaced by [FILTERED].
|
|
16
|
+
module Api
|
|
17
|
+
API_HOST = 'api.telegram.org'
|
|
18
|
+
REDACTED_URL = "https://#{API_HOST}/bot[FILTERED]/sendMessage".freeze
|
|
19
|
+
|
|
20
|
+
PASSTHROUGH_OPTIONS = %i[
|
|
21
|
+
text
|
|
22
|
+
message_thread_id
|
|
23
|
+
parse_mode
|
|
24
|
+
entities
|
|
25
|
+
link_preview_options
|
|
26
|
+
disable_web_page_preview
|
|
27
|
+
disable_notification
|
|
28
|
+
protect_content
|
|
29
|
+
reply_markup
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
def self.included(base)
|
|
33
|
+
base.required_options :bot_token, :chat_id
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def deliver
|
|
37
|
+
payload = json_payload
|
|
38
|
+
response = send_message(payload)
|
|
39
|
+
|
|
40
|
+
raise ResponseUnsuccessful.new(response, REDACTED_URL, { json: payload }) if raise_if_not_ok? && !ok?(response)
|
|
41
|
+
|
|
42
|
+
response
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def json_payload
|
|
46
|
+
payload = { chat_id: evaluate_option(:chat_id) }
|
|
47
|
+
PASSTHROUGH_OPTIONS.each do |option|
|
|
48
|
+
payload[option] = evaluate_option(option) if config.key?(option)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
return payload unless config.key?(:json)
|
|
52
|
+
|
|
53
|
+
custom = evaluate_option(:json)
|
|
54
|
+
custom.is_a?(Hash) ? payload.merge(custom.symbolize_keys) : custom
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def send_message(payload)
|
|
60
|
+
logger.debug("POST #{REDACTED_URL}")
|
|
61
|
+
|
|
62
|
+
response = Net::HTTP.start(API_HOST, 443, use_ssl: true, open_timeout: 5, read_timeout: 15) do |http|
|
|
63
|
+
http.post("/bot#{evaluate_option(:bot_token)}/sendMessage", payload.to_json,
|
|
64
|
+
'Content-Type' => 'application/json')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
logger.debug("Response: #{response.code}")
|
|
68
|
+
response
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Telegram answers {"ok": true, ...} on success and {"ok": false, ...}
|
|
72
|
+
# with a 4xx status on failure, so a send counts only when both agree.
|
|
73
|
+
def ok?(response)
|
|
74
|
+
response.is_a?(Net::HTTPSuccess) && JSON.parse(response.body.to_s)['ok'] == true
|
|
75
|
+
rescue JSON::ParserError
|
|
76
|
+
false
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def raise_if_not_ok?
|
|
80
|
+
config.key?(:raise_if_not_ok) ? evaluate_option(:raise_if_not_ok) : true
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'noticed'
|
|
4
|
+
require 'noticed/telegram/version'
|
|
5
|
+
|
|
6
|
+
# Noticed resolves `deliver_by :telegram` to Noticed::DeliveryMethods::Telegram
|
|
7
|
+
# at delivery time. The classes are autoloaded rather than required because
|
|
8
|
+
# their superclasses inherit from Noticed::ApplicationJob, which only exists
|
|
9
|
+
# once the host app's autoloaders are running.
|
|
10
|
+
module Noticed
|
|
11
|
+
module Telegram
|
|
12
|
+
autoload :Api, 'noticed/telegram/api'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module DeliveryMethods
|
|
16
|
+
autoload :Telegram, 'noticed/delivery_methods/telegram'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module BulkDeliveryMethods
|
|
20
|
+
autoload :Telegram, 'noticed/bulk_delivery_methods/telegram'
|
|
21
|
+
end
|
|
22
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: noticed-telegram
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yaroslav Shmarov
|
|
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: noticed
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '4'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '2.0'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '4'
|
|
32
|
+
description: |
|
|
33
|
+
Send Noticed notifications through the Telegram Bot API: one message per
|
|
34
|
+
recipient with `deliver_by :telegram`, or one message to a group, forum
|
|
35
|
+
topic, or channel with `bulk_deliver_by :telegram`. The bot token is kept
|
|
36
|
+
out of logs and error messages.
|
|
37
|
+
email:
|
|
38
|
+
- yaroslav.shmarov@clickfunnels.com
|
|
39
|
+
executables: []
|
|
40
|
+
extensions: []
|
|
41
|
+
extra_rdoc_files: []
|
|
42
|
+
files:
|
|
43
|
+
- CHANGELOG.md
|
|
44
|
+
- MIT-LICENSE
|
|
45
|
+
- README.md
|
|
46
|
+
- SECURITY.md
|
|
47
|
+
- lib/noticed/bulk_delivery_methods/telegram.rb
|
|
48
|
+
- lib/noticed/delivery_methods/telegram.rb
|
|
49
|
+
- lib/noticed/telegram.rb
|
|
50
|
+
- lib/noticed/telegram/api.rb
|
|
51
|
+
- lib/noticed/telegram/version.rb
|
|
52
|
+
homepage: https://github.com/yshmarov/noticed-telegram
|
|
53
|
+
licenses:
|
|
54
|
+
- MIT
|
|
55
|
+
metadata:
|
|
56
|
+
homepage_uri: https://github.com/yshmarov/noticed-telegram
|
|
57
|
+
source_code_uri: https://github.com/yshmarov/noticed-telegram
|
|
58
|
+
changelog_uri: https://github.com/yshmarov/noticed-telegram/blob/main/CHANGELOG.md
|
|
59
|
+
bug_tracker_uri: https://github.com/yshmarov/noticed-telegram/issues
|
|
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'
|
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubygems_version: 4.0.20
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: 'Telegram delivery methods for Noticed: deliver_by :telegram and bulk_deliver_by
|
|
78
|
+
:telegram.'
|
|
79
|
+
test_files: []
|