tgbot 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +42 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/example.rb +34 -0
- data/lib/methods.json +1 -0
- data/lib/tgbot.rb +5 -0
- data/lib/tgbot/core.rb +145 -0
- data/lib/tgbot/dsl.rb +54 -0
- data/lib/tgbot/runner.rb +46 -0
- data/lib/tgbot/update.rb +76 -0
- data/lib/tgbot/version.rb +3 -0
- data/lib/types.json +1 -0
- data/tgbot.gemspec +30 -0
- data/tools/gen_methods_json.rb +19 -0
- data/tools/gen_methods_txt.rb +15 -0
- data/tools/gen_types_json.rb +15 -0
- data/tools/gen_types_txt.rb +21 -0
- data/tools/methods.json +1 -0
- data/tools/methods.txt +336 -0
- data/tools/types.json +1 -0
- data/tools/types.txt +577 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 662c96b10f41495f1a6aec7c80801950d22d40eb
|
4
|
+
data.tar.gz: ce8833bebe2c18dc1976ef330460d64cacd52d23
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cba3af636ed854c1e225ae3cdbea48db5145ee3fc9422a3ff7431b60206c1e5ac3652bac64e59b2413184e871134d07df4d10a03b4acf66fe155e6b962c0af92
|
7
|
+
data.tar.gz: 64129d237a5b93b5cb4e7a1c7aa56321cd4704df44c99b57fac81120965b548ece3defed49057e841aee1cfadfe05b6ada9853fcab90a3a638fcbd29968edb95
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 hyrious
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# `Tgbot`
|
2
|
+
|
3
|
+
A tiny but easy-to-use wrapper of [Telegram Bot API](https://core.telegram.org/bots/api).
|
4
|
+
|
5
|
+
It's still under experiment, not ready for use.
|
6
|
+
|
7
|
+
## `future.rb`
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# `Tgbot.run' will start a loop until `Interrupt (Ctrl+C)' occured
|
11
|
+
Tgbot.run TOKEN, proxy: 'https://127.0.0.1:1080' do |bot|
|
12
|
+
bot.start do # once
|
13
|
+
bot.ok "this is @#{bot.first_name}, sir."
|
14
|
+
end
|
15
|
+
bot.get 'drive' do # if message['drive']
|
16
|
+
bot.send_photo garage.pop rescue bot.retry(1) { |x| bot.sorry "Failed #{x} times." }
|
17
|
+
end # totally retry ^ times
|
18
|
+
bot.on /\-md([.^]+)\Z/m do |matched|
|
19
|
+
phantomjs 'md.js', matched
|
20
|
+
bot.send_photo 'cp.jpg' rescue bot.sorry
|
21
|
+
end
|
22
|
+
bot.finish do # rescue Interrupt
|
23
|
+
bot.ok 'byebye.'
|
24
|
+
end
|
25
|
+
bot.before_update do |update|
|
26
|
+
# do something before handling every [update]
|
27
|
+
end
|
28
|
+
bot.after_update do |update|
|
29
|
+
# do something after handling every [update]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
## Further future
|
35
|
+
|
36
|
+
Features often needed by bots.
|
37
|
+
|
38
|
+
- database
|
39
|
+
- session
|
40
|
+
- access control
|
41
|
+
- dynamically add functions
|
42
|
+
-
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tgbot"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/example.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require './helper'
|
2
|
+
save_pid
|
3
|
+
require 'tgbot'
|
4
|
+
@garage = load_data.shuffle
|
5
|
+
|
6
|
+
TOKEN =
|
7
|
+
Tgbot.run TOKEN, proxy: 'https://127.0.0.1:1080' do |bot|
|
8
|
+
|
9
|
+
bot.start do
|
10
|
+
log "this is \e[33m#{bot.name}\e[32m, master.", 2
|
11
|
+
end
|
12
|
+
bot.finish do
|
13
|
+
log "byebye.", 1
|
14
|
+
end
|
15
|
+
bot.get 'drive' do |x, update|
|
16
|
+
pic = @garage.pop
|
17
|
+
log ">> Sending #{File.basename(pic)} to @#{update.message.from.username} ##{update.id}", 6
|
18
|
+
update.reply_photo pic, caption: File.basename(pic, '.*')
|
19
|
+
end
|
20
|
+
bot.before do |update|
|
21
|
+
log ">> Processing ##{update.id} #{bot.timeout}"
|
22
|
+
end
|
23
|
+
bot.after do |update|
|
24
|
+
if update.done?
|
25
|
+
log "=> Success ##{update.id}", 2
|
26
|
+
else
|
27
|
+
log "?> Retry ##{update.id}", 3
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
save_data @garage
|
34
|
+
delete_pid
|
data/lib/methods.json
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"getUpdates":{"ret":"[Update]","params":{"offset":{"type":"Integer","optional":true},"limit":{"type":"Integer","optional":true},"timeout":{"type":"Integer","optional":true},"allowed_updates":{"type":"[String]","optional":true}}},"setWebhook":{"ret":"True","params":{"url":{"type":"String","optional":false},"certificate":{"type":"InputFile","optional":true},"max_connections":{"type":"Integer","optional":true},"allowed_updates":{"type":"[String]","optional":true}}},"deleteWebhook":{"ret":"True","params":{}},"getWebhookInfo":{"ret":"WebhookInfo","params":{}},"getMe":{"ret":"User","params":{}},"sendMessage":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"text":{"type":"String","optional":false},"parse_mode":{"type":"String","optional":true},"disable_web_page_preview":{"type":"Boolean","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"forwardMessage":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"from_chat_id":{"type":"Integer|String","optional":false},"disable_notification":{"type":"Boolean","optional":true},"message_id":{"type":"Integer","optional":false}}},"sendPhoto":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"photo":{"type":"InputFile|String","optional":false},"caption":{"type":"String","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendAudio":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"audio":{"type":"InputFile|String","optional":false},"caption":{"type":"String","optional":true},"duration":{"type":"Integer","optional":true},"performer":{"type":"String","optional":true},"title":{"type":"String","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendDocument":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"document":{"type":"InputFile|String","optional":false},"caption":{"type":"String","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendVideo":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"video":{"type":"InputFile|String","optional":false},"duration":{"type":"Integer","optional":true},"width":{"type":"Integer","optional":true},"height":{"type":"Integer","optional":true},"caption":{"type":"String","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendVoice":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"voice":{"type":"InputFile|String","optional":false},"caption":{"type":"String","optional":true},"duration":{"type":"Integer","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendVideoNote":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"video_note":{"type":"InputFile|String","optional":false},"duration":{"type":"Integer","optional":true},"length":{"type":"Integer","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendLocation":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"latitude":{"type":"Float","optional":false},"longitude":{"type":"Float","optional":false},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendVenue":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"latitude":{"type":"Float","optional":false},"longitude":{"type":"Float","optional":false},"title":{"type":"String","optional":false},"address":{"type":"String","optional":false},"foursquare_id":{"type":"String","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendContact":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"phone_number":{"type":"String","optional":false},"first_name":{"type":"String","optional":false},"last_name":{"type":"String","optional":false},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"sendChatAction":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"action":{"type":"String","optional":false}}},"getUserProfilePhotos":{"ret":"UserProfilePhotos","params":{"user_id":{"type":"Integer","optional":false},"offset":{"type":"Integer","optional":true},"limit":{"type":"Integer","optional":true}}},"getFile":{"ret":"File","params":{"file_id":{"type":"String","optional":false}}},"kickChatMember":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"user_id":{"type":"Integer","optional":false},"until_date":{"type":"Integer","optional":true}}},"unbanChatMember":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"user_id":{"type":"Integer","optional":false}}},"restrictChatMember":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"user_id":{"type":"Integer","optional":false},"until_date":{"type":"Integer","optional":true},"can_send_messages":{"type":"Boolean","optional":true},"can_send_media_messages":{"type":"Boolean","optional":true},"can_send_other_messages":{"type":"Boolean","optional":true},"can_add_web_page_previews":{"type":"Boolean","optional":true}}},"promoteChatMember":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"user_id":{"type":"Integer","optional":false},"can_change_info":{"type":"Boolean","optional":true},"can_post_messages":{"type":"Boolean","optional":true},"can_edit_messages":{"type":"Boolean","optional":true},"can_delete_messages":{"type":"Boolean","optional":true},"can_invite_users":{"type":"Boolean","optional":true},"can_restrict_members":{"type":"Boolean","optional":true},"can_pin_messages":{"type":"Boolean","optional":true},"can_promote_members":{"type":"Boolean","optional":true}}},"exportChatInviteLink":{"ret":"String","params":{"chat_id":{"type":"Integer|String","optional":false}}},"setChatPhoto":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"photo":{"type":"InputFile","optional":false}}},"deleteChatPhoto":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false}}},"setChatTitle":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"title":{"type":"String","optional":false}}},"setChatDescription":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"description":{"type":"String","optional":true}}},"pinChatMessage":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"message_id":{"type":"Integer","optional":false},"disable_notification":{"type":"Boolean","optional":true}}},"unpinChatMessage":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false}}},"leaveChat":{"ret":"Boolean","params":{"chat_id":{"type":"Integer|String","optional":false}}},"getChat":{"ret":"Chat","params":{"chat_id":{"type":"Integer|String","optional":false}}},"getChatAdministrators":{"ret":"[ChatMember]","params":{"chat_id":{"type":"Integer|String","optional":false}}},"getChatMembersCount":{"ret":"Integer","params":{"chat_id":{"type":"Integer|String","optional":false}}},"getChatMember":{"ret":"ChatMember","params":{"chat_id":{"type":"Integer|String","optional":false},"user_id":{"type":"Integer","optional":false}}},"answerCallbackQuery":{"ret":"True","params":{"callback_query_id":{"type":"String","optional":false},"text":{"type":"Boolean","optional":true},"show_alert":{"type":"Boolean","optional":true},"url":{"type":"String","optional":true},"cache_time":{"type":"Integer","optional":true}}},"editMessageText":{"ret":"Message|True","params":{"chat_id":{"type":"Integer|String","optional":true},"message_id":{"type":"Integer","optional":true},"inline_message_id":{"type":"String","optional":true},"text":{"type":"String","optional":false},"parse_mode":{"type":"String","optional":true},"disable_web_page_preview":{"type":"Boolean","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup","optional":true}}},"editMessageCaption":{"ret":"Message|True","params":{"chat_id":{"type":"Integer|String","optional":true},"message_id":{"type":"Integer","optional":true},"inline_message_id":{"type":"String","optional":true},"caption":{"type":"String","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup","optional":true}}},"editMessageReplyMarkup":{"ret":"Message|True","params":{"chat_id":{"type":"Integer|String","optional":true},"message_id":{"type":"Integer","optional":true},"inline_message_id":{"type":"String","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup","optional":true}}},"deleteMessage":{"ret":"True","params":{"chat_id":{"type":"Integer|String","optional":false},"message_id":{"type":"Integer","optional":false}}},"sendSticker":{"ret":"Message","params":{"chat_id":{"type":"Integer|String","optional":false},"sticker":{"type":"InputFile|String","optional":false},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply","optional":true}}},"getStickerSet":{"ret":"StickerSet","params":{"name":{"type":"String","optional":false}}},"uploadStickerFile":{"ret":"File","params":{"user_id":{"type":"Integer","optional":false},"png_sticker":{"type":"InputFile","optional":false}}},"createNewStickerSet":{"ret":"True","params":{"name":{"type":"String","optional":false},"title":{"type":"String","optional":false},"png_sticker":{"type":"InputFile|String","optional":false},"emojis":{"type":"String","optional":false},"contains_masks":{"type":"Boolean","optional":true},"mask_position":{"type":"MaskPosition","optional":true}}},"addStickerToSet":{"ret":"True","params":{"user_id":{"type":"Integer","optional":false},"name":{"type":"String","optional":false},"png_sticker":{"type":"InputFile|String","optional":false},"emojis":{"type":"String","optional":false},"mask_position":{"type":"MaskPosition","optional":true}}},"setStickerPositionInSet":{"ret":"True","params":{"sticker":{"type":"String","optional":false},"position":{"type":"Integer","optional":false}}},"deleteStickerFromSet":{"ret":"True","params":{"sticker":{"type":"String","optional":false}}},"answerInlineQuery":{"ret":"Boolean","params":{"inline_query_id":{"type":"String","optional":false},"results":{"type":"[InlineQueryResult]","optional":false},"cache_time":{"type":"Integer","optional":true},"is_personal":{"type":"Boolean","optional":true},"next_offset":{"type":"String","optional":true},"switch_pm_text":{"type":"String","optional":true},"switch_pm_parameter":{"type":"String","optional":true}}},"sendInvoice":{"ret":"Message","params":{"chat_id":{"type":"Integer","optional":false},"title":{"type":"String","optional":false},"description":{"type":"String","optional":false},"payload":{"type":"String","optional":false},"provider_token":{"type":"String","optional":false},"start_parameter":{"type":"String","optional":false},"currency":{"type":"String","optional":false},"prices":{"type":"[LabeledPrice]","optional":false},"photo_url":{"type":"String","optional":true},"photo_size":{"type":"Integer","optional":true},"photo_width":{"type":"Integer","optional":true},"photo_height":{"type":"Integer","optional":true},"need_name":{"type":"Boolean","optional":true},"need_phone_number":{"type":"Boolean","optional":true},"need_email":{"type":"Boolean","optional":true},"need_shipping_address":{"type":"Boolean","optional":true},"is_flexible":{"type":"Boolean","optional":true},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup","optional":true}}},"answerShippingQuery":{"ret":"True","params":{"shipping_query_id":{"type":"String","optional":false},"ok":{"type":"Boolean","optional":false},"shipping_options":{"type":"[ShippingOption]","optional":true},"error_message":{"type":"String","optional":true}}},"answerPreCheckoutQuery":{"ret":"True","params":{"pre_checkout_query_id":{"type":"String","optional":false},"ok":{"type":"Boolean","optional":false},"error_message":{"type":"String","optional":true}}},"sendGame":{"ret":"Message","params":{"chat_id":{"type":"Integer","optional":false},"game_short_name":{"type":"String","optional":false},"disable_notification":{"type":"Boolean","optional":true},"reply_to_message_id":{"type":"Integer","optional":true},"reply_markup":{"type":"InlineKeyboardMarkup","optional":true}}},"setGameScore":{"ret":"Message|True","params":{"user_id":{"type":"Integer","optional":false},"score":{"type":"Integer","optional":false},"force":{"type":"Boolean","optional":true},"disable_edit_message":{"type":"Boolean","optional":true},"chat_id":{"type":"Integer","optional":true},"message_id":{"type":"Integer","optional":true},"inline_message_id":{"type":"String","optional":true}}},"getGameHighScores":{"ret":"[GameHighScore]","params":{"user_id":{"type":"Integer","optional":false},"chat_id":{"type":"Integer","optional":true},"message_id":{"type":"Integer","optional":true},"inline_message_id":{"type":"String","optional":true}}}}
|
data/lib/tgbot.rb
ADDED
data/lib/tgbot/core.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'faraday'
|
3
|
+
|
4
|
+
module Tgbot
|
5
|
+
API_URL = 'https://api.telegram.org'.freeze
|
6
|
+
TYPES = JSON.parse(File.read File.expand_path '../types.json' , __dir__).freeze
|
7
|
+
METHODS = JSON.parse(File.read File.expand_path '../methods.json', __dir__).freeze
|
8
|
+
|
9
|
+
# This bot supports a minimal usage of Telegram Bot APIs.
|
10
|
+
# bot = Bot.new TOKEN, proxy: 'https://127.0.0.1:1080'
|
11
|
+
#
|
12
|
+
# API's methods' input and output are hash.
|
13
|
+
# bot.get_updates offset: 0 #=> { 'ok' => 'true', 'result' => [] }
|
14
|
+
#
|
15
|
+
# It will check type of params before post method, and
|
16
|
+
# if invalid, it will raise an error with detail.
|
17
|
+
class Bot
|
18
|
+
attr_accessor :token
|
19
|
+
|
20
|
+
# Initialize a bot, and call getMe at once to see if given token is valid.
|
21
|
+
# If everything ok, the bot will get its id and name and so on.
|
22
|
+
# token :: String = TOKEN of your bot from botfather
|
23
|
+
# opts :: Hash = Options passed to Faraday.new
|
24
|
+
def initialize(token, **opts)
|
25
|
+
@token = token
|
26
|
+
get_connection(**opts)
|
27
|
+
identify_self
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get bot's info.
|
31
|
+
def identify_self
|
32
|
+
x = get_me
|
33
|
+
if x['ok']
|
34
|
+
@me = x['result']
|
35
|
+
else
|
36
|
+
raise ArgumentError, 'not found myself, check your token.'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Shortcuts for bot's info.
|
41
|
+
def id ; @me && @me['id'] ; end
|
42
|
+
def first_name; @me && @me['first_name']; end
|
43
|
+
def username ; @me && @me['username'] ; end
|
44
|
+
alias name first_name
|
45
|
+
|
46
|
+
# Connect to API_URL. It will take few seconds.
|
47
|
+
# opts :: Hash = Options passed to Faraday.new
|
48
|
+
def get_connection(**opts)
|
49
|
+
@conn = Faraday.new(url: API_URL, **opts) do |faraday|
|
50
|
+
faraday.request :multipart
|
51
|
+
faraday.request :url_encoded
|
52
|
+
faraday.adapter Faraday.default_adapter
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Verify methods and params then call(post) it.
|
57
|
+
# `:get_me' and `:getMe' are both valid.
|
58
|
+
def method_missing(meth, **kwargs)
|
59
|
+
camelized_meth = camelize meth
|
60
|
+
meth_body = METHODS[camelized_meth]
|
61
|
+
super unless meth_body
|
62
|
+
ret, params = meth_body.values_at 'ret', 'params'
|
63
|
+
check_params! (JSON.parse JSON.generate kwargs), params
|
64
|
+
call camelized_meth, kwargs
|
65
|
+
end
|
66
|
+
|
67
|
+
def call meth, kwargs
|
68
|
+
JSON.parse @conn.post("/bot#{@token}/#{meth}", kwargs).body
|
69
|
+
rescue
|
70
|
+
{}
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check args to meet method declaration. Raises error if invalid.
|
74
|
+
def check_params! kwargs, params
|
75
|
+
params.each do |param, info|
|
76
|
+
arg = kwargs[param]
|
77
|
+
type, optional = info.values_at 'type', 'optional'
|
78
|
+
if (arg.nil? && !optional) || (!arg.nil? && !check_type(arg, type))
|
79
|
+
raise ArgumentError, "[#{param}] should be #{type}\n#{error_message_of_type type}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get declaration of type in form of:
|
85
|
+
# User := { id :: Integer, first_name :: String }
|
86
|
+
def error_message_of_type type
|
87
|
+
(type.delete('[]').split('|') - ['True', 'Boolean', 'Integer', 'Float', 'String']).map { |type|
|
88
|
+
"#{type} := { #{TYPES[type].map { |field, info|
|
89
|
+
"#{info['optional'] ? '' : '*'}#{field} :: #{info['type']}"
|
90
|
+
}.join(', ')} }"
|
91
|
+
}.join(' ')
|
92
|
+
end
|
93
|
+
|
94
|
+
# Check arg to meet type declaration. Returns false if invalid.
|
95
|
+
def check_type arg, type
|
96
|
+
case type
|
97
|
+
when 'True' then return arg == true
|
98
|
+
when 'Boolean' then return arg == true || arg == false
|
99
|
+
when 'Integer' then return arg.is_a? Integer
|
100
|
+
when 'Float' then return arg.is_a? Float
|
101
|
+
when 'String' then return arg.is_a? String
|
102
|
+
end
|
103
|
+
if type[0] == '['
|
104
|
+
return arg.is_a?(Array) ? arg.all? { |a| check_type a, type[1..-2] } : false
|
105
|
+
elsif type.include? '|'
|
106
|
+
return type.split('|').any? { |t| check_type arg, t }
|
107
|
+
end
|
108
|
+
return false unless TYPES[type]
|
109
|
+
check_params(arg, TYPES[type])
|
110
|
+
end
|
111
|
+
|
112
|
+
# Check args to meet method declaration. Returns false if invalid.
|
113
|
+
def check_params kwargs, params
|
114
|
+
check_params!(kwargs, params)
|
115
|
+
true
|
116
|
+
rescue
|
117
|
+
false
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_types
|
121
|
+
TYPES.keys.map(&:to_sym)
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_methods
|
125
|
+
METHODS.keys.map { |e| underscore e }.map(&:to_sym)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Transform 'TheName' or 'theName' to 'the_name'.
|
129
|
+
def underscore str
|
130
|
+
str.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
131
|
+
.gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
132
|
+
end
|
133
|
+
|
134
|
+
# Transform 'the_name' to 'theName'.
|
135
|
+
def camelize meth
|
136
|
+
ret = String(meth).split('_')
|
137
|
+
ret.drop(1).map(&:capitalize!)
|
138
|
+
ret.join
|
139
|
+
end
|
140
|
+
|
141
|
+
def inspect
|
142
|
+
"#<Bot token=#{@token} id=#{id} first_name=#{first_name} username=#{username}>"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/tgbot/dsl.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'tgbot/runner'
|
2
|
+
|
3
|
+
module Tgbot
|
4
|
+
class DSL
|
5
|
+
attr_accessor :runner
|
6
|
+
def initialize(token, **opts)
|
7
|
+
@runner = Runner.new(token, **opts)
|
8
|
+
@procs = { command: {} }
|
9
|
+
end
|
10
|
+
def start(&blk)
|
11
|
+
@procs[:start] = blk
|
12
|
+
end
|
13
|
+
def finish(&blk)
|
14
|
+
@procs[:finish] = blk
|
15
|
+
end
|
16
|
+
def before(&blk)
|
17
|
+
@procs[:before] = blk
|
18
|
+
end
|
19
|
+
def after(&blk)
|
20
|
+
@procs[:after] = blk
|
21
|
+
end
|
22
|
+
def on(regex, &blk)
|
23
|
+
@procs[:command][regex] = blk
|
24
|
+
end
|
25
|
+
alias get on
|
26
|
+
def run
|
27
|
+
yield self if block_given?
|
28
|
+
@procs[:start]&.call
|
29
|
+
begin
|
30
|
+
@runner.mainloop do |update|
|
31
|
+
@procs[:before]&.call update
|
32
|
+
update.done = true
|
33
|
+
@procs[:command].each do |key, blk|
|
34
|
+
x = update.text&.match key
|
35
|
+
blk.call x, update if x
|
36
|
+
end
|
37
|
+
@procs[:after]&.call update
|
38
|
+
end
|
39
|
+
rescue Interrupt
|
40
|
+
@procs[:finish]&.call
|
41
|
+
rescue => e
|
42
|
+
puts $!
|
43
|
+
puts e.backtrace
|
44
|
+
retry
|
45
|
+
end
|
46
|
+
end
|
47
|
+
def method_missing(meth, *args, &blk)
|
48
|
+
@runner.send(meth, *args, &blk)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
def self.run(token, **opts, &blk)
|
52
|
+
DSL.new(token, **opts).run(&blk)
|
53
|
+
end
|
54
|
+
end
|
data/lib/tgbot/runner.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'tgbot/core'
|
4
|
+
require 'tgbot/update'
|
5
|
+
|
6
|
+
module Tgbot
|
7
|
+
class Runner
|
8
|
+
attr_accessor :bot, :offset, :timeout, :updates
|
9
|
+
def initialize(token, **opts)
|
10
|
+
@bot = Bot.new(token, **opts)
|
11
|
+
@offset = 0
|
12
|
+
@timeout = 2
|
13
|
+
@updates = []
|
14
|
+
end
|
15
|
+
def mainloop
|
16
|
+
loop do
|
17
|
+
@updates.each { |u| u.count += 1 }
|
18
|
+
update_updates
|
19
|
+
@updates.each { |update| yield update }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def update_updates
|
23
|
+
@updates.delete_if(&:done?)
|
24
|
+
x = x
|
25
|
+
t = time { x = @bot.get_updates offset: @offset + 1, limit: 7, timeout: @timeout }
|
26
|
+
case
|
27
|
+
when t > @timeout then @timeout += [@timeout / 2, 1].max
|
28
|
+
when t < @timeout then @timeout -= 1
|
29
|
+
end
|
30
|
+
@timeout = [[0, @timeout].max, 15].min
|
31
|
+
x['result'].each { |e| @updates.push Update.new(@bot, hash_to_ostruct(e)) } if x['ok']
|
32
|
+
@offset = [*@updates.map(&:update_id), @offset].max
|
33
|
+
end
|
34
|
+
def hash_to_ostruct hash
|
35
|
+
JSON.parse JSON.generate(hash), object_class: OpenStruct
|
36
|
+
end
|
37
|
+
def time
|
38
|
+
t = Time.now
|
39
|
+
yield
|
40
|
+
Time.now - t
|
41
|
+
end
|
42
|
+
def method_missing(meth, *args, &blk)
|
43
|
+
@bot.send(meth, *args, &blk)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/tgbot/update.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'mimemagic'
|
3
|
+
module Tgbot
|
4
|
+
class Update
|
5
|
+
attr_accessor :bot, :update, :type, :done, :count
|
6
|
+
def initialize bot, update
|
7
|
+
@bot, @update = bot, update
|
8
|
+
@type = get_type
|
9
|
+
@done = false
|
10
|
+
@count = 0
|
11
|
+
end
|
12
|
+
alias done? done
|
13
|
+
def id
|
14
|
+
@update.update_id
|
15
|
+
end
|
16
|
+
def chat_id
|
17
|
+
@update[@type].chat&.id
|
18
|
+
end
|
19
|
+
def text
|
20
|
+
@update[@type].text
|
21
|
+
end
|
22
|
+
def send_message(text = nil, **kwargs)
|
23
|
+
return unless chat_id
|
24
|
+
return unless text = text || kwargs.delete(:text)
|
25
|
+
@bot.send_message(chat_id: chat_id, text: text, **kwargs)
|
26
|
+
end
|
27
|
+
def reply_message(text = nil, **kwargs)
|
28
|
+
return unless chat_id
|
29
|
+
return unless text = text || kwargs.delete(:text)
|
30
|
+
@bot.send_message(
|
31
|
+
chat_id: chat_id, text: text,
|
32
|
+
reply_to_message_id: @update[@type].message_id, **kwargs)
|
33
|
+
end
|
34
|
+
%i(photo audio document video voice video_note).each do |name|
|
35
|
+
class_eval %{
|
36
|
+
def send_#{name}(#{name} = nil, **kwargs)
|
37
|
+
return unless chat_id
|
38
|
+
return unless #{name} = #{name} || kwargs.delete(:#{name})
|
39
|
+
@bot.send_#{name}(
|
40
|
+
chat_id: chat_id,
|
41
|
+
#{name}: Faraday::UploadIO.new(#{name}, MimeMagic.by_path(#{name}).type),
|
42
|
+
**kwargs)
|
43
|
+
end
|
44
|
+
def reply_#{name}(#{name} = nil, **kwargs)
|
45
|
+
return unless chat_id
|
46
|
+
return unless #{name} = #{name} || kwargs.delete(:#{name})
|
47
|
+
@bot.send_#{name}(
|
48
|
+
chat_id: chat_id,
|
49
|
+
#{name}: Faraday::UploadIO.new(#{name}, MimeMagic.by_path(#{name}).type),
|
50
|
+
reply_to_message_id: @update[@type].message_id, **kwargs)
|
51
|
+
end
|
52
|
+
}
|
53
|
+
end
|
54
|
+
def get_type
|
55
|
+
%i(
|
56
|
+
message
|
57
|
+
edited_message
|
58
|
+
channel_post
|
59
|
+
edited_channel_post
|
60
|
+
inline_query
|
61
|
+
chosen_inline_result
|
62
|
+
callback_query
|
63
|
+
shipping_query
|
64
|
+
pre_checkout_query
|
65
|
+
).find { |f| @update[f] }
|
66
|
+
end
|
67
|
+
def method_missing(field)
|
68
|
+
@update[field]
|
69
|
+
end
|
70
|
+
def inspect
|
71
|
+
"#<Update ##{id} #{@type}=#{@update[@type]}>"
|
72
|
+
end
|
73
|
+
alias to_str inspect
|
74
|
+
alias to_s inspect
|
75
|
+
end
|
76
|
+
end
|