rubydium 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c97622609c92a647d42a147d5f6d3b50b8d3f166364839e7457dfa801463677f
4
- data.tar.gz: 383f1f95771e399b41b3155b55ae06cf3287005af7250d7bc4b14add9bdb12fa
3
+ metadata.gz: 8787fccb275711579c29e53c725847948c7b4ff6f1824284a91e5e037bd0768e
4
+ data.tar.gz: 1650a9f2d7531bd6173a907ac000b3b00f74a1f52c5b6318d283b2ea328f1d9d
5
5
  SHA512:
6
- metadata.gz: 1ad2ec41e84c6e36b910c5a403bda5e059f825831a9891ed6a5fc50e4d15e8630e9c84ac82425aae8c0f0138c256a8ff7ed44060c966d55112deb6b5aa60fc8b
7
- data.tar.gz: efae72abbd1130732908a5832b3b487b238d15abea26c89ca086e234fb2be330f32a2fd4e5e2ec959acb010209e7d4618de9de6926feb006420f0e89555f75e3
6
+ metadata.gz: 72c52fde7897f448aef03fc3601a677ee2278bb979544d633d3be9aef0c835afbe16ae554d8eb9c7581ef8020eb961e7d0dfc7ab1ddd9e8f573257529cfc324a
7
+ data.tar.gz: 0e9480b09c9a489b10e34869478fa3f797641619b9843e93c78ca122a2ac279d60819cbfced0a4805e43b83b182aedb6e448338ffb4d3b334e30cb9b6ae359d2
data/.gitignore CHANGED
@@ -1 +1,3 @@
1
1
  rubydium-*.gem
2
+ test.rb
3
+ /temp/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.rubocop.yml CHANGED
@@ -1,7 +1,62 @@
1
+ require: rubocop-rspec
2
+
1
3
  AllCops:
2
4
  TargetRubyVersion: 3.1.2
3
5
  NewCops: enable
6
+ Exclude:
7
+ - test.rb
8
+
4
9
  Layout/LineLength:
5
10
  Max: 100
11
+
12
+ Layout/SpaceAroundEqualsInParameterDefault:
13
+ EnforcedStyle: no_space
14
+
15
+ Layout/SpaceInsideBlockBraces:
16
+ EnforcedStyleForEmptyBraces: space
17
+
18
+ Lint/EmptyBlock:
19
+ Exclude:
20
+ - spec/rubydium/mixins/command_macros_spec.rb
21
+
22
+ Metrics/BlockLength:
23
+ Exclude:
24
+ - spec/**/*
25
+
26
+ Metrics/MethodLength:
27
+ CountAsOne:
28
+ - array
29
+ - heredoc
30
+ - method_call
31
+ - hash
32
+
33
+ RSpec/BeNil:
34
+ EnforcedStyle: be
35
+
36
+ RSpec/ExampleLength:
37
+ Max: 10
38
+
39
+ RSpec/HookArgument:
40
+ EnforcedStyle: example
41
+
42
+ RSpec/MessageSpies:
43
+ EnforcedStyle: receive
44
+
45
+ RSpec/MultipleExpectations:
46
+ Max: 5
47
+
48
+ Style/BlockDelimiters:
49
+ EnforcedStyle: braces_for_chaining
50
+
51
+ Style/Documentation:
52
+ AllowedConstants:
53
+ - ClassMethods
54
+
55
+ Style/HashSyntax:
56
+ EnforcedShorthandSyntax: never
57
+
58
+ Style/IfUnlessModifier:
59
+ Enabled: false
60
+
6
61
  Style/StringLiterals:
7
62
  EnforcedStyle: double_quotes
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.2
1
+ 3.2.2
data/Gemfile CHANGED
@@ -4,8 +4,9 @@ source "https://rubygems.org"
4
4
 
5
5
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
6
6
 
7
- ruby "~> 3.1", ">= 3.1.2"
8
-
9
7
  gem "pry"
8
+ gem "rspec"
10
9
  gem "rubocop"
10
+ gem "rubocop-rspec"
11
11
  gem "telegram-bot-ruby"
12
+ gem "async"
data/Gemfile.lock CHANGED
@@ -2,6 +2,10 @@ GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
4
  ast (2.4.2)
5
+ async (2.3.1)
6
+ console (~> 1.10)
7
+ io-event (~> 1.1)
8
+ timers (~> 4.1)
5
9
  axiom-types (0.1.1)
6
10
  descendants_tracker (~> 0.0.4)
7
11
  ice_nine (~> 0.11.0)
@@ -9,8 +13,11 @@ GEM
9
13
  coderay (1.1.3)
10
14
  coercible (1.0.0)
11
15
  descendants_tracker (~> 0.0.1)
16
+ console (1.16.2)
17
+ fiber-local
12
18
  descendants_tracker (0.0.4)
13
19
  thread_safe (~> 0.3, >= 0.3.1)
20
+ diff-lcs (1.5.0)
14
21
  dry-inflector (1.0.0)
15
22
  faraday (2.7.2)
16
23
  faraday-net_http (>= 2.0, < 3.1)
@@ -18,7 +25,9 @@ GEM
18
25
  faraday-multipart (1.0.4)
19
26
  multipart-post (~> 2)
20
27
  faraday-net_http (3.0.2)
28
+ fiber-local (1.0.0)
21
29
  ice_nine (0.11.2)
30
+ io-event (1.1.5)
22
31
  json (2.6.3)
23
32
  method_source (1.0.0)
24
33
  multipart-post (2.2.3)
@@ -31,6 +40,19 @@ GEM
31
40
  rainbow (3.1.1)
32
41
  regexp_parser (2.6.1)
33
42
  rexml (3.2.5)
43
+ rspec (3.12.0)
44
+ rspec-core (~> 3.12.0)
45
+ rspec-expectations (~> 3.12.0)
46
+ rspec-mocks (~> 3.12.0)
47
+ rspec-core (3.12.0)
48
+ rspec-support (~> 3.12.0)
49
+ rspec-expectations (3.12.1)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.12.0)
52
+ rspec-mocks (3.12.1)
53
+ diff-lcs (>= 1.2.0, < 2.0)
54
+ rspec-support (~> 3.12.0)
55
+ rspec-support (3.12.0)
34
56
  rubocop (1.41.1)
35
57
  json (~> 2.3)
36
58
  parallel (~> 1.10)
@@ -43,6 +65,8 @@ GEM
43
65
  unicode-display_width (>= 1.4.0, < 3.0)
44
66
  rubocop-ast (1.24.0)
45
67
  parser (>= 3.1.1.0)
68
+ rubocop-rspec (2.16.0)
69
+ rubocop (~> 1.33)
46
70
  ruby-progressbar (1.11.0)
47
71
  ruby2_keywords (0.0.5)
48
72
  telegram-bot-ruby (0.23.0)
@@ -51,6 +75,7 @@ GEM
51
75
  faraday-multipart (~> 1.0)
52
76
  virtus (~> 2.0)
53
77
  thread_safe (0.3.6)
78
+ timers (4.3.5)
54
79
  unicode-display_width (2.3.0)
55
80
  virtus (2.0.0)
56
81
  axiom-types (~> 0.1)
@@ -61,12 +86,12 @@ PLATFORMS
61
86
  x86_64-linux
62
87
 
63
88
  DEPENDENCIES
89
+ async
64
90
  pry
91
+ rspec
65
92
  rubocop
93
+ rubocop-rspec
66
94
  telegram-bot-ruby
67
95
 
68
- RUBY VERSION
69
- ruby 3.1.2p20
70
-
71
96
  BUNDLED WITH
72
97
  2.3.26
data/README.md CHANGED
@@ -1 +1,85 @@
1
- don't read me yet, i'm not ready
1
+ # What it is
2
+ Rubydium is a framework for building Telegram bots.
3
+ Built on top of [telegram-bot-ruby](https://github.com/atipugin/telegram-bot-ruby) API wrapper, it aims to provide tools for building your bots with minimum boilerplate.
4
+
5
+ It's far from being done, but all the code in the [examples](example/) directory is functional and *mostly* covered with [specs](spec/rubydium/).
6
+
7
+ # Installation
8
+ CLI tool for creating and setting up new projects is planned. For now:
9
+
10
+ 1. Create your project:
11
+ `mkdir example_bot && cd example_bot`
12
+ 2. Install the gem:
13
+ - Add this gem to the Gemfile:
14
+ `bundle init && echo 'gem "rubydium"' >> Gemfile`
15
+ `bundle install`
16
+ - Or install system-wide:
17
+ `gem install rubydium`
18
+ 3. Create your main file:
19
+ `touch example_bot.rb`
20
+ 4. See the [examples](example/) directory for bot examples.
21
+
22
+ # Configuring the bot
23
+ There's two main ways to write your config. With a block:
24
+ ```ruby
25
+ ExampleBot.configure do |config|
26
+ config.token = "1234567890:long_alphanumeric_string_goes_here"
27
+ config.bot_username = "ends_with_bot"
28
+ config.owner_username = "thats_you"
29
+ config.privileged_usernames = %w[
30
+ your_friend your_chat_moderator
31
+ ]
32
+ end
33
+ ```
34
+ Or with a hash:
35
+ ```ruby
36
+ ExampleBot.config = {
37
+ token: "1234567890:long_alphanumeric_string_goes_here",
38
+ bot_username: "ends_with_bot",
39
+ owner_username: "thats_you",
40
+ privileged_usernames: %w[
41
+ your_friend your_chat_moderator
42
+ ]
43
+ }
44
+ ```
45
+ The hash variant also means you can pass in any valid Ruby hash, regardless of where it comes from. For example, you can set the same values, if you parse
46
+ <details>
47
+ <summary>a JSON file:</summary>
48
+
49
+ `example_bot/config.json`:
50
+ ```json
51
+ {
52
+ "token": "1234567890:long_alphanumeric_string_goes_here",
53
+ "bot_username": "ends_with_bot",
54
+ "owner_username": "thats_you",
55
+ "privileged_usernames": [
56
+ "your_friend",
57
+ "your_chat_moderator"
58
+ ]
59
+ }
60
+ ```
61
+ `example_bot/example_bot.rb`:
62
+ ```ruby
63
+ require "json"
64
+ ExampleBot.config = JSON.load_file("./config.json")
65
+ ```
66
+ </details>
67
+
68
+ <details>
69
+ <summary>or a YAML:</summary>
70
+
71
+ `example_bot/config.yaml`
72
+ ```yaml
73
+ token: 1234567890:long_alphanumeric_string_goes_here
74
+ bot_username: ends_with_bot
75
+ owner_username: thats_you
76
+ privileged_usernames:
77
+ - your_friend
78
+ - your_chat_moderator
79
+ ```
80
+ `example_bot/example_bot.rb`:
81
+ ```ruby
82
+ require "yaml"
83
+ ExampleBot.config = YAML.load_file("./config.yaml")
84
+ ```
85
+ </details>
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubydium"
4
+
5
+ # Your actual logic of handling the updates goes here.
6
+ class ExampleBot < Rubydium::Bot
7
+ on_every_message :log_message
8
+
9
+ on_command "/help", description: "Show help message" do
10
+ text = help_message
11
+ send_message(text)
12
+ end
13
+
14
+ on_command "/start", :greet_user, description: "Say hello"
15
+
16
+ def log_message
17
+ puts "Got message from #{@user.first_name}, text: \n#{@text}"
18
+ end
19
+
20
+ def greet_user
21
+ text = "Hi hello"
22
+ reply(text)
23
+ end
24
+ end
25
+
26
+ ExampleBot.configure do |config|
27
+ config.token = "1234567890:long_alphanumeric_string_goes_here"
28
+ config.bot_username = "ends_with_bot"
29
+ config.owner_username = "thats_you"
30
+ config.privileged_usernames = %w[
31
+ your_friend your_chat_moderator
32
+ ]
33
+ end
34
+
35
+ ExampleBot.run
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubydium
4
+ # On every update a new instance of this class is created to handle it.
5
+ # This is a base class that's meant to be inherited by user's actual bot class,
6
+ # where actual logic of handling the updates is described.
7
+ class Bot
8
+ include Mixins
9
+
10
+ COMMAND_REGEXP = %r{\A/}
11
+
12
+ def self.run
13
+ Telegram::Bot::Client.run(config.token) do |client|
14
+ Async do |task|
15
+ client.listen do |update|
16
+ task.async do
17
+ # Not all `update`s here are messages (`listen` yields `Update#current_message`,
18
+ # which can be `ChatMemberUpdated` etc.)
19
+ # TODO: rework to allow for clean handling of other types.
20
+ if update.is_a? Telegram::Bot::Types::Message
21
+ new(client, update).handle_update
22
+ end
23
+ rescue => e
24
+ puts e.detailed_message, e.backtrace
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def initialize(client, update)
32
+ @client = client
33
+ @api = client.api
34
+ @msg = update
35
+ @user = @msg.from
36
+ @chat = @msg.chat
37
+ @replies_to = @msg.reply_to_message
38
+ @target = @replies_to&.from
39
+ @text = @msg.text.to_s
40
+ @message_id = @msg.message_id
41
+ @command = get_command(@msg.text)
42
+ @text_without_command = @text.gsub(@command.to_s, '').gsub(/@#{config.bot_username}\b/, '').strip
43
+ @text_without_bot_mentions = @text.gsub(/@#{config.bot_username}\b/, '')
44
+ end
45
+
46
+ def handle_update
47
+ execute_on_every_message
48
+ execute_on_mention if @text.split(/\s/).first == "@#{config.bot_username}"
49
+ # execute_on_reply if @target&.username == config.bot_username
50
+ execute_command
51
+ end
52
+
53
+ private
54
+
55
+ def help_message
56
+ self.class.registered_commands.map { |command, info|
57
+ "#{command} - #{info[:description]}"
58
+ }.join("\n")
59
+ end
60
+
61
+ # For example:
62
+ # "/start asdf", "/start@yourbot", "/start /another", "asdf /start" will all return "/start".
63
+ def get_command(text)
64
+ return unless text
65
+
66
+ @command = text.split.grep(%r{\A/}).first
67
+ @command&.delete_suffix("@#{config.bot_username}")
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubydium
4
+ class Bot # :nodoc:
5
+ def self.configure
6
+ yield config
7
+ end
8
+
9
+ def self.config
10
+ @config ||= Config.new
11
+ end
12
+
13
+ def self.config=(config_hash)
14
+ config_hash.each_pair do |key, value|
15
+ config.public_send("#{key}=", value)
16
+ end
17
+ end
18
+
19
+ def config
20
+ self.class.config
21
+ end
22
+ end
23
+
24
+ # Things that a full-fledged bot probably needs to know.
25
+ # Not necessary for basic functionality, though.
26
+ class Config
27
+ def method_missing(method, ...)
28
+ if method[-1] == '='
29
+ self.class.attr_accessor method[0..-2]
30
+ public_send(method, ...)
31
+ else
32
+ super(method, ...)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubydium
4
+ module Mixins
5
+ # Macro-like definitions that describe what actions bot takes
6
+ # in reaction to messages.
7
+ module CommandMacros
8
+ def self.included(base)
9
+ base.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ def on_mention(method_name=nil, ignore_forwarded: true, &block)
14
+ @registered_on_mention ||= []
15
+ action = (method_name || block)
16
+ raise ArgumentError, "Provide either method name or a block" unless action
17
+
18
+ @registered_on_mention << {
19
+ action: action,
20
+ ignore_forwarded: ignore_forwarded
21
+ }
22
+ end
23
+
24
+ def registered_on_mention
25
+ @registered_on_mention ||= []
26
+ end
27
+
28
+ def on_every_message(method_name=nil, ignore_forwarded: true, &block)
29
+ @registered_on_every_message ||= []
30
+ action = (method_name || block)
31
+ raise ArgumentError, "Provide either method name or a block" unless action
32
+
33
+ @registered_on_every_message << {
34
+ action: action,
35
+ ignore_forwarded: ignore_forwarded
36
+ }
37
+ end
38
+
39
+ def registered_on_every_message
40
+ @registered_on_every_message ||= []
41
+ end
42
+
43
+ def on_command(command, method_name=nil, description: nil, ignore_forwarded: true, &block)
44
+ @registered_commands ||= {}
45
+ action = (method_name || block)
46
+ raise ArgumentError, "Provide either method name or a block" unless action
47
+
48
+ @registered_commands.merge!(
49
+ {
50
+ command => {
51
+ action: action,
52
+ description: description,
53
+ ignore_forwarded: ignore_forwarded
54
+ }
55
+ }
56
+ )
57
+ end
58
+
59
+ def registered_commands
60
+ @registered_commands ||= {}
61
+ end
62
+ end
63
+
64
+ def skip_task(**kwargs)
65
+ kwargs.each_pair do |task_type, task_name|
66
+
67
+ end
68
+ end
69
+
70
+ def execute_on_every_message
71
+ self.class.registered_on_every_message.each do |action|
72
+ next if action[:ignore_forwarded] && @msg.forward_date
73
+ execute_action(action[:action])
74
+ end
75
+ end
76
+
77
+ def execute_on_mention
78
+ self.class.registered_on_mention.each do |action|
79
+ next if action[:ignore_forwarded] && @msg.forward_date
80
+ execute_action(action[:action])
81
+ end
82
+ end
83
+
84
+ def execute_command
85
+ command = self.class.registered_commands[@command]
86
+ return unless command
87
+ return if command[:ignore_forwarded] && @msg.forward_date
88
+
89
+ action = command[:action]
90
+
91
+ execute_action(action)
92
+ end
93
+
94
+ def execute_action(action)
95
+ case action
96
+ when Symbol
97
+ public_send action
98
+ when Proc
99
+ instance_exec(&action)
100
+ end
101
+ end
102
+
103
+ # # Implement @commands
104
+ # def execute_all_commands
105
+ # method_names = self.class.registered_commands.slice(*@commands).values
106
+ # method_name.each { |method| self.public_send(method) }
107
+ # end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubydium
4
+ module Mixins
5
+ # These methods encapsulate common logic in control flow structures
6
+ # a bot will probably have.
7
+ module ControlFlow
8
+ def must_be_owner
9
+ return not_from_owner unless @user.username == config.owner_username
10
+
11
+ yield
12
+ end
13
+
14
+ def must_be_reply
15
+ return not_a_reply unless @replies_to
16
+
17
+ yield
18
+ end
19
+
20
+ def must_be_privileged
21
+ return not_from_privileged unless config.privileged_usernames.include?(@user.username)
22
+
23
+ yield
24
+ end
25
+
26
+ private
27
+
28
+ # Overridable methods
29
+ def not_from_owner; end
30
+
31
+ def not_a_reply; end
32
+
33
+ def not_from_privileged; end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubydium
4
+ module Mixins
5
+ # Shorthand methods for sending messages in different ways.
6
+ module MessageSending
7
+ def send_message(text)
8
+ @api.send_message(
9
+ chat_id: @chat.id,
10
+ text: text
11
+ )
12
+ end
13
+
14
+ def send_sticker(sticker, action: nil, **kwargs)
15
+ chat_action_if_provided(action, :choose_sticker)
16
+
17
+ @api.send_sticker(
18
+ chat_id: @chat.id,
19
+ sticker: sticker,
20
+ **kwargs
21
+ )
22
+ end
23
+
24
+ def chat_action_if_provided(duration, action_name)
25
+ if duration
26
+ send_chat_action(action_name)
27
+ sleep duration if duration.is_a? Numeric
28
+ end
29
+ end
30
+
31
+ def send_chat_action(action, **kwargs)
32
+ @api.send_chat_action(
33
+ chat_id: @chat.id,
34
+ action: action,
35
+ **kwargs
36
+ )
37
+ end
38
+
39
+ def send_video(video, action: nil, **kwargs)
40
+ chat_action_if_provided(action, :upload_video)
41
+
42
+ @api.send_video(
43
+ chat_id: @chat.id,
44
+ video: video,
45
+ **kwargs
46
+ )
47
+ end
48
+
49
+ def send_photo(photo, action: nil, **kwargs)
50
+ chat_action_if_provided(action, :upload_photo)
51
+
52
+ @api.send_photo(
53
+ chat_id: @chat.id,
54
+ photo: photo,
55
+ **kwargs
56
+ )
57
+ end
58
+
59
+ def reply(text, **args)
60
+ @api.send_message(
61
+ chat_id: @chat.id,
62
+ reply_to_message_id: @message_id,
63
+ text: text,
64
+ **args
65
+ )
66
+ end
67
+
68
+ def reply_code(text)
69
+ reply("```\n#{text}```", parse_mode: "Markdown")
70
+ end
71
+
72
+ def reply_to_target(text)
73
+ @api.send_message(
74
+ chat_id: @chat.id,
75
+ reply_to_message_id: @replies_to.message_id,
76
+ text: text
77
+ )
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "mixins/message_sending"
4
+ require_relative "mixins/command_macros"
5
+ require_relative "mixins/control_flow"
6
+
7
+ module Rubydium
8
+ module Mixins # :nodoc:
9
+ def self.included(base)
10
+ base.include(
11
+ MessageSending,
12
+ CommandMacros,
13
+ ControlFlow
14
+ )
15
+ end
16
+ end
17
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubydium
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/rubydium.rb CHANGED
@@ -1,4 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rubydium
3
+ require "telegram/bot"
4
+ require "async"
5
+ require_relative "rubydium/mixins"
6
+ require_relative "rubydium/bot"
7
+ require_relative "rubydium/config"
8
+ require_relative "rubydium/version"
9
+
10
+ module Rubydium # :nodoc:
4
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubydium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bulgakke
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-27 00:00:00.000000000 Z
11
+ date: 2023-09-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -18,13 +18,21 @@ extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
20
  - ".gitignore"
21
+ - ".rspec"
21
22
  - ".rubocop.yml"
22
23
  - ".ruby-version"
23
24
  - Gemfile
24
25
  - Gemfile.lock
25
26
  - LICENSE
26
27
  - README.md
28
+ - example/example_bot.rb
27
29
  - lib/rubydium.rb
30
+ - lib/rubydium/bot.rb
31
+ - lib/rubydium/config.rb
32
+ - lib/rubydium/mixins.rb
33
+ - lib/rubydium/mixins/command_macros.rb
34
+ - lib/rubydium/mixins/control_flow.rb
35
+ - lib/rubydium/mixins/message_sending.rb
28
36
  - lib/rubydium/version.rb
29
37
  - rubydium.gemspec
30
38
  homepage: https://github.com/bulgakke/rubydium
@@ -49,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
57
  - !ruby/object:Gem::Version
50
58
  version: '0'
51
59
  requirements: []
52
- rubygems_version: 3.3.21
60
+ rubygems_version: 3.4.10
53
61
  signing_key:
54
62
  specification_version: 4
55
63
  summary: An OO framework for building Telegram bots. That's the plan, at least.