tgbot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|