Class: TelegramBotMiddleware
- Inherits:
-
Object
- Object
- TelegramBotMiddleware
- Includes:
- HTTMultiParty
- Defined in:
- lib/telegram_bot_middleware.rb,
lib/telegram_bot_middleware/version.rb
Constant Summary collapse
- VERSION =
"0.3.1"
Instance Method Summary collapse
- #_call(env) ⇒ Object
-
#call(env) ⇒ Object
necessary for thread safe.
-
#initialize(app) {|@config| ... } ⇒ TelegramBotMiddleware
constructor
A new instance of TelegramBotMiddleware.
- #log(level, message) ⇒ Object
- #log_debug(message) ⇒ Object
- #log_error(exception) ⇒ Object
- #log_info(message) ⇒ Object
- #process_hash_message(message, params) ⇒ Object
- #process_json_message(message, params) ⇒ Object
- #send_to_bot(path, query) ⇒ Object
- #start_get_updates_thread ⇒ Object
Constructor Details
#initialize(app) {|@config| ... } ⇒ TelegramBotMiddleware
Returns a new instance of TelegramBotMiddleware.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/telegram_bot_middleware.rb', line 13 def initialize(app, &block) # save the app var @app = app # local cookies hash @cookies = Hash.new @env = nil # create the config and populate passing do the block function @config = OpenStruct.new yield(@config) if block_given? self.class.persistent_connection_adapter pool_size: (@config.connection_pool_size || 2), keep_alive: (@config.connection_keep_alive || 30), force_retry: (@config.connection_force_retry || true) @config.get_updates ||= :polling # setup webhook if @config.webhook.nil? @config.host = "#{@config.host}/" unless @config.host.end_with?('/') @config.webhook = "#{@config.host}#{@config.token}" end # setup telegram messages input case @config.get_updates # setup polling when :polling # clear the webhook in case was set in the past send_to_bot('setWebhook', {url: ''}) # setup a thread with get_updates function start_get_updates_thread # setup webhook when :webhook send_to_bot('setWebhook', {url: @config.webhook}) # in this case get_updates is a non valid value else raise ArgumentError.new('Config error: get_updates must be :webhook or :polling.') end end |
Instance Method Details
#_call(env) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/telegram_bot_middleware.rb', line 88 def _call(env) @env = env # retrieve the request object req = Rack::Request.new(@env) # if the request is a post to bot webhhok if req.post? and req.path == "/#{@config.token}" # in case someone already read it req.body.rewind # build an openstruct based on post params params = OpenStruct.from_json(req.body.read) log_debug("Message from chat: #{params}") path = nil unless params.['text'].nil? # build path based on message # - get only message part of post params # - remove empty chars from beginning or end (strip) # - replace first sequence of spaces with / # - encode as uri path = URI.escape(params..text.strip.sub(/\s+/, '/')) # - add first / if not present path = "/#{path}" unless path.start_with?('/') else %w(audio document photo sticker video voice contact location new_chat_participant left_chat_participant new_chat_title new_chat_photo delete_chat_photo group_chat_created).each do |type| unless params.[type].nil? path = "/#{type}" break end end end # build the querystring using message but nested query_string = Rack::Utils.build_nested_query(params..to_h_nested) # transform the POST in GET @env['PATH_INFO'] = path @env['QUERY_STRING'] = query_string @env['REQUEST_METHOD'] = 'GET' @env['REQUEST_URI'] = "https://#{req.host}#{path}" # if in cache a cookie for this chat was present add to the header @env['HTTP_COOKIE'] = @cookies[params..chat.id] if @cookies.include?(params..chat.id) # call the rack stack status, headers, body = @app.call(@env) # try to send to telegram only if no errors if status == 200 or status == '200' # if the call setted a cookie save to local cache @cookies[params..chat.id] = headers['Set-Cookie'] if headers.include?('Set-Cookie') case headers['Content-Type'].split(';').first when 'text/html', 'application/json' if body.is_a? Hash (body.clone, params) body = Array.new(1) { '' } else body.each do |data| begin #TODO: add better json parsing to support symbols too (JSON.parse(data.gsub('=>', ':')), params) rescue send_to_bot('sendMessage', {chat_id: params..chat.id, text: data}) end end end when /(^image\/)/ send_to_bot('sendPhoto', {chat_id: params..chat.id, photo: File.new(body)}) when /(^audio\/)/ send_to_bot('sendAudio', {chat_id: params..chat.id, audio: File.new(body)}) when /(^video\/)/ send_to_bot('sendVideo', {chat_id: params..chat.id, video: File.new(body)}) end end # return result [status, headers, body] else # normal rack flow - not a bot call @app.call(env) end end |
#call(env) ⇒ Object
necessary for thread safe
84 85 86 |
# File 'lib/telegram_bot_middleware.rb', line 84 def call(env) dup._call(env) end |
#log(level, message) ⇒ Object
227 228 229 230 231 232 233 234 |
# File 'lib/telegram_bot_middleware.rb', line 227 def log(level, ) return if @env.nil? if @env['rack.logger'] @env['rack.logger'].send(level, ) else @env['rack.errors'].write() end end |
#log_debug(message) ⇒ Object
223 224 225 |
# File 'lib/telegram_bot_middleware.rb', line 223 def log_debug() log(:debug, ) end |
#log_error(exception) ⇒ Object
214 215 216 217 |
# File 'lib/telegram_bot_middleware.rb', line 214 def log_error(exception) = "Error: #{exception.}\n#{exception.backtrace.join("\n")}\n" log(:error, ) end |
#log_info(message) ⇒ Object
219 220 221 |
# File 'lib/telegram_bot_middleware.rb', line 219 def log_info() log(:info, ) end |
#process_hash_message(message, params) ⇒ Object
179 180 181 182 183 184 185 186 187 |
# File 'lib/telegram_bot_middleware.rb', line 179 def (, params) if (.include?(:multiple) && [:multiple].is_a?(Array)) [:multiple].each { |item| (item, params) } elsif (.include?('multiple') && ['multiple'].is_a?(Array)) ['multiple'].each { |item| (item, params) } else (, params) end end |
#process_json_message(message, params) ⇒ Object
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/telegram_bot_middleware.rb', line 189 def (, params) [:chat_id] = params..chat.id unless .include?(:chat_id) or .include?('chat_id') [:reply_markup] = [:reply_markup].to_json if .include?(:reply_markup) ['reply_markup'] = ['reply_markup'].to_json if .include?('reply_markup') ['photo', :photo, 'audio', :audio, 'video', :video].each do |item| [item] = File.new([item]) if .include?(item) end if .include?(:text) or .include?('text') send_to_bot('sendMessage', ) elsif (.include?(:latitude) and .include?(:longitude)) or (.include?('latitude') and .include?('longitude')) send_to_bot('sendLocation', ) elsif .include?(:photo) or .include?('photo') send_to_bot('sendPhoto', ) elsif .include?(:audio) or .include?('audio') send_to_bot('sendAudio', ) elsif .include?(:video) or .include?('video') send_to_bot('sendVideo', ) else # TODO: invalid query end end |
#send_to_bot(path, query) ⇒ Object
236 237 238 239 240 |
# File 'lib/telegram_bot_middleware.rb', line 236 def send_to_bot(path, query) log_debug("Sending to chat: #{path} - #{query}") response = self.class.post("/bot#{@config.token}/#{path}", query: query) # TODO check response error and return response end |
#start_get_updates_thread ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/telegram_bot_middleware.rb', line 59 def start_get_updates_thread # start a new thread Thread.new do # the initial offset is always 0 @offset = 0 # wait 5 seconds to don't risk to post message too early when the app is not still up sleep 5 # loop forever loop do # call the getUpdates telegram function response = send_to_bot('getUpdates', {offset: @offset}) # enumerate the results response.to_hash['result'].each do |data| # create an update message from the post data update = OpenStruct.new(data) # store the last offset +1 but ensure that is not lower than the already stored @offset = (update.update_id + 1) if update.update_id + 1 > @offset # simulate a post to itself HTTP.post @config.webhook, json: update.to_h_nested end end end end |