tgbot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in tgbot.gemspec
4
+ gemspec
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
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc 'Make lib/*.json'
4
+ task :json do
5
+ cd 'tools'
6
+ ruby 'gen_types_json.rb'
7
+ cp 'types.json', '../lib'
8
+ ruby 'gen_methods_json.rb'
9
+ cp 'methods.json', '../lib'
10
+ end
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
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
@@ -0,0 +1,5 @@
1
+ require 'tgbot/version'
2
+ require 'tgbot/core'
3
+ require 'tgbot/update'
4
+ require 'tgbot/runner'
5
+ require 'tgbot/dsl'
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
@@ -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
@@ -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