vk_longpoll_bot 0.0.1

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
+ SHA256:
3
+ metadata.gz: f28f217393130c2c06d14a05e785bde07781ed164da0032689e15cf02526fae5
4
+ data.tar.gz: e3c750777c8347751b940fdc27366cb90b30c2de932894c4456cfd2c05e1f81d
5
+ SHA512:
6
+ metadata.gz: 4ecb287c910584456bbbba055d7af012dba65fffad2657336766c78560f1165c5184d2d45b892232ab228bfc556935d1815def1b9ba3b90a6c17badbff791aa6
7
+ data.tar.gz: 143c4c9964f20b9750619820bde70c66c2f8142e48e7396df9443021ef5c90fc10ca648b5c11c7cccf80aa550a7c90d157d9979e224228bc0a14c459278ed330
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Fizvlad
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,2 @@
1
+ # vk-longpoll-bot-rb
2
+ Ruby library to create VK longpoll bot
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ GEM_NAME = "vk_longpoll_bot"
2
+
3
+ desc "Build gem file"
4
+ task :build do
5
+ puts `gem build #{GEM_NAME}.gemspec`
6
+ end
7
+
8
+ desc "Uninstall gem"
9
+ task :uninstall do
10
+ puts `gem uninstall #{GEM_NAME}`
11
+ end
12
+
13
+ desc "Build and install gem"
14
+ task :install_local => :build do
15
+ puts `gem install ./#{GEM_NAME}-*.gem`
16
+ end
17
+
18
+ desc "Setup SSL certificate"
19
+ task :setup_ssl do
20
+ print "Path to SSL certificate (leave empty if there is no troubles with SSL):"
21
+ ssl_cert_path = STDIN.gets.chomp
22
+ puts
23
+ ENV["SSL_CERT_FILE"] = ssl_cert_path
24
+ end
@@ -0,0 +1,134 @@
1
+ module VkLongpollBot
2
+
3
+ # Main class, which contains all the methods of bot.
4
+ class Bot
5
+
6
+ # Every bot stores id of group it operates.
7
+ attr_reader :id, :event_listeners
8
+
9
+ # Initialize bot. This method don't run longpoll.
10
+ #
11
+ # <tt>options</tt> hash can contain following keys:
12
+ # * <tt>:api_version</tt> - version of api to use
13
+ # * <tt>:longpoll_wait</tt> - longpoll requests timeout
14
+ def initialize(access_token, id, options = {})
15
+ @event_listeners = Hash.new { |hash, key| hash[key] = Array.new }
16
+ @on_start = []
17
+ @on_finish = []
18
+
19
+ @access_token = access_token
20
+ @id = id
21
+
22
+ @api_version = options[:api_version] || VK_API_CURRENT_VERSION
23
+ @longpoll_wait = options[:longpoll_wait] || LONGPOLL_STANDART_WAIT
24
+
25
+ @longpoll = {}
26
+
27
+ # TODO
28
+ end
29
+
30
+ # Call for api method with given parameters.
31
+ def api(method_name, parameters = {})
32
+ Request.api(method_name, parameters, @access_token, @api_version)
33
+ end
34
+
35
+ # Messaging
36
+
37
+ # Send message to <tt>target</tt> with provided <tt>content</tt>.
38
+ def send_message(target, content)
39
+ target_id = target.to_i
40
+ api("messages.send", user_id: target_id, message: content, random_id: Utility.random_id(target_id))
41
+ end
42
+
43
+ # TODO: Which methods are also addable here?
44
+
45
+ # Events
46
+
47
+ # Add event listener.
48
+ #
49
+ # <tt>attributes</tt> hash can contain following keys:
50
+ # * <tt>:subtype</tt> - event subtype. All of event types and subtypes are stated in Events::TYPES
51
+ def on(attributes, &block)
52
+ raise ArgumentError.new("Got subtype #{attributes[:subtype]} of class #{attributes[:subtype].class}") unless String === attributes[:subtype] && Events.valid_subtype?(attributes[:subtype])
53
+ @event_listeners[attributes[:subtype]] << Events::EventListener.new(attributes, &block)
54
+ end
55
+
56
+ # Add code to be executed right after bot starts.
57
+ def on_start(&block)
58
+ @on_start << block
59
+ end
60
+
61
+ # Add code to be executed right after bot finishes.
62
+ def on_finish(&block)
63
+ @on_finish << block
64
+ end
65
+
66
+ # Running bot
67
+
68
+ # Start bot. This methods freeze current thread until <tt>stop</tt> called.
69
+ def run
70
+ @on_start.each(&:call)
71
+
72
+ init_longpoll
73
+ run_longpoll
74
+
75
+ @on_finish.each(&:call)
76
+ end
77
+
78
+ # Stop bot.
79
+ def stop
80
+ @finish_flag = true
81
+ end
82
+
83
+ private
84
+
85
+ # Request longpoll data.
86
+ def init_longpoll
87
+ lp = api("groups.getLongPollServer", group_id: @id)
88
+ @longpoll[:server] = lp["server"]
89
+ @longpoll[:key] = lp["key"]
90
+ @longpoll[:ts] = lp["ts"]
91
+ end
92
+
93
+ # Start longpoll. Requires <tt>init_longpoll</tt> to be run first.
94
+ def run_longpoll
95
+ @finish_flag = false # Setting up flag for loop
96
+
97
+ until @finish_flag
98
+ response = Request.longpoll(@longpoll[:server], @longpoll[:key], @longpoll[:ts], @longpoll_wait) # TODO
99
+ if response["failed"]
100
+ # Error happened
101
+ Utility.warn "Longpoll failed with code #{response["failed"]}. This must be solvable. Keep running..."
102
+ case response["failed"]
103
+ when 1
104
+ # Just update ts
105
+ @longpoll[:ts] = response["ts"]
106
+ when 2, 3
107
+ # Need to reconnect
108
+ init_longpoll
109
+ else
110
+ raise Exceptions::LongpollError("Unknown 'failed' value: #{response["failed"]}. Full response: #{response.to_s}")
111
+ end
112
+ elsif response["ts"] && response["updates"]
113
+ # Everything is fine. Handling update
114
+ @longpoll[:ts] = response["ts"]
115
+ updates = response["updates"]
116
+ response["updates"].each { |update| update_handler(update) }
117
+ else
118
+ raise Exceptions::LongpollError("Strange longpoll response: #{response.to_s}")
119
+ end
120
+ end
121
+ end
122
+
123
+ # Handle update from longpoll.
124
+ def update_handler(update)
125
+ event = Events::Event.new(update["type"], update["object"], update["group_id"], self)
126
+ @event_listeners[event.subtype].each do |listener|
127
+ # NOTE: If we had any attributes, we would check whether matching here.
128
+ listener.call(event)
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ end
@@ -0,0 +1,12 @@
1
+ module VkLongpollBot
2
+
3
+ # Base of URL to API.
4
+ VK_API_URL_BASE = "https://api.vk.com"
5
+
6
+ # It's recommended to use last version of VK API.
7
+ VK_API_CURRENT_VERSION = Gem::Version.new("5.101")
8
+
9
+ # Longpoll requests timeout.
10
+ LONGPOLL_STANDART_WAIT = 25
11
+
12
+ end
@@ -0,0 +1,72 @@
1
+ module VkLongpollBot
2
+
3
+ # Everything related to longpoll events.
4
+ module Events
5
+
6
+ # All the types and subtypes of events
7
+ TYPES = {
8
+ message: %w{message_new message_reply message_edit message_typing_state message_allow message_deny},
9
+ photo: %w{photo_new photo_comment_new photo_comment_edit photo_comment_restore photo_comment_delete},
10
+ audio: %w{audio_new},
11
+ video: %w{video_new video_comment_new video_comment_restore video_comment_delete},
12
+ wall: %w{wall_post_new wall_repost wall_reply_new wall_reply_edit wall_reply_restore wall_reply_delete},
13
+ board: %w{board_post_new board_post_edit board_post_restore board_post_delete},
14
+ market: %w{market_comment_new market_comment_edit market_comment_restore market_comment_delete},
15
+ group: %w{group_leave group_join group_officers_edit group_change_settings group_change_photo},
16
+ user: %w{user_block user_unblock},
17
+ poll: %w{poll_vote_new},
18
+ vkpay: %w{vkpay_transaction},
19
+ app: %w{app_payload}
20
+ }
21
+
22
+ def self.valid_subtype?(subtype)
23
+ TYPES.values.any? { |arr| arr.include?(subtype) }
24
+ end
25
+
26
+ # Class containing data recieved from longpoll. Provides easy update to it's data.
27
+ class Event
28
+
29
+ attr_reader :subtype, :group_id, :data, :bot
30
+
31
+ # Initialize from fields of update json and bot which got this event.
32
+ def initialize(subtype, data, group_id, bot)
33
+ @subtype = subtype.to_s
34
+ @data = data
35
+ @group_id = group_id.to_i
36
+ @bot = bot
37
+ end
38
+
39
+ # Provides access to fields of update data.
40
+ def [](arg)
41
+ @data[arg.to_s]
42
+ end
43
+
44
+ # TODO
45
+
46
+ end
47
+
48
+ # NOTE: It might be better to create separate class for each event but there's lot of them and they don't have good hierarchy.
49
+
50
+ # Class containing block to run on some event.
51
+ class EventListener
52
+
53
+ attr_reader :subtype
54
+
55
+ def initialize(attributes, &block)
56
+ @subtype = attributes[:subtype]
57
+ @block = block
58
+
59
+ # TODO
60
+ end
61
+
62
+ def call(event)
63
+ @block.call(event)
64
+ end
65
+
66
+ # TODO
67
+
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,84 @@
1
+ module VkLongpollBot
2
+
3
+ # Custom exceptions.
4
+ module Exceptions
5
+
6
+ # All of the error codes descriptions. Source: https://vk.com/dev/errors
7
+ CODES = {
8
+ 1 => "Произошла неизвестная ошибка.",
9
+ 2 => "Приложение выключено.",
10
+ 3 => "Передан неизвестный метод.",
11
+ 4 => "Неверная подпись.",
12
+ 5 => "Авторизация пользователя не удалась.",
13
+ 6 => "Слишком много запросов в секунду.",
14
+ 7 => "Нет прав для выполнения этого действия.",
15
+ 8 => "Неверный запрос.",
16
+ 9 => "Слишком много однотипных действий.",
17
+ 10 => "Произошла внутренняя ошибка сервера.",
18
+ 11 => "В тестовом режиме приложение должно быть выключено или пользователь должен быть залогинен.",
19
+ 14 => "Требуется ввод кода с картинки (Captcha).",
20
+ 15 => "Доступ запрещён.",
21
+ 16 => "Требуется выполнение запросов по протоколу HTTPS, т.к. пользователь включил настройку, требующую работу через безопасное соединение.",
22
+ 17 => "Требуется валидация пользователя.",
23
+ 18 => "Страница удалена или заблокирована.",
24
+ 20 => "Данное действие запрещено для не Standalone приложений.",
25
+ 21 => "Данное действие разрешено только для Standalone и Open API приложений.",
26
+ 23 => "Метод был выключен.",
27
+ 24 => "Требуется подтверждение со стороны пользователя.",
28
+ 27 => "Ключ доступа сообщества недействителен.",
29
+ 28 => "Ключ доступа приложения недействителен.",
30
+ 29 => "Достигнут количественный лимит на вызов метода.",
31
+ 30 => "Профиль является приватным.",
32
+ 33 => "Метод ещё не реализован.",
33
+ 100 => "Один из необходимых параметров был не передан или неверен.",
34
+ 101 => "Неверный API ID приложения.",
35
+ 113 => "Неверный идентификатор пользователя.",
36
+ 150 => "Неверный timestamp.",
37
+ 200 => "Доступ к альбому запрещён.",
38
+ 201 => "Доступ к аудио запрещён.",
39
+ 203 => "Доступ к группе запрещён.",
40
+ 300 => "Альбом переполнен.",
41
+ 500 => "Действие запрещено. Вы должны включить переводы голосов в настройках приложения. ",
42
+ 600 => "Нет прав на выполнение данных операций с рекламным кабинетом.",
43
+ 603 => "Произошла ошибка при работе с рекламным кабинетом.",
44
+ 900 => "Нельзя отправлять сообщение пользователю из черного списка.",
45
+ 901 => "Пользователь запретил отправку сообщений от имени сообщества.",
46
+ 902 => "Нельзя отправлять сообщения этому пользователю в связи с настройками приватности.",
47
+ 911 => "Формат клавиатуры некорректен.",
48
+ 912 => "Это функция чат-бота, измените данный статус в настройках.",
49
+ 913 => "Слишком много пересланных сообщений.",
50
+ 914 => "Сообщение слишком длинное.",
51
+ 917 => "У вас нет доступа к этому чату.",
52
+ 921 => "Невозможно переслать выбранные сообщения.",
53
+ 936 => "Контакт не найден.",
54
+ 940 => "Слишком много постов в сообщении."
55
+ }
56
+
57
+ # Something wrong with response from vk.com.
58
+ class ResponseError < RuntimeError
59
+ end
60
+
61
+ # API error. Must have some code and description.
62
+ class APIError < ResponseError
63
+
64
+ attr_reader :code, :error
65
+
66
+ def initialize(error)
67
+ @error = error["error"]
68
+ @code = @error["error_code"]
69
+ super(@error["error_msg"])
70
+ end
71
+
72
+ def description
73
+ CODES[@code]
74
+ end
75
+
76
+ end
77
+
78
+ # Something wrong in longpoll response.
79
+ class LongpollError < ResponseError
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,34 @@
1
+ require "net/http"
2
+ require "json"
3
+
4
+ module VkLongpollBot
5
+
6
+ # Some functions to send HTTP requests
7
+ module Request
8
+
9
+ # Regular HTTP request to given URI.
10
+ def self.to(url)
11
+ uri = URI(url.to_s)
12
+ Net::HTTP.get(uri)
13
+ end
14
+
15
+ # Request to api.
16
+ def self.api(method_name, parameters, access_token, v = VK_API_CURRENT_VERSION)
17
+ response = JSON.parse self.to("#{VK_API_URL_BASE}/method/#{method_name}?access_token=#{access_token}&v=#{v.to_s}&#{URI.encode_www_form(parameters)}")
18
+ if response["response"]
19
+ response["response"]
20
+ elsif response["error"]
21
+ raise Exceptions::APIError.new(response)
22
+ else
23
+ raise Exceptions::ResponseError.new(response)
24
+ end
25
+ end
26
+
27
+ # Request to longpoll server.
28
+ def self.longpoll(server, key, ts, wait = LONGPOLL_STANDART_WAIT)
29
+ JSON.parse self.to("#{server}?act=a_check&key=#{key}&ts=#{ts}&wait=#{wait}")
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,22 @@
1
+ module VkLongpollBot
2
+
3
+ # Module with some utility methods.
4
+ module Utility
5
+
6
+ # Log warning.
7
+ def self.warn(msg)
8
+ if defined?(Warning.warn)
9
+ Warning.warn msg
10
+ else
11
+ STDERR.puts "Warning: #{msg}"
12
+ end
13
+ end
14
+
15
+ # Generate <tt>random_id</tt> for message.
16
+ def self.random_id(target_id)
17
+ (rand(100) * target_id) % 2**32
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,6 @@
1
+ require_relative "vk_longpoll_bot/utility.rb"
2
+ require_relative "vk_longpoll_bot/constants.rb"
3
+ require_relative "vk_longpoll_bot/exceptions.rb"
4
+ require_relative "vk_longpoll_bot/request.rb"
5
+ require_relative "vk_longpoll_bot/bot.rb"
6
+ require_relative "vk_longpoll_bot/events.rb"
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "vk_longpoll_bot"
3
+ s.summary = "Provides interface to create simple VK longpoll bot"
4
+ s.description = "Library to work with VK API and create simple longpoll bot for group."
5
+ s.version = "0.0.1"
6
+ s.author = "Kuznetsov Vladislav"
7
+ s.email = "fizvlad@mail.ru"
8
+ s.homepage = "https://github.com/fizvlad/vk-longpoll-bot-rb"
9
+ s.platform = Gem::Platform::RUBY
10
+ s.required_ruby_version = ">=2.3.1"
11
+ s.files = Dir[ "lib/**/**", "example/**/**", "LICENSE", "Rakefile", "README.md", "vk_longpoll_bot.gemspec" ]
12
+ s.license = "MIT"
13
+
14
+ s.add_runtime_dependency "rake", "~>12.3"
15
+ s.add_runtime_dependency "json", "~>2.2"
16
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vk_longpoll_bot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kuznetsov Vladislav
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-07-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '12.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '12.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.2'
41
+ description: Library to work with VK API and create simple longpoll bot for group.
42
+ email: fizvlad@mail.ru
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - LICENSE
48
+ - README.md
49
+ - Rakefile
50
+ - lib/vk_longpoll_bot.rb
51
+ - lib/vk_longpoll_bot/bot.rb
52
+ - lib/vk_longpoll_bot/constants.rb
53
+ - lib/vk_longpoll_bot/events.rb
54
+ - lib/vk_longpoll_bot/exceptions.rb
55
+ - lib/vk_longpoll_bot/request.rb
56
+ - lib/vk_longpoll_bot/utility.rb
57
+ - vk_longpoll_bot.gemspec
58
+ homepage: https://github.com/fizvlad/vk-longpoll-bot-rb
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 2.3.1
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.7.6.2
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Provides interface to create simple VK longpoll bot
82
+ test_files: []