From e6ec24f2ab95252cbef596f5e3292d44d00e51f8 Mon Sep 17 00:00:00 2001 From: Valov Date: Sat, 2 Mar 2024 21:46:49 -0300 Subject: [PATCH 1/4] Telegram::Bot::WebApp --- lib/telegram/bot/client.rb | 3 +- lib/telegram/bot/types/web_app_user.rb | 20 ++++++++++++ lib/telegram/bot/web_app.rb | 43 ++++++++++++++++++++++++++ spec/lib/telegram/bot/web_app_spec.rb | 26 ++++++++++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 lib/telegram/bot/types/web_app_user.rb create mode 100644 lib/telegram/bot/web_app.rb create mode 100644 spec/lib/telegram/bot/web_app_spec.rb diff --git a/lib/telegram/bot/client.rb b/lib/telegram/bot/client.rb index 6e7806b..c2f07af 100644 --- a/lib/telegram/bot/client.rb +++ b/lib/telegram/bot/client.rb @@ -3,7 +3,7 @@ module Telegram module Bot class Client - attr_reader :api, :options + attr_reader :api, :options, :web_app attr_accessor :logger def self.run(*args, &block) @@ -13,6 +13,7 @@ def self.run(*args, &block) def initialize(token, hash = {}) @options = default_options.merge(hash) @api = Api.new(token, url: options.delete(:url), environment: options.delete(:environment)) + @web_app = WebApp.new(token) @logger = options.delete(:logger) end diff --git a/lib/telegram/bot/types/web_app_user.rb b/lib/telegram/bot/types/web_app_user.rb new file mode 100644 index 0000000..0f9f7c9 --- /dev/null +++ b/lib/telegram/bot/types/web_app_user.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Telegram + module Bot + module Types + class WebAppUser < Base + attribute :id, Types::Integer + attribute :first_name, Types::String + attribute? :is_bot, Types::Bool + attribute? :last_name, Types::String + attribute? :username, Types::String + attribute? :language_code, Types::String + attribute? :is_premium, Types::True + attribute? :added_to_attachment_menu, Types::True + attribute? :allows_write_to_pm, Types::Bool + attribute? :photo_url, Types::String + end + end + end +end diff --git a/lib/telegram/bot/web_app.rb b/lib/telegram/bot/web_app.rb new file mode 100644 index 0000000..ef6499b --- /dev/null +++ b/lib/telegram/bot/web_app.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'openssl' +require 'json' +require 'uri' + +module Telegram + module Bot + class WebApp + TG_KEY = 'WebAppData' + + def initialize(token) + @token = token + end + + def verify_data_init(data_init) + decoded_data = URI.decode_www_form(data_init).to_h + data_check_string = build_data_check_string(decoded_data) + + secret_key = hmac_sha256(TG_KEY, @token) + hex = hex_encode(hmac_sha256(secret_key, data_check_string)) + + return false unless decoded_data['hash'] == hex + + Types::WebAppUser.new(JSON.parse(decoded_data['user'], symbolize_names: true)) + end + + private + + def build_data_check_string(decoded_data) + decoded_data.filter_map { |key, value| "#{key}=#{value}" unless key == 'hash' }.sort.join("\n") + end + + def hmac_sha256(key, data) + OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, data) + end + + def hex_encode(data) + data.unpack1('H*') + end + end + end +end diff --git a/spec/lib/telegram/bot/web_app_spec.rb b/spec/lib/telegram/bot/web_app_spec.rb new file mode 100644 index 0000000..6793934 --- /dev/null +++ b/spec/lib/telegram/bot/web_app_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +RSpec.describe Telegram::Bot::WebApp do + let(:web_app) { described_class.new(token) } + let(:token) { '6066223859:AAHTBqDfGUUXbb9R0HQmYlrJ6_moCPb6-wI' } + + describe '#verify_data_init' do + subject { web_app.verify_data_init(data_init) } + + context 'when data_init is invalid' do + # rubocop:disable Layout/LineLength + let(:data_init) do + 'query_id=AAFiJKgRAAAAAGIkqBEsKipR&user=%7B%22id%22%3A296232034%2C%22first_name%22%3A%22%D0%9C%D0%B8%D1%85%D0%B0%D0%B8%D0%BB%22%2C%22last_name%22%3A%22%D0%92%D0%B0%D0%BB%D0%BE%D0%B2%22%2C%22username%22%3A%22valovm%22%2C%22language_code%22%3A%22en%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%7D&auth_date=1708281435&hash=c7813b4aee476fa0c79a08e182b197ed8d97a8010cebc813320ea61b0ced2b66' + end + # rubocop:enable Layout/LineLength + + it { is_expected.to be_a(Telegram::Bot::Types::WebAppUser) } + end + + context 'when data_init is valid' do + let(:data_init) { 'data_init_not_valid' } + + it { is_expected.to be_falsey } + end + end +end From cc69a3ee13cbe8d3ac1f0a09472df8cffd5e86be Mon Sep 17 00:00:00 2001 From: Valov Date: Sat, 2 Mar 2024 21:59:45 -0300 Subject: [PATCH 2/4] Readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 9733ea7..e3f929f 100644 --- a/README.md +++ b/README.md @@ -265,6 +265,16 @@ Telegram::Bot.configure do |config| end ``` +## Validating data received via the Mini App + +To validate data received via the Mini App, you can use the `Telegram::Bot::WebApp` class. +This class has a method `#verify_data_init` receive string with telegram data init that returns `Telegram::Bot::Types::WebAppUser` if the data is valid and `false` if it is not. + +```ruby +bot = Telegram::Bot::Client.new(token) +bot.web_app.verify_data_init(td_data_init) +``` + ## Boilerplates If you don't know how to setup database for your bot or how to use it with different languages From 1ff816e2ca1090dd511345d10ae54ab62ee5ea27 Mon Sep 17 00:00:00 2001 From: Valov Date: Sat, 2 Mar 2024 22:06:07 -0300 Subject: [PATCH 3/4] Readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e3f929f..4140a75 100644 --- a/README.md +++ b/README.md @@ -265,9 +265,9 @@ Telegram::Bot.configure do |config| end ``` -## Validating data received via the Mini App +## Validating data received via the Web App -To validate data received via the Mini App, you can use the `Telegram::Bot::WebApp` class. +To validate data received via the Web App, you can use the `Telegram::Bot::WebApp` class. This class has a method `#verify_data_init` receive string with telegram data init that returns `Telegram::Bot::Types::WebAppUser` if the data is valid and `false` if it is not. ```ruby From 81367dcc5b5fe5dfba9c2be41b7d9c8fd330ff2a Mon Sep 17 00:00:00 2001 From: Valov Date: Sat, 2 Mar 2024 22:10:15 -0300 Subject: [PATCH 4/4] Readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4140a75..164ebd4 100644 --- a/README.md +++ b/README.md @@ -267,13 +267,15 @@ end ## Validating data received via the Web App -To validate data received via the Web App, you can use the `Telegram::Bot::WebApp` class. +To validate data received via the [Web App](https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app), you can use the `Telegram::Bot::WebApp` class. This class has a method `#verify_data_init` receive string with telegram data init that returns `Telegram::Bot::Types::WebAppUser` if the data is valid and `false` if it is not. ```ruby bot = Telegram::Bot::Client.new(token) bot.web_app.verify_data_init(td_data_init) ``` + - `td_data_init` - String with telegram data init. + ## Boilerplates