1
0
зеркало из https://github.com/glebtv/telegram-bot.git synced 2026-09-04 10:15:50 +03:00

Сравнить коммиты

..

8 Коммитов

Автор SHA1 Сообщение Дата
Max Melentiev
380d13f65a Update to Bot API 4.1 2018-09-03 08:57:10 +03:00
Max Melentiev
20427499c3 Read config from secrets when credentials don't provide it in rails >= 5.2 2018-06-27 09:47:26 +03:00
Max Melentiev
8936a7a96c Remove botan.io support
https://github.com/botanio/sdk#this-service-will-be-shut-down-on-25th-may-2018
2018-06-22 07:45:08 +03:00
printercu
e2e32e041a Merge pull request #94 from KonstantinReido/master
Changed README a bit
2018-06-22 07:34:40 +03:00
Konstantin Reido
543258fa11 Remove section about Botan.io because this service isn’t available anymore 2018-06-22 00:52:27 +03:00
Konstantin Reido
4bf5956b41 Add a note to README about credentials API appeared in rails 5.2 2018-06-22 00:30:11 +03:00
Konstantin Reido
8b6dc60aaa Fix typo in README 2018-06-22 00:25:20 +03:00
Max Melentiev
b6bef94081 Fix spelling in readme 2018-06-07 16:57:14 +06:00
16 изменённых файлов: 69 добавлений и 302 удалений

Просмотреть файл

@@ -1,5 +1,9 @@
# Unreleased # Unreleased
- Read config from secrets when credentials don't provide it in rails >= 5.2.
- Remove botan.io support. It's already shut down, so it should not be a braking change.
https://github.com/botanio/sdk#this-service-will-be-shut-down-on-25th-may-2018
# 0.14.0 # 0.14.0
- Make integration & controller specs consistent. - Make integration & controller specs consistent.

Просмотреть файл

@@ -4,7 +4,7 @@
[![Code Climate](https://codeclimate.com/github/telegram-bot-rb/telegram-bot/badges/gpa.svg)](https://codeclimate.com/github/telegram-bot-rb/telegram-bot) [![Code Climate](https://codeclimate.com/github/telegram-bot-rb/telegram-bot/badges/gpa.svg)](https://codeclimate.com/github/telegram-bot-rb/telegram-bot)
[![Build Status](https://travis-ci.org/telegram-bot-rb/telegram-bot.svg)](https://travis-ci.org/telegram-bot-rb/telegram-bot) [![Build Status](https://travis-ci.org/telegram-bot-rb/telegram-bot.svg)](https://travis-ci.org/telegram-bot-rb/telegram-bot)
__Breaking changes in v0.14!__ See [upgrading guide](https://github.com/telegram-bot-rb/telegram-bot/wiki/Upgrading-to-0.14). __Breaking changes in v0.14!__ See [upgrade guide](https://github.com/telegram-bot-rb/telegram-bot/wiki/Upgrading-to-0.14).
Tools for developing Telegram bots. Best used with Rails, but can be used in Tools for developing Telegram bots. Best used with Rails, but can be used in
[standalone app](https://github.com/telegram-bot-rb/telegram-bot/wiki/Not-rails-application). [standalone app](https://github.com/telegram-bot-rb/telegram-bot/wiki/Not-rails-application).
@@ -19,7 +19,7 @@ Package contains:
- Middleware and routes helpers for production env. - Middleware and routes helpers for production env.
- Poller with automatic source-reloader for development env. - Poller with automatic source-reloader for development env.
- Rake tasks to update webhook urls. - Rake tasks to update webhook urls.
- __[Async mode](#async-mode)__ for Telegram and/or Botan API. - __[Async mode](#async-mode)__.
Let the queue adapter handle network errors! Let the queue adapter handle network errors!
Here is sample [telegram_bot_app](https://github.com/telegram-bot-rb/telegram_bot_app) Here is sample [telegram_bot_app](https://github.com/telegram-bot-rb/telegram_bot_app)
@@ -55,9 +55,28 @@ require 'telegram/bot'
## Usage ## Usage
### Configuration
While clients can be instantiated explicitly, there is `Telegram.bots_config=` method
to configure app-wide clients, which are accessible via `Telegram.bots`.
It accepts hash of `{bot_id: bot_config}`, and there is special id `:default`
which is used for `Telegram.bot`.
```ruby
Telegram.bots_config = {
default: DEFAULT_BOT_TOKEN,
chat: {token: CHAT_BOT_TOKEN, username: 'chatbot'},
}
Telegram.bot.get_updates
Telegram.bot == Telegram.bots[:default] # true
Telegram.bots[:chat].send_message(...)
```
### Configuration in Rails app ### Configuration in Rails app
Add `telegram` section into `secrets.yml`: In Rails app `Telegram.bots_config` is read from `secrets.yml` automatically
from `telegram` section:
```yml ```yml
development: development:
@@ -79,6 +98,15 @@ development:
username: ChatBot username: ChatBot
``` ```
For Rails >= 5.2 `Telegram::Bot` searches for config first in credentials and then in secrets.
To use credentials as config store, add telegram section to credentials instead of secrets using
`rails credentials:edit`. In this case be aware of that [Rails may not load
credentials in dev environment by default](https://github.com/telegram-bot-rb/telegram-bot/issues/74#issuecomment-384205609).
I suggest not using Rails 5.2 credentials because it can lead to leakage of sesitive data
and it's more difficult to use in multiple environments. See
[secure_credentials](https://github.com/printercu/secure_credentials) gem for better option.
From now clients will be accessible with `Telegram.bots[:chat]` or `Telegram.bots[:auction]`. From now clients will be accessible with `Telegram.bots[:chat]` or `Telegram.bots[:auction]`.
Single bot can be accessed with `Telegram.bot` or `Telegram.bots[:default]`. Single bot can be accessed with `Telegram.bot` or `Telegram.bots[:default]`.
@@ -206,7 +234,7 @@ def edit_message(type, params = {}); end
#### Optional typecasting #### Optional typecasting
You can enable typecasting of `update` with `telegram-bot-types` by including You can enable typecasting of `update` with `telegram-bot-types` by including
`Telegram::Bot::UpdatesPoller::TypedUpdate`: `Telegram::Bot::UpdatesController::TypedUpdate`:
```ruby ```ruby
class Telegram::WebhookController < Telegram::Bot::UpdatesController class Telegram::WebhookController < Telegram::Bot::UpdatesController
@@ -494,48 +522,12 @@ While webhooks-mode is prefered, poller still can be used in production.
See [comparison and examples](https://github.com/telegram-bot-rb/telegram-bot/wiki/Deployment) See [comparison and examples](https://github.com/telegram-bot-rb/telegram-bot/wiki/Deployment)
for details. for details.
### Botan.io metrics
Initialize with `bot = Telegram::Bot::Client.new(token, botan: 'botan token')`
or just add `botan` key in `secrets.yml`:
```yml
telegram:
bot:
token: bot_token
botan: botan_token
```
Access to Botan client with `bot.botan`.
Use `bot.botan.track(event, uid, payload)` to track events.
There are some helpers for controllers in `Telegram::Bot::Botan::ControllerHelpers`:
```ruby
class Telegram::WebhookController < Telegram::Bot::UpdatesController
include Telegram::Bot::Botan::ControllerHelpers
# This will track with event: action_name & data: payload
before_action :botan_track_action
def smth(*)
# This will track event for current user only when botan is configured.
botan_track :my_event, custom_data
# or get access directly to botan client:
botan.track(...)
end
end
```
There is no stubbing for botan clients, so don't set botan token in tests.
### Async mode ### Async mode
There is built in support for async requests using ActiveJob. Without Rails There is built in support for async requests using ActiveJob. Without Rails
you can implement your own worker class to handle such requests. This allows: you can implement your own worker class to handle such requests. This allows:
- Process updates very fast, without waiting for telegram and botan responses. - Process updates very fast, without waiting for telegram responses.
- Handle and retry network and other errors with queue adapter. - Handle and retry network and other errors with queue adapter.
- ??? - ???
@@ -543,7 +535,7 @@ Instead of performing request instantly client serializes it, pushes to queue,
and immediately return control back. The job is then fetched with a worker and immediately return control back. The job is then fetched with a worker
and real API request is performed. And this all is absolutely transparent for the app. and real API request is performed. And this all is absolutely transparent for the app.
To enable this mode add `async: true` to bot's and botan's config. To enable this mode add `async: true` to bot's config.
For more information and custom configuration check out For more information and custom configuration check out
[docs](http://www.rubydoc.info/github/telegram-bot-rb/telegram-bot/master/Telegram/Bot/Async) or [docs](http://www.rubydoc.info/github/telegram-bot-rb/telegram-bot/master/Telegram/Bot/Async) or
[source](https://github.com/telegram-bot-rb/telegram-bot/blob/master/lib/telegram/bot/async.rb). [source](https://github.com/telegram-bot-rb/telegram-bot/blob/master/lib/telegram/bot/async.rb).

Просмотреть файл

@@ -22,7 +22,6 @@ module Telegram
end end
autoload :Async, 'telegram/bot/async' autoload :Async, 'telegram/bot/async'
autoload :Botan, 'telegram/bot/botan'
autoload :Client, 'telegram/bot/client' autoload :Client, 'telegram/bot/client'
autoload :ClientStub, 'telegram/bot/client_stub' autoload :ClientStub, 'telegram/bot/client_stub'
autoload :DebugClient, 'telegram/bot/debug_client' autoload :DebugClient, 'telegram/bot/debug_client'

Просмотреть файл

@@ -1,20 +1,15 @@
module Telegram module Telegram
module Bot module Bot
# Telegram & Botan clients can perform requests in async way with # Telegram clients can perform requests in async way with
# any job adapter (ActiveJob by default). Using Rails you don't need any # any job adapter (ActiveJob by default). Using Rails you don't need any
# additional configuration. However you may want to enable async requests # additional configuration. However you may want to enable async requests
# by default with `async: true` in `secrets.yml`. Botan client doesn't inherit # by default with `async: true` in `secrets.yml`.
# async setting from client and must be configured separately.
# #
# telegram: # telegram:
# bots: # bots:
# chat_async: # chat_async:
# token: secret # token: secret
# async: true # enable async mode for client # async: true # enable async mode for client
# botan: botan_token # in this way botan will not be async
# botan: # in this way - it's in async mode
# token: botan_token
# async: true
# #
# Without Rails To start using async requests # Without Rails To start using async requests
# initialize client with `id` kwarg and make sure the client is # initialize client with `id` kwarg and make sure the client is

Просмотреть файл

@@ -1,53 +0,0 @@
module Telegram
module Bot
class Botan
TRACK_URI = 'https://api.botan.io/track'.freeze
autoload :ClientHelpers, 'telegram/bot/botan/client_helpers'
autoload :ControllerHelpers, 'telegram/bot/botan/controller_helpers'
class Error < Bot::Error; end
extend Initializers
prepend Async
include DebugClient
class << self
def by_id(id)
Telegram.botans[id]
end
def prepare_async_args(method, uri, query = {}, body = nil)
[method.to_s, uri.to_s, Async.prepare_hash(query), body]
end
end
attr_reader :client, :token
def initialize(token = nil, **options)
@client = HTTPClient.new
@token = token || options[:token]
end
def track(event, uid, payload = {})
request(:post, TRACK_URI, {name: event, uid: uid}, payload.to_json)
end
def request(method, uri, query = {}, body = nil)
res = http_request(method, uri, query.merge(token: token), body)
status = res.status
return JSON.parse(res.body) if status < 300
result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
err_msg = "#{res.reason}: #{result && result['info'] || '-'}"
raise Error, err_msg
end
def http_request(method, uri, query, body)
client.request(method, uri, query, body)
end
def inspect
"#<#{self.class.name}##{object_id}(#{@id})>"
end
end
end
end

Просмотреть файл

@@ -1,15 +0,0 @@
module Telegram
module Bot
class Botan
# Helpers for botan.io metrics.
module ClientHelpers
attr_reader :botan
def initialize(*, botan: nil, **options)
super
@botan = Botan.wrap(botan, id: id) if botan
end
end
end
end
end

Просмотреть файл

@@ -1,33 +0,0 @@
module Telegram
module Bot
class Botan
# Helpers for botan.io metrics.
module ControllerHelpers
class MissingFrom < Error; end
protected
def botan
@botan ||= bot.try!(:botan)
end
# Track custom event for user taken from `from` field:
#
# botan_track :my_event, {data: :val}
#
def botan_track(event, data = {})
raise MissingFrom, 'Can not track without user' unless from
botan.try! { |x| x.track(event, from['id'], data) }
end
# Track current action and payload for current user. Best used with `before_action`:
#
# before_action :botan_track_action
#
def botan_track_action
botan_track(action_name, payload)
end
end
end
end
end

Просмотреть файл

@@ -9,7 +9,6 @@ module Telegram
autoload :TypedResponse, 'telegram/bot/client/typed_response' autoload :TypedResponse, 'telegram/bot/client/typed_response'
extend Initializers extend Initializers
prepend Async prepend Async
prepend Botan::ClientHelpers
include DebugClient include DebugClient
require 'telegram/bot/client/api_helper' require 'telegram/bot/client/api_helper'

Просмотреть файл

@@ -1,5 +1,5 @@
# Generated with bin/fetch-telegram-methods # Generated with bin/fetch-telegram-methods
# Bot API 3.5. # Bot API 4.1
getUpdates getUpdates
setWebhook setWebhook
@@ -13,6 +13,7 @@ sendPhoto
sendAudio sendAudio
sendDocument sendDocument
sendVideo sendVideo
sendAnimation
sendVoice sendVoice
sendVideoNote sendVideoNote
sendMediaGroup sendMediaGroup
@@ -46,6 +47,7 @@ answerCallbackQuery
editMessageText editMessageText
editMessageCaption editMessageCaption
editMessageMedia
editMessageReplyMarkup editMessageReplyMarkup
deleteMessage deleteMessage
@@ -63,6 +65,8 @@ sendInvoice
answerShippingQuery answerShippingQuery
answerPreCheckoutQuery answerPreCheckoutQuery
setPassportDataErrors
sendGame sendGame
setGameScore setGameScore
getGameHighScores getGameHighScores

Просмотреть файл

@@ -40,11 +40,6 @@ module Telegram
end end
end end
# Hash of botan clients made from #bots.
def botans
@botans ||= bots.map { |k, v| [k, v.botan] }.to_h
end
# Returns config for .bots method. By default uses `telegram['bots']` section # Returns config for .bots method. By default uses `telegram['bots']` section
# from `secrets.yml` merging `telegram['bot']` at `:default` key. # from `secrets.yml` merging `telegram['bot']` at `:default` key.
# #
@@ -53,10 +48,12 @@ module Telegram
@bots_config ||= @bots_config ||=
if defined?(Rails.application) if defined?(Rails.application)
app = Rails.application app = Rails.application
store = app.respond_to?(:credentials) ? app.credentials : app.secrets store = app.credentials[:telegram] if app.respond_to?(:credentials)
secrets = store.fetch(:telegram, {}).with_indifferent_access store ||= app.secrets[:telegram] if app.respond_to?(:secrets)
secrets.fetch(:bots, {}).symbolize_keys.tap do |config| store ||= {}
default = secrets[:bot] store = store.with_indifferent_access
store.fetch(:bots, {}).symbolize_keys.tap do |config|
default = store[:bot]
config[:default] = default if default config[:default] = default if default
end end
else else
@@ -69,7 +66,6 @@ module Telegram
@bots = nil @bots = nil
@bot = nil @bot = nil
@bots_config = nil @bots_config = nil
@botans = nil
end end
end end
end end

Просмотреть файл

@@ -1,6 +1,6 @@
module Telegram module Telegram
module Bot module Bot
VERSION = '0.14.0'.freeze VERSION = '0.14.1'.freeze
def self.gem_version def self.gem_version
Gem::Version.new VERSION Gem::Version.new VERSION

Просмотреть файл

@@ -1,33 +0,0 @@
RSpec.describe Telegram::Bot::Client do
let(:instance) { described_class.new(*client_args) }
let(:token) { 'token' }
let(:botan_token) { double(:botan_token) }
let(:client_id) { 'client_id' }
let(:client_args) { [token] }
describe '#botan' do
subject { instance.botan }
it { should eq nil }
context 'when botan token is set' do
let(:client_args) { [token, id: client_id, async: Class.new, botan: botan_token] }
it { should be_instance_of Telegram::Bot::Botan }
its(:token) { should eq botan_token }
its(:id) { should eq client_id }
it 'doesnt inherit async from client' do
expect(instance.async).to be
expect(subject.async).to_not be
end
end
context 'when botan is configured with hash' do
let(:client_args) { [token, id: client_id, async: Class.new, botan: botan_config] }
let(:botan_config) { {token: botan_token, async: Class.new} }
it { should be_instance_of Telegram::Bot::Botan }
its(:token) { should eq botan_token }
its(:id) { should eq client_id }
its(:async) { should eq botan_config[:async] }
end
end
end

Просмотреть файл

@@ -1,54 +0,0 @@
RSpec.describe Telegram::Bot::Botan::ControllerHelpers do
include_context 'telegram/bot/updates_controller'
let(:controller_class) do
described_class = self.described_class
Class.new(Telegram::Bot::UpdatesController) do
include described_class
end
end
let(:botan) { double(:botan) }
let(:result) { double(:result) }
let(:payload) { {from: {id: user_id}} }
let(:payload_type) { :message }
let(:user_id) { double(:user_id) }
before { allow(bot).to receive(:botan) { botan } }
shared_examples 'basic tracking' do
context 'when botan is not configured' do
let(:botan) {}
it { should eq nil }
end
context 'when `from` is empty' do
let(:payload) { {text: 'test'} }
it { expect { subject }.to raise_error described_class::MissingFrom }
end
end
describe '#botan_track' do
subject { controller.send(:botan_track, event, data) }
let(:event) { double(:event) }
let(:data) { double(:data) }
it 'calls botan#track' do
expect(botan).to receive(:track).with(event, user_id, data) { result }
should eq result
end
include_examples 'basic tracking'
end
describe '#botan_track_action' do
subject { controller.send(:botan_track_action) }
let(:action_name) { double(:action_name) }
it 'calls botan#track with current action and payload' do
expect(controller).to receive(:action_name) { action_name }
expect(botan).to receive(:track).
with(action_name, user_id, deep_stringify(payload)) { result }
should eq result
end
include_examples 'basic tracking'
end
end

Просмотреть файл

@@ -1,27 +0,0 @@
RSpec.describe Telegram::Bot::Botan do
let(:instance) { described_class.new 'token' }
let(:token) { 'token' }
include_examples 'initializers', :botans
it_behaves_like 'async', request_args: -> { [double(:method), double(:url)] }
describe '.new' do
subject { described_class.new(*args) }
context 'when usual args are given' do
let(:args) { ['secret'] }
its(:token) { should eq args[0] }
end
context 'when options are given' do
let(:args) { [token: 'secret'] }
its(:token) { should eq args[0][:token] }
end
end
describe '.prepare_async_args' do
subject { described_class.prepare_async_args(*input) }
let(:input) { [:post, :uri, {a: 1, b: :sym, 'd' => 'str'}, 'body'] }
it { should eq ['post', 'uri', {a: 1, b: 'sym', 'd' => 'str'}, 'body'] }
end
end

Просмотреть файл

@@ -1,7 +1,6 @@
RSpec.describe Telegram::Bot::Client do RSpec.describe Telegram::Bot::Client do
let(:instance) { described_class.new 'token' } let(:instance) { described_class.new 'token' }
let(:token) { 'token' } let(:token) { 'token' }
let(:botan_token) { double(:botan_token) }
include_examples 'initializers' include_examples 'initializers'
it_behaves_like 'async', request_args: -> { [double(:action), {body: :content}] } it_behaves_like 'async', request_args: -> { [double(:action), {body: :content}] }

Просмотреть файл

@@ -11,12 +11,10 @@ RSpec.describe Telegram::Bot::ConfigMethods do
chat: { chat: {
token: 'chat_token', token: 'chat_token',
username: 'Chat', username: 'Chat',
botan: 'chat_botan_token',
}, },
other_chat: { other_chat: {
'token' => 'other_chat_token', 'token' => 'other_chat_token',
'username' => 'OtherChat', 'username' => 'OtherChat',
'botan' => 'other_chat_botan_token',
}, },
} }
end end
@@ -43,7 +41,6 @@ RSpec.describe Telegram::Bot::ConfigMethods do
its(:id) { should eq :chat } its(:id) { should eq :chat }
its(:token) { should eq config[:chat][:token] } its(:token) { should eq config[:chat][:token] }
its(:username) { should eq config[:chat][:username] } its(:username) { should eq config[:chat][:username] }
its('botan.token') { should eq config[:chat][:botan] }
end end
context 'configured by hash with stringified keys' do context 'configured by hash with stringified keys' do
@@ -51,18 +48,6 @@ RSpec.describe Telegram::Bot::ConfigMethods do
its(:id) { should eq :other_chat } its(:id) { should eq :other_chat }
its(:token) { should eq config[:other_chat]['token'] } its(:token) { should eq config[:other_chat]['token'] }
its(:username) { should eq config[:other_chat]['username'] } its(:username) { should eq config[:other_chat]['username'] }
its('botan.token') { should eq config[:other_chat]['botan'] }
end
end
describe '#botans' do
subject { registry.botans }
it do
should eq(
default: nil,
chat: registry.bots[:chat].botan,
other_chat: registry.bots[:other_chat].botan,
)
end end
end end
@@ -76,13 +61,14 @@ RSpec.describe Telegram::Bot::ConfigMethods do
it { should eq({}) } it { should eq({}) }
context 'in rails environment' do context 'in rails environment' do
before { stub_const('Rails', double(application: double(secrets: secrets))) } before { stub_const('Rails', double(application: double(app_stub))) }
let(:app_stub) { {secrets: secrets} }
let(:secrets) { {} } let(:secrets) { {} }
it { should eq({}) } it { should eq({}) }
context 'when there is telegram section in secrets' do context 'when there is telegram section in secrets' do
let(:secrets) { {telegram: config.stringify_keys} } let(:secrets) { {telegram: secrets_config} }
let(:config) do let(:secrets_config) do
{ {
bot: double(:bot_config), bot: double(:bot_config),
bots: { bots: {
@@ -91,13 +77,21 @@ RSpec.describe Telegram::Bot::ConfigMethods do
}, },
} }
end end
it { should include default: config[:bot] } it { should include default: secrets_config[:bot] }
it { should include config[:bots] } it { should include secrets_config[:bots] }
context 'on rails >5.1 (deep symbolized keys)' do context 'on rails >5.1 (deep symbolized keys)' do
let(:secrets) { {telegram: config.deep_symbolize_keys} } let(:secrets) { super().deep_symbolize_keys }
it { should include default: config[:bot] } it { should include default: secrets_config[:bot] }
it { should include config[:bots] } it { should include secrets_config[:bots] }
end
context 'and credentials (>= 5.2)' do
let(:app_stub) { super().merge(credentials: credentials) }
let(:credentials) { {telegram: credentials_config} }
let(:credentials_config) { {bot: double(:credentials_bot_config)} }
it { should include default: credentials_config[:bot] }
it { should_not include secrets[:bots] }
end end
end end
end end