telegram-bot 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d30226c4449c1cbe5ebd12cf4ec90203532fea57
4
- data.tar.gz: 8fc903c5a4cf84377bf258584f7d2adf04741ef5
3
+ metadata.gz: 33815725b4d37b05fdc85130cdc70de21c1b1f42
4
+ data.tar.gz: 2cc11d12481d962c04cbaf440337a358d4ee6d2e
5
5
  SHA512:
6
- metadata.gz: 1a436e375c7b22d18c5c18be2a55e4189f909e473106350b0c8e7d0d8336b55e5b76c5cf625800e753627715f665300049c282f96c74e48200ed39d36f9e1094
7
- data.tar.gz: d2c166e8ffaeec12fb83a3a391cc7aa05b431ad5a6151614ca0bbe3449ce0f4c5bf5e33538cbb0ba1d75923dc86e4ae600dc971755367f22916abf3834df38c6
6
+ metadata.gz: 3e1b110b959fef3b8bc6d1837e4328f98baa8ec3bae7dd2679b2c3f90befbba714b5c19050699c9ba1f635becacabbf781acfd8e390281d099df72b25fef0881
7
+ data.tar.gz: b95595b9e1116c5441a3742b0c8953936279647f0f010a37f55a58e41c82de8170cb1953fec4e0169bf11c91105c40e84a4e6f595dac7220b70e37a317b58319
data/Gemfile CHANGED
@@ -6,6 +6,8 @@ group :development do
6
6
  gem 'pry', '~> 0.10.1'
7
7
  gem 'pry-byebug', '~> 3.2.0'
8
8
 
9
+ gem 'telegram-bot-types', '~> 0.2.0'
10
+
9
11
  gem 'rspec', '~> 3.3.0'
10
12
  gem 'rspec-its', '~> 1.1.0'
11
13
 
data/README.md CHANGED
@@ -1,17 +1,17 @@
1
1
  # Telegram::Bot
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/telegram-bot.svg)](http://badge.fury.io/rb/telegram-bot)
4
- [![Code Climate](https://codeclimate.com/github/printercu/telegram-bot/badges/gpa.svg)](https://codeclimate.com/github/printercu/telegram-bot)
5
- [![Build Status](https://travis-ci.org/printercu/telegram-bot.svg)](https://travis-ci.org/printercu/telegram-bot)
4
+ [![Code Climate](https://codeclimate.com/github/telegram-bot-rb/telegram-bot/badges/gpa.svg)](https://codeclimate.com/github/telegram-bot-rb/telegram-bot)
5
+ [![Build Status](https://travis-ci.org/telegram-bot-rb/telegram-bot.svg)](https://travis-ci.org/telegram-bot-rb/telegram-bot)
6
6
 
7
7
  Tools for developing bot for Telegram. Best used with Rails, but can be be used in
8
- standalone app. Supposed to be used in webhook-mode in production, and poller mode
8
+ standalone app. Supposed to be used in webhook-mode in production, and poller-mode
9
9
  in development, but you can use poller in production if you want.
10
10
 
11
11
  Package contains:
12
12
 
13
- - Ligthweight client to bot API (with fast and thread-safe
14
- [httpclient](https://github.com/nahi/httpclient) is under the hood.)
13
+ - Ligthweight client for bot API (with fast and thread-safe
14
+ [httpclient](https://github.com/nahi/httpclient) under the hood).
15
15
  - Controller with message parser. Allows to write separate methods for each command.
16
16
  - Middleware and routes helpers for production env.
17
17
  - Poller with automatic source-reloader for development env.
@@ -62,23 +62,39 @@ telegram:
62
62
  From now clients will be accessible with `Telegram.bots[:chat]` or `Telegram.bots[:auction]`.
63
63
  Single bot can be accessed with `Telegram.bot` or `Telegram.bots[:default]`.
64
64
 
65
- You can create clients manually with `Telegram::Bot.new(token, username)`.
65
+ You can create clients manually with `Telegram::Bot::Client.new(token, username)`.
66
66
  Username is optional and used only to parse commands with mentions.
67
67
 
68
- Client has all available methods in underscored style
68
+ There is `request(path_suffix, body)` method to perform any query.
69
+ And there are also shortcuts for available queries in underscored style
69
70
  (`answer_inline_query` instead of `answerInlineQuery`).
70
71
  All this methods just post given params to specific URL.
71
72
 
72
73
  ```ruby
74
+ bot.request(:getMe) or bot.get_me
75
+ bot.request(:getupdates, offset: 1) or bot.get_updates(offset: 1)
73
76
  bot.send_message chat_id: chat_id, text: 'Test'
74
77
  ```
75
78
 
79
+ By default client will return parsed json responses. You can enable
80
+ response typecasting to virtus models using `telegram-bot-types` gem:
81
+ ```ruby
82
+ # Add to your gemfile:
83
+ gem 'telegram-bot-types', '~> x.x.x'
84
+ # Enable typecasting:
85
+ Telegram::Bot::Client.typed_response!
86
+ # or for single instance:
87
+ bot.extend Telegram::Bot::Client::TypedResponse
88
+
89
+ bot.get_me.class # => Telegram::Bot::Types::User
90
+ ```
91
+
76
92
  ### Controller
77
93
 
78
94
  ```ruby
79
95
  class Telegram::WebhookController < Telegram::Bot::UpdatesController
80
96
  # use callbacks like in any other controllers
81
- around_action :set_locale
97
+ around_action :with_locale
82
98
 
83
99
  # Every update can have one of: message, inline_query & chosen_inline_result.
84
100
  # Define method with same name to respond to this updates.
@@ -107,7 +123,7 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
107
123
 
108
124
  private
109
125
 
110
- def set_locale(&block)
126
+ def with_locale(&block)
111
127
  I18n.with_locale(locale_for_update, &block)
112
128
  end
113
129
 
@@ -121,6 +137,49 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
121
137
  end
122
138
  ```
123
139
 
140
+ You can enable typecasting of `update` with `telegram-bot-types` by including
141
+ `Telegram::Bot::UpdatesPoller::TypedUpdate`:
142
+
143
+ ```ruby
144
+ class Telegram::WebhookController < Telegram::Bot::UpdatesController
145
+ include Telegram::Bot::UpdatesPoller::TypedUpdate
146
+
147
+ def message(message)
148
+ message.class # => Telegram::Bot::Types::Message
149
+ end
150
+ end
151
+ ```
152
+
153
+ There is support for sessions using `ActiveSupport::Cache` stores.
154
+
155
+ ```ruby
156
+ # configure store in env files:
157
+ config.telegram_updates_controller.session_store = :redis_store, {expires_in: 1.month}
158
+
159
+ class Telegram::WebhookController < Telegram::Bot::UpdatesController
160
+ include Telegram::Bot::UpdatesController::Session
161
+ # You can override global config
162
+ self.session_store = :file_store
163
+
164
+ def write(text = nil, *)
165
+ session[:text] = text
166
+ end
167
+
168
+ def read
169
+ session[:text]
170
+ end
171
+
172
+ private
173
+ # By default it uses bot's username and user's id as a session key.
174
+ # Chat's id is used only when `from` field is empty.
175
+ # Override `session_key` method to change this behavior.
176
+ def session_key
177
+ # In this case session will persist for user only in specific chat:
178
+ "#{bot.username}:#{chat['id']}:#{from['id']}"
179
+ end
180
+ end
181
+ ```
182
+
124
183
  ### Routes
125
184
 
126
185
  Use `telegram_webhooks` helper to add routes. It will create routes for bots
@@ -181,4 +240,4 @@ push git commits and tags, and push the `.gem` file to [rubygems.org](https://ru
181
240
 
182
241
  ## Contributing
183
242
 
184
- Bug reports and pull requests are welcome on GitHub at https://github.com/printercu/telegram-bot.
243
+ Bug reports and pull requests are welcome on GitHub at https://github.com/telegram-bot-rb/telegram-bot.
data/lib/telegram/bot.rb CHANGED
@@ -1,97 +1,17 @@
1
- require 'httpclient'
2
- require 'json'
3
- require 'active_support/core_ext/string/inflections'
4
- require 'active_support/core_ext/hash/keys'
5
- require 'active_support/core_ext/array/wrap'
6
- require 'telegram/bottable'
1
+ require 'telegram/bot/config_methods'
7
2
 
8
3
  module Telegram
9
- extend Bottable
4
+ extend Bot::ConfigMethods
10
5
 
11
- class Bot
6
+ module Bot
12
7
  class Error < StandardError; end
13
8
  class NotFound < Error; end
14
9
 
10
+ autoload :Client, 'telegram/bot/client'
11
+ autoload :ClientStub, 'telegram/bot/client_stub'
15
12
  autoload :Middleware, 'telegram/bot/middleware'
16
13
  autoload :UpdatesController, 'telegram/bot/updates_controller'
17
14
  autoload :UpdatesPoller, 'telegram/bot/updates_poller'
18
-
19
- URL_TEMPLATE = 'https://api.telegram.org/bot%s/'.freeze
20
-
21
- class << self
22
- # Accepts different options to initialize bot.
23
- def wrap(input)
24
- case input
25
- when self then input
26
- when Array then input.map(&method(__callee__))
27
- when Hash then
28
- input = input.stringify_keys
29
- new input['token'], input['username']
30
- else
31
- new(input)
32
- end
33
- end
34
- end
35
-
36
- attr_reader :client, :token, :username, :base_uri
37
-
38
- def initialize(token, username = nil)
39
- @client = HTTPClient.new
40
- @token = token
41
- @username = username
42
- @base_uri = format URL_TEMPLATE, token
43
- end
44
-
45
- def debug!(dev = STDOUT)
46
- client.debug_dev = dev
47
- end
48
-
49
- def debug_off!
50
- client.debug_dev = nil
51
- end
52
-
53
- def request(action, data = {})
54
- res = http_request("#{base_uri}#{action}", data)
55
- status = res.status
56
- return JSON.parse(res.body) if 300 > status
57
- result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
58
- err_msg = "#{res.reason}: #{result && result['description'] || '-'}"
59
- # NotFound is raised only for valid responses from Telegram
60
- raise NotFound, err_msg if 404 == status && result
61
- raise Error, err_msg
62
- end
63
-
64
- %w(
65
- answerInlineQuery
66
- forwardMessage
67
- getFile
68
- getMe
69
- getUpdates
70
- getUserProfilePhotos
71
- sendAudio
72
- sendChatAction
73
- sendDocument
74
- sendLocation
75
- sendMessage
76
- sendPhoto
77
- sendSticker
78
- sendVideo
79
- sendVoice
80
- setWebhook
81
- ).each do |method|
82
- define_method(method.underscore) { |*args| request(method, *args) }
83
- end
84
-
85
- # Endpoint for low-level request. For easy host highjacking & instrumentation.
86
- # Params are not used directly but kept for instrumentation purpose.
87
- # You probably don't want to use this method directly.
88
- def http_request(uri, body)
89
- client.post(uri, body)
90
- end
91
-
92
- def inspect
93
- "#<Telegram::Bot##{object_id}(#{@username})>"
94
- end
95
15
  end
96
16
  end
97
17
 
@@ -0,0 +1,111 @@
1
+ require 'json'
2
+ require 'httpclient'
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'active_support/core_ext/hash/keys'
5
+
6
+ module Telegram
7
+ module Bot
8
+ class Client
9
+ autoload :TypedResponse, 'telegram/bot/client/typed_response'
10
+
11
+ URL_TEMPLATE = 'https://api.telegram.org/bot%s/'.freeze
12
+
13
+ class << self
14
+ # Accepts different options to initialize bot.
15
+ def wrap(input)
16
+ case input
17
+ when self then input
18
+ when Array then input.map(&method(__callee__))
19
+ when Hash then
20
+ input = input.stringify_keys
21
+ new input['token'], input['username']
22
+ else
23
+ new(input)
24
+ end
25
+ end
26
+
27
+ # Prepend TypedResponse module.
28
+ def typed_response!
29
+ prepend TypedResponse
30
+ end
31
+
32
+ # Encodes nested hashes as json.
33
+ def prepare_body(body)
34
+ body.each do |k, val|
35
+ body[k] = val.to_json if val.is_a?(Hash)
36
+ end
37
+ end
38
+ end
39
+
40
+ attr_reader :client, :token, :username, :base_uri
41
+
42
+ def initialize(token, username = nil)
43
+ @client = HTTPClient.new
44
+ @token = token
45
+ @username = username
46
+ @base_uri = format URL_TEMPLATE, token
47
+ end
48
+
49
+ def debug!(dev = STDOUT)
50
+ if block_given?
51
+ begin
52
+ old_dev = client.debug_dev
53
+ client.debug_dev = dev
54
+ yield
55
+ ensure
56
+ client.debug_dev = old_dev
57
+ end
58
+ else
59
+ client.debug_dev = dev
60
+ end
61
+ end
62
+
63
+ def debug_off!
64
+ client.debug_dev = nil
65
+ end
66
+
67
+ def request(action, body = {})
68
+ res = http_request("#{base_uri}#{action}", self.class.prepare_body(body))
69
+ status = res.status
70
+ return JSON.parse(res.body) if 300 > status
71
+ result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
72
+ err_msg = "#{res.reason}: #{result && result['description'] || '-'}"
73
+ # NotFound is raised only for valid responses from Telegram
74
+ raise NotFound, err_msg if 404 == status && result
75
+ raise Error, err_msg
76
+ end
77
+
78
+ %w(
79
+ answerInlineQuery
80
+ forwardMessage
81
+ getFile
82
+ getMe
83
+ getUpdates
84
+ getUserProfilePhotos
85
+ sendAudio
86
+ sendChatAction
87
+ sendDocument
88
+ sendLocation
89
+ sendMessage
90
+ sendPhoto
91
+ sendSticker
92
+ sendVideo
93
+ sendVoice
94
+ setWebhook
95
+ ).each do |method|
96
+ define_method(method.underscore) { |*args| request(method, *args) }
97
+ end
98
+
99
+ # Endpoint for low-level request. For easy host highjacking & instrumentation.
100
+ # Params are not used directly but kept for instrumentation purpose.
101
+ # You probably don't want to use this method directly.
102
+ def http_request(uri, body)
103
+ client.post(uri, body)
104
+ end
105
+
106
+ def inspect
107
+ "#<Telegram::Bot::Client##{object_id}(#{@username})>"
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,38 @@
1
+ module Telegram
2
+ module Bot
3
+ class Client
4
+ # Actions with type-casted results. Install `telegram-bot-types` gem first.
5
+ module TypedResponse
6
+ {
7
+ getFile: :File,
8
+ getMe: :User,
9
+ getUpdates: [:Update],
10
+ getUserProfilePhotos: :UserProfilePhotos,
11
+
12
+ forwardMessage: :Message,
13
+ sendAudio: :Message,
14
+ sendDocument: :Message,
15
+ sendLocation: :Message,
16
+ sendMessage: :Message,
17
+ sendPhoto: :Message,
18
+ sendSticker: :Message,
19
+ sendVideo: :Message,
20
+ sendVoice: :Message,
21
+ }.each do |method, type|
22
+ next unless type
23
+ if type.is_a?(Array)
24
+ type_class = Types.const_get(type.first)
25
+ define_method(method.to_s.underscore) do |*args|
26
+ request(method, *args)['result'].map { |x| type_class.new(x) }
27
+ end
28
+ else
29
+ type_class = Types.const_get(type)
30
+ define_method(method.to_s.underscore) do |*args|
31
+ type_class.new request(method, *args)['result']
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ module Telegram
2
+ module Bot
3
+ # Stubbed client for tests. Saves all requests into #requests hash.
4
+ class ClientStub < Client
5
+ attr_reader :requests
6
+
7
+ def initialize(username = nil)
8
+ @username = username
9
+ reset
10
+ end
11
+
12
+ def reset
13
+ @requests = Hash.new { |h, k| h[k] = [] }
14
+ end
15
+
16
+ def request(action, body)
17
+ requests[action.to_sym] << body
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+ require 'active_support/core_ext/hash/transform_values'
3
+
4
+ module Telegram
5
+ module Bot
6
+ module ConfigMethods
7
+ # Overwrite config.
8
+ attr_writer :bots_config
9
+
10
+ # Keep this setting here, so we can avoid loading Bot::UpdatesPoller
11
+ # when polling is disabled.
12
+ attr_writer :bot_poller_mode
13
+
14
+ # It just tells routes helpers whether to add routed bots to
15
+ # Bot::UpdatesPoller, so their config will be available by bot key in
16
+ # Bot::UpdatesPoller.start.
17
+ def bot_poller_mode?
18
+ return @bot_poller_mode if defined?(@bot_poller_mode)
19
+ Rails.env.development? if defined?(Rails)
20
+ end
21
+
22
+ # Hash of bots made with bots_config.
23
+ def bots
24
+ @bots ||= bots_config.transform_values(&Client.method(:wrap))
25
+ end
26
+
27
+ # Default bot.
28
+ def bot
29
+ @bot ||= bots[:default]
30
+ end
31
+
32
+ # Returns config for .bots method. By default uses `telegram['bots']` section
33
+ # from `secrets.yml` merging `telegram['bot']` at `:default` key.
34
+ #
35
+ # Can be overwritten with .bots_config=
36
+ def bots_config
37
+ return @bots_config if @bots_config
38
+ telegram_config = Rails.application.secrets[:telegram]
39
+ (telegram_config['bots'] || {}).symbolize_keys.tap do |config|
40
+ default = telegram_config['bot']
41
+ config[:default] = default if default
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -3,7 +3,7 @@ require 'action_dispatch/http/mime_type'
3
3
  require 'action_dispatch/middleware/params_parser'
4
4
 
5
5
  module Telegram
6
- class Bot
6
+ module Bot
7
7
  class Middleware
8
8
  attr_reader :bot, :controller
9
9
 
@@ -1,7 +1,7 @@
1
1
  require 'telegram/bot/routes_helper'
2
2
 
3
3
  module Telegram
4
- class Bot
4
+ module Bot
5
5
  class Railtie < Rails::Railtie
6
6
  config.telegram_updates_controller = ActiveSupport::OrderedOptions.new
7
7
 
@@ -18,6 +18,7 @@ module Telegram
18
18
 
19
19
  ActiveSupport.on_load('telegram.bot.updates_controller') do
20
20
  self.logger = options.logger || Rails.logger
21
+ self.session_store = options.session_store || Rails.cache
21
22
  end
22
23
  end
23
24
 
@@ -1,7 +1,8 @@
1
1
  require 'telegram/bot'
2
+ require 'active_support/core_ext/array/wrap'
2
3
 
3
4
  module Telegram
4
- class Bot
5
+ module Bot
5
6
  module RoutesHelper
6
7
  class << self
7
8
  # Returns route name for given bot. Result depends on `Telegram.bots`.
@@ -42,7 +43,7 @@ module Telegram
42
43
  controllers = Hash[bots.map { |x| [x, controllers] }]
43
44
  end
44
45
  controllers.each do |bot, controller|
45
- bot = Bot.wrap(bot)
46
+ bot = Client.wrap(bot)
46
47
  controller, bot_options = controller if controller.is_a?(Array)
47
48
  params = {
48
49
  to: Middleware.new(bot, controller),
@@ -1,15 +1,32 @@
1
1
  require 'abstract_controller'
2
2
  require 'active_support/callbacks'
3
+ require 'active_support/version'
3
4
 
4
5
  module Telegram
5
- class Bot
6
+ module Bot
6
7
  class UpdatesController < AbstractController::Base
7
- include AbstractController::Callbacks
8
- include AbstractController::Translation
8
+ abstract!
9
9
 
10
+ require 'telegram/bot/updates_controller/session'
10
11
  require 'telegram/bot/updates_controller/log_subscriber'
11
12
  require 'telegram/bot/updates_controller/instrumentation'
13
+
14
+ include AbstractController::Callbacks
15
+ # Redefine callbacks with default terminator.
16
+ if ActiveSupport.gem_version >= Gem::Version.new('5')
17
+ define_callbacks :process_action,
18
+ skip_after_callbacks_if_terminated: true
19
+ else
20
+ define_callbacks :process_action,
21
+ terminator: ->(_, result) { result == false },
22
+ skip_after_callbacks_if_terminated: true
23
+ end
24
+
25
+ include AbstractController::Translation
12
26
  prepend Instrumentation
27
+ extend Session::ConfigMethods
28
+
29
+ autoload :TypedUpdate, 'telegram/bot/updates_controller/typed_update'
13
30
 
14
31
  PAYLOAD_TYPES = %w(
15
32
  message
@@ -18,7 +35,6 @@ module Telegram
18
35
  ).freeze
19
36
  CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
20
37
  CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)")
21
- abstract!
22
38
 
23
39
  class << self
24
40
  def dispatch(*args)
@@ -1,5 +1,5 @@
1
1
  module Telegram
2
- class Bot
2
+ module Bot
3
3
  class UpdatesController
4
4
  # Most methods are taken from ActionController::Instrumentation,
5
5
  # some are slightly modified.
@@ -1,7 +1,7 @@
1
1
  require 'active_support/log_subscriber'
2
2
 
3
3
  module Telegram
4
- class Bot
4
+ module Bot
5
5
  class UpdatesController
6
6
  class LogSubscriber < ActiveSupport::LogSubscriber
7
7
  def start_processing(event)
@@ -0,0 +1,31 @@
1
+ RSpec.shared_context 'telegram/bot/updates_controller' do
2
+ let(:controller_class) { described_class }
3
+ let(:instance) { controller_class.new(bot, update) }
4
+ let(:update) { {payload_type => payload} }
5
+ let(:payload_type) { 'some_type' }
6
+ let(:payload) { double(:payload) }
7
+ let(:bot) { Telegram::Bot::ClientStub.new(bot_name) }
8
+ let(:bot_name) { 'bot' }
9
+ let(:session) do
10
+ session = Telegram::Bot::UpdatesController::Session::TestSessionHash.new
11
+ allow_any_instance_of(controller_class).to receive(:session) { session }
12
+ session
13
+ end
14
+
15
+ def dispatch_message(text, options = {})
16
+ payload = build_payload :message, options.merge(text: text)
17
+ controller_class.dispatch bot, payload
18
+ end
19
+
20
+ def build_payload(type, content)
21
+ deep_stringify type => content
22
+ end
23
+
24
+ def deep_stringify(input)
25
+ case input
26
+ when Array then input.map(&method(__callee__))
27
+ when Hash then input.transform_keys(&:to_s).transform_values(&method(__callee__))
28
+ else input
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,84 @@
1
+ require 'rack/session/abstract/id'
2
+ require 'active_support/cache'
3
+
4
+ module Telegram
5
+ module Bot
6
+ class UpdatesController
7
+ # Add functionality to store data between requests.
8
+ module Session
9
+ extend ActiveSupport::Concern
10
+
11
+ def process_action(*)
12
+ super
13
+ ensure
14
+ session.commit
15
+ end
16
+
17
+ protected
18
+
19
+ def session
20
+ @_session ||= SessionHash.new(self.class.session_store, session_key)
21
+ end
22
+
23
+ def session_key
24
+ "#{bot.username}:#{from ? "from:#{from['id']}" : "chat:#{chat['id']}"}"
25
+ end
26
+
27
+ # Rack::Session::Abstract::SessionHash is taken to provide lazy loading.
28
+ # All methods that access store are overriden to support
29
+ # ActiveSupport::Cache::Store stores.
30
+ class SessionHash < Rack::Session::Abstract::SessionHash
31
+ attr_reader :id
32
+
33
+ def initialize(store, id)
34
+ @store = store
35
+ @id = id
36
+ end
37
+
38
+ def destroy
39
+ clear
40
+ @store.delete(id)
41
+ end
42
+
43
+ def exists?
44
+ return @exists if defined?(@exists)
45
+ @data = {}
46
+ @exists = @store.exist? id
47
+ end
48
+
49
+ def load!
50
+ session = @store.read(id)
51
+ @data = session ? stringify_keys(session) : {}
52
+ @loaded = true
53
+ end
54
+
55
+ def commit
56
+ return unless loaded?
57
+ data = to_hash.delete_if { |_, v| v.nil? }
58
+ @store.write(id, data)
59
+ end
60
+ end
61
+
62
+ class TestSessionHash < SessionHash
63
+ def initialize
64
+ @data = {}
65
+ @loaded = true
66
+ @exists = true
67
+ end
68
+
69
+ alias_method :destroy, :clear
70
+ alias_method :load!, :id
71
+ alias_method :commit, :id
72
+ end
73
+
74
+ module ConfigMethods
75
+ delegate :session_store, to: :config
76
+
77
+ def session_store=(store)
78
+ config.session_store = ActiveSupport::Cache.lookup_store(store)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,14 @@
1
+ module Telegram
2
+ module Bot
3
+ class UpdatesController
4
+ # Include this module to type cast update to Virtus model
5
+ # using `telegram-bot-types` gem (install this gem first).
6
+ module TypedUpdate
7
+ def initialize(bot, update)
8
+ update = Types::Update.new(update) if update && !update.is_a?(Types::Update)
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,5 +1,5 @@
1
1
  module Telegram
2
- class Bot
2
+ module Bot
3
3
  # Supposed to be used in development environments only.
4
4
  class UpdatesPoller
5
5
  class << self
@@ -15,7 +15,7 @@ module Telegram
15
15
  end
16
16
 
17
17
  def start(bot_id, controller = nil)
18
- bot = bot_id.is_a?(Symbol) ? Telegram.bots[bot_id] : Bot.wrap(bot_id)
18
+ bot = bot_id.is_a?(Symbol) ? Telegram.bots[bot_id] : Client.wrap(bot_id)
19
19
  instance = controller ? new(bot, controller) : instances[bot]
20
20
  raise "Poller not found for #{bot_id.inspect}" unless instance
21
21
  instance.start
@@ -1,6 +1,6 @@
1
1
  module Telegram
2
- class Bot
3
- VERSION = '0.3.0'.freeze
2
+ module Bot
3
+ VERSION = '0.4.0'.freeze
4
4
 
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION
data/telegram-bot.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ['melentievm@gmail.com']
11
11
 
12
12
  spec.summary = 'Library for building Telegram Bots with Rails integration'
13
- spec.homepage = 'https://github.com/printercu/telegram-bot'
13
+ spec.homepage = 'https://github.com/telegram-bot-rb/telegram-bot'
14
14
  spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^spec/}) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telegram-bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max Melentiev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-02-22 00:00:00.000000000 Z
11
+ date: 2016-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -101,17 +101,23 @@ files:
101
101
  - bin/setup
102
102
  - lib/tasks/telegram-bot.rake
103
103
  - lib/telegram/bot.rb
104
+ - lib/telegram/bot/client.rb
105
+ - lib/telegram/bot/client/typed_response.rb
106
+ - lib/telegram/bot/client_stub.rb
107
+ - lib/telegram/bot/config_methods.rb
104
108
  - lib/telegram/bot/middleware.rb
105
109
  - lib/telegram/bot/railtie.rb
106
110
  - lib/telegram/bot/routes_helper.rb
107
111
  - lib/telegram/bot/updates_controller.rb
108
112
  - lib/telegram/bot/updates_controller/instrumentation.rb
109
113
  - lib/telegram/bot/updates_controller/log_subscriber.rb
114
+ - lib/telegram/bot/updates_controller/rspec_helpers.rb
115
+ - lib/telegram/bot/updates_controller/session.rb
116
+ - lib/telegram/bot/updates_controller/typed_update.rb
110
117
  - lib/telegram/bot/updates_poller.rb
111
118
  - lib/telegram/bot/version.rb
112
- - lib/telegram/bottable.rb
113
119
  - telegram-bot.gemspec
114
- homepage: https://github.com/printercu/telegram-bot
120
+ homepage: https://github.com/telegram-bot-rb/telegram-bot
115
121
  licenses:
116
122
  - MIT
117
123
  metadata: {}
@@ -1,41 +0,0 @@
1
- module Telegram
2
- module Bottable
3
- # Overwrite config.
4
- attr_writer :bots_config
5
-
6
- # Keep this setting here, so we can avoid loading Bot::UpdatesPoller
7
- # when polling is disabled.
8
- attr_writer :bot_poller_mode
9
-
10
- # It just tells routes helpers whether to add routed bots to
11
- # Bot::UpdatesPoller, so their config will be available by bot key in
12
- # Bot::UpdatesPoller.start.
13
- def bot_poller_mode?
14
- return @bot_poller_mode if defined?(@bot_poller_mode)
15
- Rails.env.development? if defined?(Rails)
16
- end
17
-
18
- # Hash of bots made with bots_config.
19
- def bots
20
- @bots ||= bots_config.transform_values(&Bot.method(:wrap))
21
- end
22
-
23
- # Default bot.
24
- def bot
25
- @bot ||= bots[:default]
26
- end
27
-
28
- # Returns config for .bots method. By default uses `telegram['bots']` section
29
- # from `secrets.yml` merging `telegram['bot']` at `:default` key.
30
- #
31
- # Can be overwritten with .bots_config=
32
- def bots_config
33
- return @bots_config if @bots_config
34
- telegram_config = Rails.application.secrets[:telegram]
35
- (telegram_config['bots'] || {}).symbolize_keys.tap do |config|
36
- default = telegram_config['bot']
37
- config[:default] = default if default
38
- end
39
- end
40
- end
41
- end