telepost 0.8.1 → 0.10.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.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +29 -35
  3. data/README.md +16 -4
  4. data/lib/telepost.rb +73 -13
  5. data/telepost.gemspec +1 -1
  6. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72e3854ed2c2ef04727919095d396357f7cd91641387825a039d28cafdf819cf
4
- data.tar.gz: 4ef6fe085695c41b615dbc31ce0c885c966ecd357e361625e91d74aad61a07a4
3
+ metadata.gz: bb2f082807cb2bf827333ccbaf2dbe286b46edd1ddd8518df9628c7507822dc3
4
+ data.tar.gz: bcd93a98a219b978a7673c49746b54dd8240bbbfa51c2a1749fd17d129a96455
5
5
  SHA512:
6
- metadata.gz: 29ef8828ecfb355777daffb9a8a69fffd59b3393357bd60bc9b5012a9be6062c67ffd0660cd6d4bbd5c9a5effec7ef4a4c1bafbed5be7801b4494b935c1a3db2
7
- data.tar.gz: 9e5aab74dccdca37292a2eb5959c2b24396cddfdd6189de47445ad0e48194efee77dbc3a7728303a70d1860f8f80984a9809794cdfecdf0440b5fb929d6effae
6
+ metadata.gz: 6a96040340518bec226b2ff447ac0d784e23785050de3c518c84369f64e265eba8225459d04405de64e2b877529d594f04597f8617689af6331f6d76f56c1d14
7
+ data.tar.gz: a1278e354d65fe8fbc9ae8adcea25ae851e6f10223ccfd735de47b8d39331982191b01b6abf76e79d593f235423d52f591715c3d28b828b78e46f243b3d53de0
data/Gemfile.lock CHANGED
@@ -11,25 +11,25 @@ GEM
11
11
  public_suffix (>= 2.0.2, < 8.0)
12
12
  ansi (1.6.0)
13
13
  ast (2.4.3)
14
- bigdecimal (4.1.0)
14
+ bigdecimal (4.1.2)
15
15
  builder (3.3.0)
16
- concurrent-ruby (1.3.5)
16
+ concurrent-ruby (1.3.7)
17
17
  crack (1.0.1)
18
18
  bigdecimal
19
19
  rexml
20
20
  docile (1.4.1)
21
21
  drb (2.2.3)
22
- dry-core (1.1.0)
22
+ dry-core (1.2.0)
23
23
  concurrent-ruby (~> 1.0)
24
24
  logger
25
25
  zeitwerk (~> 2.6)
26
- dry-inflector (1.2.0)
26
+ dry-inflector (1.3.1)
27
27
  dry-logic (1.6.0)
28
28
  bigdecimal
29
29
  concurrent-ruby (~> 1.0)
30
30
  dry-core (~> 1.1)
31
31
  zeitwerk (~> 2.6)
32
- dry-struct (1.8.0)
32
+ dry-struct (1.8.1)
33
33
  dry-core (~> 1.1)
34
34
  dry-types (~> 1.8, >= 1.8.2)
35
35
  ice_nine (~> 0.11)
@@ -41,26 +41,21 @@ GEM
41
41
  dry-inflector (~> 1.0)
42
42
  dry-logic (~> 1.4)
43
43
  zeitwerk (~> 2.6)
44
- faraday (2.13.2)
44
+ faraday (2.14.3)
45
45
  faraday-net_http (>= 2.0, < 3.5)
46
46
  json
47
47
  logger
48
- faraday-multipart (1.1.1)
48
+ faraday-multipart (1.2.0)
49
49
  multipart-post (~> 2.0)
50
- faraday-net_http (3.4.1)
51
- net-http (>= 0.5.0)
50
+ faraday-net_http (3.4.4)
51
+ net-http (~> 0.5)
52
52
  hashdiff (1.2.1)
53
53
  ice_nine (0.11.2)
54
- json (2.19.2)
55
- json-schema (6.2.0)
56
- addressable (~> 2.8)
57
- bigdecimal (>= 3.1, < 5)
58
- language_server-protocol (3.17.0.5)
54
+ json (2.21.2)
55
+ language_server-protocol (3.17.0.6)
59
56
  lint_roller (1.1.0)
60
57
  logger (1.7.0)
61
- mcp (0.9.2)
62
- json-schema (>= 4.1)
63
- minitest (6.0.5)
58
+ minitest (6.0.6)
64
59
  drb (~> 2.0)
65
60
  prism (~> 1.5)
66
61
  minitest-reporters (1.8.0)
@@ -69,10 +64,10 @@ GEM
69
64
  minitest (>= 5.0, < 7)
70
65
  ruby-progressbar
71
66
  multipart-post (2.4.1)
72
- net-http (0.6.0)
73
- uri
74
- parallel (1.27.0)
75
- parser (3.3.10.2)
67
+ net-http (0.9.1)
68
+ uri (>= 0.11.1)
69
+ parallel (2.1.0)
70
+ parser (3.3.12.0)
76
71
  ast (~> 2.4.1)
77
72
  racc
78
73
  prism (1.9.0)
@@ -80,33 +75,32 @@ GEM
80
75
  racc (1.8.1)
81
76
  rainbow (3.1.1)
82
77
  rake (13.4.2)
83
- regexp_parser (2.11.3)
78
+ regexp_parser (2.12.0)
84
79
  rexml (3.4.4)
85
- rubocop (1.85.1)
86
- json (~> 2.3)
80
+ rubocop (1.90.0)
81
+ json (>= 2.3)
87
82
  language_server-protocol (~> 3.17.0.2)
88
83
  lint_roller (~> 1.1.0)
89
- mcp (~> 0.6)
90
- parallel (~> 1.10)
84
+ parallel (>= 1.10)
91
85
  parser (>= 3.3.0.2)
92
86
  rainbow (>= 2.2.2, < 4.0)
93
87
  regexp_parser (>= 2.9.3, < 3.0)
94
88
  rubocop-ast (>= 1.49.0, < 2.0)
95
89
  ruby-progressbar (~> 1.7)
96
90
  unicode-display_width (>= 2.4.0, < 4.0)
97
- rubocop-ast (1.49.1)
91
+ rubocop-ast (1.50.0)
98
92
  parser (>= 3.3.7.2)
99
93
  prism (~> 1.7)
100
- rubocop-elegant (0.0.20)
94
+ rubocop-elegant (0.5.1)
101
95
  lint_roller (~> 1.1)
102
96
  rubocop (~> 1.75)
103
- rubocop-minitest (0.39.1)
97
+ rubocop-minitest (0.40.0)
104
98
  lint_roller (~> 1.1)
105
99
  rubocop (>= 1.75.0, < 2.0)
106
100
  rubocop-ast (>= 1.38.0, < 2.0)
107
- rubocop-performance (1.26.1)
101
+ rubocop-performance (1.27.0)
108
102
  lint_roller (~> 1.1)
109
- rubocop (>= 1.75.0, < 2.0)
103
+ rubocop (>= 1.89.0, < 2.0)
110
104
  rubocop-ast (>= 1.47.1, < 2.0)
111
105
  rubocop-rake (0.7.1)
112
106
  lint_roller (~> 1.1)
@@ -129,13 +123,13 @@ GEM
129
123
  unicode-display_width (3.2.0)
130
124
  unicode-emoji (~> 4.1)
131
125
  unicode-emoji (4.2.0)
132
- uri (1.0.4)
133
- webmock (3.26.2)
126
+ uri (1.1.1)
127
+ webmock (3.26.3)
134
128
  addressable (>= 2.8.0)
135
129
  crack (>= 0.3.2)
136
130
  hashdiff (>= 0.4.0, < 2.0.0)
137
- yard (0.9.43)
138
- zeitwerk (2.7.3)
131
+ yard (0.9.45)
132
+ zeitwerk (2.8.2)
139
133
 
140
134
  PLATFORMS
141
135
  arm64-darwin-22
data/README.md CHANGED
@@ -37,7 +37,7 @@ tp.post(12345, 'How are you?', 'How are you doing?')
37
37
  All lines you provide to the `post()` method will be concatenated
38
38
  with a space between them.
39
39
 
40
- Or you can pre-configure it to talk to certain list of chats.
40
+ Or you can pre-configure it to talk to a certain list of chats.
41
41
  Your bot has to be an admin of the channel, in order to post there.
42
42
  Here is how you "spam":
43
43
 
@@ -46,13 +46,25 @@ tp = Telepost.new('..token..', chats: [12345])
46
46
  tp.spam('How are you?')
47
47
  ```
48
48
 
49
- You can also attach a file (sent as a Telegram document). The
50
- `file` argument accepts a path or an already-open `IO`/`File`,
51
- and `caption:` is optional:
49
+ You can also attach a file. The `file` argument accepts a path or
50
+ an already-open `IO`/`File`, and `caption:` is optional. A file with
51
+ an image extension goes out as a photo, which Telegram shows inline,
52
+ while everything else goes out as a document:
52
53
 
53
54
  ```ruby
54
55
  tp = Telepost.new('..token..')
55
56
  tp.attach(12345, '/tmp/dump.sql', caption: 'Today is dump')
57
+ tp.attach(12345, '/tmp/chart.png', caption: 'Today is chart')
58
+ ```
59
+
60
+ You can also pass an array of files, to post them all as a single
61
+ grouped message (a Telegram "album"). Files with an image extension
62
+ are sent as photos, the rest as documents, and the `caption:` is
63
+ shown once, on the first item:
64
+
65
+ ```ruby
66
+ tp = Telepost.new('..token..')
67
+ tp.attach(12345, ['/tmp/a.png', '/tmp/b.png'], caption: 'Two shots')
56
68
  ```
57
69
 
58
70
  That's it.
data/lib/telepost.rb CHANGED
@@ -42,9 +42,22 @@ class Telepost
42
42
  end
43
43
 
44
44
  def attach(chat, file, caption: nil)
45
- path = file.is_a?(String) ? file : file.path
46
- tail = caption.nil? ? '' : " caption=#{caption}"
47
- @sent << "#{chat}: [attach:#{File.basename(path)}#{tail}]"
45
+ @sent <<
46
+ if file.is_a?(Array)
47
+ "#{chat}: [attach-group:#{names(file)}#{cap(caption)}]"
48
+ else
49
+ "#{chat}: [attach:#{names([file])}#{cap(caption)}]"
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def names(files)
56
+ files.map { |f| File.basename(f.is_a?(String) ? f : f.path) }.join(',')
57
+ end
58
+
59
+ def cap(caption)
60
+ caption.nil? ? '' : " caption=#{caption}"
48
61
  end
49
62
  end
50
63
 
@@ -108,22 +121,69 @@ class Telepost
108
121
  @bot.api.send_message(chat_id: chat, parse_mode:, disable_web_page_preview: true, text: lines.join(' '))
109
122
  end
110
123
 
111
- # Attach a file (as a Telegram document) to the chat. The file
112
- # argument can either be a path (String) or an open IO/File. The
113
- # filename shown in Telegram comes from the basename of the path.
124
+ # Attach a file to the chat. The file argument can either be a
125
+ # path (String) or an open IO/File. The filename shown in Telegram
126
+ # comes from the basename of the path. A file with an image
127
+ # extension goes out as a photo via +sendPhoto+, so Telegram shows
128
+ # it inline, and everything else as a document via +sendDocument+.
129
+ #
130
+ # When +file+ is an +Array+, all of its items are posted as a single
131
+ # grouped message (a Telegram "album") via +sendMediaGroup+. Items
132
+ # with an image extension are wrapped in +InputMediaPhoto+, the rest
133
+ # in +InputMediaDocument+. The +caption+ is attached to the first
134
+ # item only, so the album shows one caption.
114
135
  #
115
136
  # @param chat [Integer, String] Chat ID or channel name
116
- # @param file [String, File, IO] Path to the file or an open IO
137
+ # @param file [String, File, IO, Array] File (or array of files) to attach
117
138
  # @param caption [String, nil] Optional caption for the attachment
118
139
  # @param parse_mode [String] Parse mode used for the caption
119
140
  # @return [Telegram::Bot::Types::Message] The sent message object
120
141
  def attach(chat, file, caption: nil, parse_mode: 'Markdown')
121
- document =
122
- if file.is_a?(String)
123
- Faraday::UploadIO.new(file, 'application/octet-stream', File.basename(file))
124
- else
125
- file
142
+ return album(chat, file, caption:, parse_mode:) if file.is_a?(Array)
143
+ io = upload(file)
144
+ if photo?(file)
145
+ @bot.api.send_photo(chat_id: chat, photo: io, caption:, parse_mode:)
146
+ else
147
+ @bot.api.send_document(chat_id: chat, document: io, caption:, parse_mode:)
148
+ end
149
+ ensure
150
+ io.close if file.is_a?(String) && io.respond_to?(:close)
151
+ end
152
+
153
+ PHOTO_EXTENSIONS = %w[.jpg .jpeg .png .gif .webp].freeze
154
+
155
+ private
156
+
157
+ def album(chat, files, caption: nil, parse_mode: 'Markdown')
158
+ opened = []
159
+ params = { chat_id: chat }
160
+ params[:media] =
161
+ files.each_with_index.map do |file, idx|
162
+ io = upload(file)
163
+ opened << io if file.is_a?(String)
164
+ params[:"file#{idx}"] = io
165
+ attrs = { media: "attach://file#{idx}" }
166
+ if idx.zero? && !caption.nil?
167
+ attrs[:caption] = caption
168
+ attrs[:parse_mode] = parse_mode
169
+ end
170
+ klass(file).new(**attrs)
126
171
  end
127
- @bot.api.send_document(chat_id: chat, document:, caption:, parse_mode:)
172
+ @bot.api.send_media_group(**params)
173
+ ensure
174
+ opened.each(&:close)
175
+ end
176
+
177
+ def upload(file)
178
+ return file unless file.is_a?(String)
179
+ Faraday::UploadIO.new(file, 'application/octet-stream', File.basename(file))
180
+ end
181
+
182
+ def klass(file)
183
+ photo?(file) ? Telegram::Bot::Types::InputMediaPhoto : Telegram::Bot::Types::InputMediaDocument
184
+ end
185
+
186
+ def photo?(file)
187
+ PHOTO_EXTENSIONS.include?(File.extname(file.is_a?(String) ? file : file.path).downcase)
128
188
  end
129
189
  end
data/telepost.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to?(:required_rubygems_version=)
10
10
  s.required_ruby_version = '>=3.2'
11
11
  s.name = 'telepost'
12
- s.version = '0.8.1'
12
+ s.version = '0.10.0'
13
13
  s.license = 'MIT'
14
14
  s.summary = 'Simple Telegram posting Ruby gem'
15
15
  s.description = 'Simple Telegram posting Ruby gem'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telepost
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko