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 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