1
0
зеркало из https://github.com/glebtv/telegram-bot.git synced 2026-08-28 15:26:18 +03:00

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

..

16 Коммитов

Автор SHA1 Сообщение Дата
Max Melentiev
6abbe5489d v0.12.0 2017-08-11 15:04:41 +03:00
Max Melentiev
ff7de976d0 Add specs for #edit_message 2017-08-11 15:04:19 +03:00
Max Melentiev
aaba27eb70 New API methods and payload types (up to Bot API 3.2) 2017-08-11 15:04:02 +03:00
printercu
88a4a9aab3 Merge pull request #37 from zuf/master
update rails dependency (<5.2 instead <=5.1)
2017-08-11 13:43:13 +03:00
Egor
9387043ec1 fix rails dependency (<5.2 instead <=5.1) 2017-08-10 18:07:21 +03:00
Max Melentiev
3dca35ba9a callback_query context for RSpec 2017-07-25 10:58:18 +03:00
Max Melentiev
6879618c00 edit_message helper 2017-07-25 10:49:39 +03:00
Max Melentiev
6e4a69b6b1 Fix storing token in ClientStub when config hash is given 2017-05-17 17:45:21 +06:00
printercu
b8db576452 Merge pull request #31 from telegram-bot-rb/integration_test
Integration test with rails app and two bots
2017-05-15 22:20:26 +06:00
Max Melentiev
2fffdeb475 Integration test with rails app and two bots 2017-05-15 22:11:52 +06:00
Max Melentiev
9defa42f40 Add notes on testing multiple bots to readme
Fixes #29
2017-05-11 14:01:42 +06:00
Max Melentiev
0320507abe Telegram.bot raises error when there is no default config 2017-05-11 13:57:29 +06:00
Max Melentiev
cd3d3739a4 v0.11.3 2017-05-05 17:30:49 +06:00
Max Melentiev
a054ac704c Build with rails 5.1 on travis 2017-05-05 17:28:59 +06:00
printercu
23aa92e614 Merge pull request #28 from ironbrains/master
Increase dependencies version
2017-05-05 17:19:05 +06:00
Bulat F
a49dca66fa Increase dependencies version
- Add support `activesupport 5.1`
- Add support `actionpack 5.1`
2017-05-05 10:16:30 +03:00
22 изменённых файлов: 306 добавлений и 27 удалений

1
.gitignore поставляемый
Просмотреть файл

@@ -6,4 +6,5 @@
/doc/
/pkg/
/spec/reports/
/log/
/tmp/

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

@@ -5,5 +5,6 @@ rvm:
env:
- RAILS=4
- RAILS=5
- RAILS=5_1
notifications:
email: false

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

@@ -1,3 +1,18 @@
# Unreleased
# 0.12.0
- New API methods and payload types (up to Bot API 3.2).
- Fix rails 5.1.x support.
- RSpec context for callback_query updates.
- `edit_message` helper.
- ClientStub saves and returns token. Fixes testing multiple bots.
- Raise descriptive error when accessing not-configured bot.
# 0.11.3
- Release dependencies for Rails 5.1.
# 0.11.2
- Rails 5.1 deep symbolized secrets support.

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

@@ -1,14 +1,22 @@
source 'https://rubygems.org'
gemspec
case ENV['RAILS']
when '5'
gem 'actionpack', '5.0.0.rc1'
when '4'
gem 'actionpack', '~> 4.2'
end
group :development do
case ENV['RAILS']
when '5_1'
gem 'railties', '5.1.0'
gem 'actionpack', '5.1.0'
when '5'
gem 'railties', '5.0.2'
gem 'actionpack', '5.0.2'
when '4'
gem 'railties', '~> 4.2'
gem 'actionpack', '~> 4.2'
else
gem 'railties'
gem 'actionpack'
end
gem 'sdoc', '~> 0.4.1'
gem 'pry', '~> 0.10.1'
gem 'pry-byebug', '~> 3.2.0'
@@ -17,6 +25,7 @@ group :development do
gem 'rspec', '~> 3.5.0'
gem 'rspec-its', '~> 1.1.0'
gem 'rspec-rails', '~> 3.5.0'
gem 'rubocop', '~> 0.37.0'

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

@@ -27,6 +27,8 @@ Run it on your local machine in 1 minute!
And here is [app teamplate](https://github.com/telegram-bot-rb/rails_template)
to generate clean app in seconds.
Examples and cookbook in [wiki](https://github.com/telegram-bot-rb/telegram-bot/wiki).
## Installation
Add this line to your application's Gemfile:
@@ -166,6 +168,20 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
end
```
#### Reply helpers
There are helpers to respond for basic actions. They just set chat/message/query
identifiers from update. See `ReplyHelpers` method for more information.
Here are this methods signatures:
```ruby
def respond_with(type, params); end
def reply_with(type, params); end
def answer_inline_query(results, params = {}); end
def answer_callback_query(text, params = {}); end
def edit_message(type, params = {}); end
```
#### Optional typecasting
You can enable typecasting of `update` with `telegram-bot-types` by including
@@ -341,6 +357,8 @@ Telegram::Bot::ClientStub.stub_all!
RSpec.configure do |config|
# ...
config.after { Telegram.bot.reset }
# or for multiple bots:
config.after { Telegram.bots.each_value(&:reset) }
# ...
end
```
@@ -359,6 +377,13 @@ RSpec.describe TelegramWebhooksController, :telegram_bot do
subject { -> { dispatch_command :start } }
it { should respond_with_message 'Hi there!' }
end
# There is context for callback queries with related matchers.
describe '#hey_callback_query', :callback_query do
let(:data) { "hey:#{name}" }
let(:name) { 'Joe' }
it { should answer_callback_query('Hey Joe') }
it { should edit_current_message :text, text: 'Done' }
end
# For controller specs use
@@ -376,6 +401,10 @@ expect(&process_update).
to make_telegram_request(bot, :sendMessage, hash_including(text: 'msg text'))
```
Place integration tests inside `spec/requests`
when using RSpec's `infer_spec_type_from_file_location!`,
or just add `type: :request` to `describe`.
See sample app for more examples.
### Deploying

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

@@ -5,7 +5,7 @@ require 'active_support/core_ext/hash/keys'
module Telegram
module Bot
class Client
class Client # rubocop:disable ClassLength
URL_TEMPLATE = 'https://api.telegram.org/bot%s/'.freeze
autoload :TypedResponse, 'telegram/bot/client/typed_response'
@@ -70,6 +70,8 @@ module Telegram
setWebhook
answerCallbackQuery
deleteChatPhoto
exportChatInviteLink
forwardMessage
getChat
getChatAdministrators
@@ -80,6 +82,9 @@ module Telegram
getUserProfilePhotos
kickChatMember
leaveChat
pinChatMessage
promoteChatMember
restrictChatMember
sendAudio
sendChatAction
sendContact
@@ -87,18 +92,35 @@ module Telegram
sendLocation
sendMessage
sendPhoto
sendSticker
sendVenue
sendVideo
sendVideoNote
sendVoice
setChatDescription
setChatPhoto
setChatTitle
unbanChatMember
unpinChatMessage
deleteMessage
editMessageCaption
editMessageReplyMarkup
editMessageText
sendSticker
getStickerSet
uploadStickerFile
createNewStickerSet
addStickerToSet
setStickerPositionInSet
deleteStickerFromSet
answerInlineQuery
sendInvoice
answerShippingQuery
answerPreCheckoutQuery
getGameHighScores
sendGame
setGameScore

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

@@ -35,6 +35,7 @@ module Telegram
end
def initialize(token = nil, username = nil, **options)
@token = token || options[:token]
@username = username || options[:username] || token
reset
end

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

@@ -34,7 +34,11 @@ module Telegram
# Default bot.
def bot
@bot ||= bots[:default]
@bot ||= bots.fetch(:default) do
raise 'Default bot is not configured.' \
' Add :default to bots_config' \
' or use telegram.bot/telegram.bots.default section in secrets.yml.'
end
end
# Hash of botan clients made from #bots.

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

@@ -14,7 +14,7 @@ RSpec.shared_context 'telegram/bot/integration' do
}
end
let(:clear_session?) { described_class.respond_to?(:session_store) }
before { described_class.session_store.clear if clear_session? }
before { described_class.session_store.try!(:clear) if clear_session? }
include Telegram::Bot::RSpec::ClientMatchers
@@ -36,14 +36,48 @@ RSpec.shared_context 'telegram/bot/integration' do
end
# Matcher to check response. Make sure to define `let(:chat_id)`.
def respond_with_message(expected)
def respond_with_message(expected = Regexp.new(''))
raise 'Define chat_id to use respond_with_message' unless defined?(chat_id)
send_telegram_message(bot, expected, chat_id: chat_id)
end
end
RSpec.shared_context 'telegram/bot/callback_query', callback_query: true do
include_context 'telegram/bot/integration'
subject { -> { dispatch callback_query: payload } }
let(:payload) { {id: 11, from: from, message: message, data: data} }
let(:message) { {message_id: 22, chat: chat, text: 'message text'} }
# Matcher to check that origin message got edited.
def edit_current_message(type, options = {})
description = 'edit current message'
options = options.merge(
message_id: message[:message_id],
chat_id: chat_id,
)
Telegram::Bot::RSpec::ClientMatchers::MakeTelegramRequest.new(
bot, :"editMessage#{type.to_s.camelize}", description: description
).with(hash_including(options))
end
# Matcher to check that callback query is answered.
def answer_callback_query(text = Regexp.new(''), options = {})
description = "answer callback query with #{text.inspect}"
text = a_string_matching(text) if text.is_a?(Regexp)
options = options.merge(
callback_query_id: payload[:id],
text: text,
)
Telegram::Bot::RSpec::ClientMatchers::MakeTelegramRequest.new(
bot, :answerCallbackQuery, description: description
).with(hash_including(options))
end
end
RSpec.configure do |config|
if config.respond_to?(:include_context)
config.include_context 'telegram/bot/integration', :telegram_bot
config.include_context 'telegram/bot/callback_query', :telegram_bot, :callback_query
end
end

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

@@ -86,6 +86,8 @@ module Telegram
inline_query
chosen_inline_result
callback_query
shipping_query
pre_checkout_query
).freeze
CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)")
@@ -169,15 +171,21 @@ module Telegram
# Returns array `[is_command?, action, args]`.
def action_for_payload
if payload_type
send("action_for_#{payload_type}") || [false, payload_type, [payload]]
send("action_for_#{payload_type}") || action_for_default_payload
else
[false, :unsupported_payload_type, []]
end
end
def action_for_default_payload
[false, payload_type, [payload]]
end
# If payload is a message with command, then returned action is an
# action for this command.
# Separate method, so it can be easily overriden (ex. MessageContext).
#
# This is not used for edited messages/posts. It process them as basic updates.
def action_for_message
cmd, args = self.class.command_from_text(payload['text'], bot_username)
cmd &&= self.class.action_for_command(cmd)
@@ -185,12 +193,6 @@ module Telegram
end
alias_method :action_for_channel_post, :action_for_message
# It doesn't extract commands from edited messages. Just process
# them as usual ones.
def action_for_edited_message
end
alias_method :action_for_edited_channel_post, :action_for_edited_message
def action_for_inline_query
[false, payload_type, [payload['query'], payload['offset']]]
end
@@ -208,6 +210,11 @@ module Telegram
def action_missing(*)
end
PAYLOAD_TYPES.each do |type|
method = :"action_for_#{type}"
alias_method method, :action_for_default_payload unless instance_methods.include?(method)
end
ActiveSupport.run_load_hooks('telegram.bot.updates_controller', self)
end
end

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

@@ -15,7 +15,6 @@ module Telegram
# because `data` param is controlled by client.
def action_for_callback_query
context, new_data = context_from_callback_query
# binding.pry
if context
action_name = "#{context}_callback_query"
[false, action_name, [new_data]] if action_method?(action_name)

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

@@ -38,6 +38,19 @@ module Telegram
)
bot.answer_callback_query(params)
end
# Edit message from callback query.
def edit_message(type, params = {})
params =
if message_id = payload['inline_message_id'] # rubocop:disable AssignmentInCondition
params.merge(inline_message_id: message_id)
elsif message = payload['message'] # rubocop:disable AssignmentInCondition
params.merge(chat_id: message['chat']['id'], message_id: message['message_id'])
else
raise 'Can not edit message without `inline_message_id` or `message`'
end
bot.public_send("edit_message_#{type}", params)
end
end
end
end

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

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

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

@@ -0,0 +1,8 @@
require 'integration_helper'
RSpec.describe DefaultBotController, :telegram_bot, type: :request do
describe '#start' do
subject { -> { dispatch_command :start } }
it { should respond_with_message 'from default' }
end
end

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

@@ -0,0 +1,9 @@
require 'integration_helper'
RSpec.describe NamedBotController, :telegram_bot, type: :request do
let(:bot) { Telegram.bots[:named] }
describe '#start' do
subject { -> { dispatch_command :start } }
it { should respond_with_message 'from named' }
end
end

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

@@ -0,0 +1,9 @@
require 'integration_helper'
RSpec.describe OtherBotController, :telegram_bot, type: :request do
let(:bot) { Telegram.bots[:other] }
describe '#start' do
subject { -> { dispatch_command :start } }
it { should respond_with_message 'from other' }
end
end

62
spec/integration_helper.rb Обычный файл
Просмотреть файл

@@ -0,0 +1,62 @@
require 'telegram/bot/rspec/integration'
require 'action_controller'
require 'action_dispatch'
require 'action_dispatch/testing/integration'
require 'rails'
require 'rspec/rails/adapters'
require 'rspec/rails/fixture_support'
require 'rspec/rails/example/rails_example_group'
require 'rspec/rails/example/request_example_group'
ENV['RAILS_ENV'] = 'test'
class TestApplication < Rails::Application
config.eager_load = false
config.log_level = :debug
secrets[:secret_key_base] = 'test'
secrets[:telegram] = {
bot: 'default_token',
bots: {
other: {token: 'other_token'},
named: {token: 'named_token', username: 'TestBot'},
},
}
end
Rails.application.initialize!
# # Controllers
%w(default other named).each do |bot_name|
controller = Class.new(Telegram::Bot::UpdatesController) do
define_method :start do |*|
respond_with :message, text: "from #{bot_name}"
end
end
Object.const_set("#{bot_name}_bot_controller".camelize, controller)
end
RSpec.configure do |config|
config.include RSpec::Rails::RequestExampleGroup, type: :request
config.around type: :request do |ex|
begin
Telegram.reset_bots
Telegram::Bot::ClientStub.stub_all!
ex.run
ensure
Telegram.reset_bots
Telegram::Bot::ClientStub.stub_all!(false)
end
end
config.before type: :request do
# Redefine routes before every example, so it does not depent on order.
Rails.application.routes.draw do
require 'telegram/bot/routes_helper'
extend Telegram::Bot::RoutesHelper
telegram_webhooks default: DefaultBotController,
other: OtherBotController,
named: NamedBotController
end
end
end

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

@@ -46,8 +46,14 @@ RSpec.describe Telegram::Bot::ClientStub do
context 'when username and token are given' do
let(:args) { %w(token superbot) }
its(:token) { should eq nil }
its(:token) { should eq args[0] }
its(:username) { should eq args[1] }
end
context 'when hash config is given' do
let(:args) { [token: 'token', username: 'superbot'] }
its(:token) { should eq args[0][:token] }
its(:username) { should eq args[0][:username] }
end
end
end

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

@@ -22,8 +22,13 @@ RSpec.describe Telegram::Bot::ConfigMethods do
end
describe '#bot' do
subject { registry.bot }
it { should eq registry.bots[:default] }
subject { -> { registry.bot } }
its(:call) { should eq registry.bots[:default] }
context 'when bot is not configured' do
let(:config) { super().except(:default) }
it { should raise_error(/bot is not configured/) }
end
end
describe '#bots' do
@@ -67,6 +72,7 @@ RSpec.describe Telegram::Bot::ConfigMethods do
context 'when not configured' do
let(:registry) { Object.new.tap { |x| x.extend described_class } }
before { hide_const('Rails') }
it { should eq({}) }
context 'in rails environment' do

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

@@ -48,4 +48,32 @@ RSpec.describe Telegram::Bot::UpdatesController do
end
end
end
describe '#edit_message' do
subject { controller.edit_message(type, params) }
let(:type) { :reply_markup }
it { expect { subject }.to raise_error(/Can not edit message without/) }
context 'when inline_message_id is present' do
let(:payload) { {inline_message_id: double(:message_id)} }
it 'sets inline_message_id' do
expect(bot).to receive("edit_message_#{type}").with(params.merge(
inline_message_id: payload[:inline_message_id],
)) { result }
should eq result
end
end
context 'when message is present' do
let(:payload) { {message: super().merge(chat: chat)} }
it 'sets chat_id & message_id' do
expect(bot).to receive("edit_message_#{type}").with(params.merge(
message_id: payload[:message][:message_id],
chat_id: payload[:message][:chat]['id'],
)) { result }
should eq result
end
end
end
end

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

@@ -108,7 +108,7 @@ RSpec.describe Telegram::Bot::UpdatesController do
end
%w(message channel_post).each do |type|
context 'when payload is edited_message' do
context "when payload is edited_#{type}" do
let(:payload_type) { "edited_#{type}" }
it { should eq [false, payload_type, [payload]] }
end
@@ -142,6 +142,22 @@ RSpec.describe Telegram::Bot::UpdatesController do
end
end
end
custom_payload_types = %w(
message
edited_message
channel_post
edited_channel_post
inline_query
chosen_inline_result
callback_query
)
(described_class::PAYLOAD_TYPES - custom_payload_types).each do |type|
context "when payload is #{type}" do
let(:payload_type) { type }
it { should eq [false, payload_type, [payload]] }
end
end
end
context 'when `update` is a virtus model' do

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

@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = '~> 2.0'
spec.add_dependency 'activesupport', '>= 4.0', '< 5.1'
spec.add_dependency 'actionpack', '>= 4.0', '< 5.1'
spec.add_dependency 'activesupport', '>= 4.0', '< 5.2'
spec.add_dependency 'actionpack', '>= 4.0', '< 5.2'
spec.add_dependency 'httpclient', '~> 2.7'
spec.add_development_dependency 'bundler', '~> 1.11'
spec.add_development_dependency 'rake', '~> 10.0'