telegram-bot 0.3.0 → 0.4.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
  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