vk_longpoll_bot 0.0.1

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
+ 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: []