Skip to content
This repository was archived by the owner on Sep 14, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ mkmf.log
*.iml
*.ipr
*.iws
.ruby-version
.ruby-gemset
.rvmrc
.versions.conf
38 changes: 17 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@ require 'telegram_bot'

bot = TelegramBot.new(token: '[YOUR TELEGRAM BOT TOKEN GOES HERE]')
bot.get_updates(fail_silently: true) do |message|
puts "@#{message.from.username}: #{message.text}"
command = message.get_command_for(bot)
msg_text = message.text
puts "@#{message.from.username}: #{msg_text}"

message.reply do |reply|
case command
case msg_text
when /greet/i
reply.text = "Hello, #{message.from.first_name}!"
reply[:text] = "Hello, #{message.from.first_name}!"
else
reply.text = "#{message.from.first_name}, have no idea what #{command.inspect} means."
reply[:text] = "#{message.from.first_name}, have no idea what #{msg_text.inspect} means."
end
puts "sending #{reply.text.inspect} to @#{message.from.username}"
reply.send_with(bot)
puts "sending #{reply[:text].inspect} to @#{message.from.username}"
bot.send_message(**reply)
end
end
```
Expand Down Expand Up @@ -96,37 +96,33 @@ message.from.first_name # "Homer"
message.from.last_name # "Simpson"
message.from.username # "mr_x"

# channel
message.channel.id # 123123123 (telegram's id)
# chat
message.chat.id # 123123123 (telegram's id)

# reply
message.reply do |reply|
reply.text = "homer please clean the garage"
reply.send_with(bot)
reply[:text] = "homer please clean the garage"
bot.send_message(**reply)
end
# or
reply = message.reply
reply.text = "i'll do it after going to moe's"
bot.send_message(reply)
reply[:text] = "i'll do it after going to moe's"
bot.send_message(**reply)
```

To send message to specific channel you could do following:
To send message to specific chat you could do following:

```ruby
bot = TelegramBot.new(token: '[YOUR TELEGRAM BOT TOKEN GOES HERE]')
channel = TelegramBot::Channel.new(id: channel_id)
message = TelegramBot::OutMessage.new
message.chat = channel
message.text = 'Some message'

message.send_with(bot)
message = {chat_id: chat_id, text: 'Some message'}
bot.send_message(**message)

```

Also you may pass additional options described in [API Docs](https://core.telegram.org/bots/api#sendmessage)

```ruby
message.parse_mode = 'Markdown'
message[:parse_mode] = 'Markdown'
```

## Contributing
Expand Down
8 changes: 4 additions & 4 deletions example/bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
logger.debug "starting telegram bot"

bot.get_updates(fail_silently: true) do |message|
logger.info "@#{message.from.username}: #{message.text}"
command = message.get_command_for(bot)
msg_text = message.text
logger.info "@#{message.from.username}: #{msg_text}"

message.reply do |reply|
case command
case msg_text
when /greet/i
reply.text = "Hello, #{message.from.first_name}!"
else
reply.text = "#{message.from.first_name}, have no idea what #{command.inspect} means."
reply.text = "#{message.from.first_name}, have no idea what #{msg_text.inspect} means."
end
logger.info "sending #{reply.text.inspect} to @#{message.from.username}"
reply.send_with(bot)
Expand Down
2 changes: 1 addition & 1 deletion fixtures/vcr_cassettes/get_me.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions fixtures/vcr_cassettes/integration_test.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 20 additions & 26 deletions lib/telegram_bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,28 @@
require 'virtus'
require 'json'

require "telegram_bot/version"
require "telegram_bot/result"
require "telegram_bot/null_logger"
require "telegram_bot/user"
require "telegram_bot/group_chat"
require "telegram_bot/channel"
require "telegram_bot/message"
require "telegram_bot/keyboard"
require "telegram_bot/reply_keyboard_hide"
require "telegram_bot/reply_keyboard_markup"
require "telegram_bot/force_replay"
require "telegram_bot/out_message"
require "telegram_bot/update"
require "telegram_bot/api_response"
require "telegram_bot/bot"
require "telegram_bot/client"


module TelegramBot
def self.new(opts)
# compatibility with just passing a token
if opts.is_a?(String)
opts = { token: opts }
end

opts[:logger] ||= NullLogger.new
opts[:client] ||= Client.new(token: opts.fetch(:token), logger: opts[:logger], proxy: opts[:proxy])
{
Version: "version",
Result: "result",
NullLogger: "null_logger",
User: "user",
Chat: "chat",
MessageEntity: "message_entity",
Message: "message",
Keyboard: "keyboard",
ReplyKeyboardHide: "reply_keyboard_hide",
ReplyKeyboardMarkup: "reply_keyboard_markup",
ForceReplay: "force_replay",
Update: "update",
ApiResponse: "api_response",
Bot: "bot",
Connection: "connection",
}.each do |key, val|
autoload(key, "telegram_bot/#{val}")
end

def self.new(opts)
Bot.new(opts)
end
end
106 changes: 73 additions & 33 deletions lib/telegram_bot/bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,24 @@ def to_h
end

class Bot
ENDPOINT = 'https://api.telegram.org/'
attr_reader :connection

def initialize(opts = {})
# compatibility with just passing a token
opts = {token: opts} if opts.is_a?(String)

@token = opts.fetch(:token)
@base_path = "/bot#{@token}"
@offset = opts[:offset] || 0
@logger = opts.fetch(:logger)
@client = opts.fetch(:client)
@logger = opts[:logger] || NullLogger.new
@connection = Connection.new(ENDPOINT, persistent: true, proxy: opts[:proxy])
end

def get_me
@me ||= @client
.request(:getMe)
.and_then do |result|
User.new(result)
end
@me ||= @connection
.get(path: "#{@base_path}/getMe")
.and_then { |result| User.new(result) }
.value!
end
alias_method :me, :get_me
Expand All @@ -41,6 +47,7 @@ def get_updates(opts = {}, &block)
logger.info "starting get_updates loop"
loop do
messages = get_last_messages(opts)
opts[:offset] = @offset
messages.compact.each do |message|
next unless message
logger.info "message from @#{message.chat.friendly_name}: #{message.text.inspect}"
Expand All @@ -49,44 +56,77 @@ def get_updates(opts = {}, &block)
end
end

def send_message(out_message)
logger.info "sending message: #{out_message.text.inspect} to #{out_message.chat_friendly_name}"
@client
.request(:sendMessage, out_message)
.and_then do |result|
Message.new(result)
end
.value!
# send_message(chat_id:, text:, parse_mode: nil, disable_web_page_preview: nil, **kwargs)
def send_message(*args, **kwargs)
chat_id = kwargs.fetch(:chat_id) { args.fetch(0) }
text = kwargs.fetch(:text) { args.fetch(1) }
parse_mode = kwargs.fetch(:parse_mode) { args[2] }
disable_web_page_preview = kwargs.fetch(:disable_web_page_preview) { args[3] }

logger.info "sending message: #{text.inspect}"
data = {text: text, chat_id: chat_id}
data[:parse_mode] = parse_mode unless parse_mode.nil?
data[:disable_web_page_preview] = disable_web_page_preview unless disable_web_page_preview.nil?

args.shift(4)
args.unshift("#{@base_path}/sendMessage", data)
Message.new(post_message(*args, **kwargs))
end

def set_webhook(url, allowed_updates: %i(message))
logger.info "setting webhook url to #{url}, allowed_updates: #{allowed_updates}"
request = WebhookRequest.new(url: url, allowed_updates: allowed_updates)
@client.request(:setWebhook, request)
webhook_request = WebhookRequest.new(url: url, allowed_updates: allowed_updates)
post_message(path: "#{@base_path}/setWebhook", data: webhook_request.to_h)
end

def remove_webhook
set_webhook("")
end

private
attr_reader :logger

def get_last_updates(opts = {})
opts[:offset] ||= @offset
request = UpdatesRequest.new(opts)
response = @client.request(:getUpdates, request)
if opts[:fail_silently] && !response.ok?
logger.warn "error when getting updates. ignoring due to fail_silently."
return []
attr_reader :logger

def get_last_updates(opts = {})
opts[:offset] ||= @offset
updates_request = UpdatesRequest.new(opts)
path = "#{@base_path}/getUpdates"
response = @connection.get(path: path, query: updates_request.to_h)
if opts[:fail_silently] && !response.ok?
logger.warn "error when getting updates. ignoring due to fail_silently."
return []
end
updates = response.value!.compact.map { |raw_update| Update.new(raw_update) }
@offset = updates.last.id + 1 if updates.any?
updates
end
updates = response.value!.compact.map{|raw_update| Update.new(raw_update) }
@offset = updates.last.id + 1 if updates.any?
updates
end

def get_last_messages(opts = {})
get_last_updates(opts).map(&:message)
end
def get_last_messages(opts = {})
get_last_updates(opts).map(&:get_message)
end

# post_message(path:, data: {}, disable_notification: nil, reply_to_message_id: nil, content_type: nil)
def post_message(*args, **kwargs)
path = kwargs.fetch(:path) { args.fetch(0) }
data = kwargs.fetch(:data) { args.fetch(1, {}) }
disable_notification = kwargs.fetch(:disable_notification) { args[2] }
reply_to_message_id = kwargs.fetch(:reply_to_message_id) { args[3] }
content_type = kwargs.fetch(:content_type) { args[4] }

data[:disable_notification] = disable_notification unless disable_notification.nil?
data[:reply_to_message_id] = reply_to_message_id unless reply_to_message_id.nil?

if content_type.nil?
content_type = "application/x-www-form-urlencoded"
data = URI.encode_www_form(data)
else
content_type = content_type.downcase
end

if content_type == "application/json"
data = JSON.dump(data)
end

@connection.post(path: path, body: data, headers: {"Content-Type" => content_type}).value!
end
end
end
12 changes: 0 additions & 12 deletions lib/telegram_bot/channel.rb

This file was deleted.

Loading