rubirai 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +11 -0
  3. data/.github/workflows/CI.yml +64 -0
  4. data/.github/workflows/docs.yml +32 -0
  5. data/.github/workflows/pull_request.yml +34 -0
  6. data/.gitignore +145 -0
  7. data/.rubocop.yml +41 -0
  8. data/.yardopts +7 -0
  9. data/Gemfile +19 -0
  10. data/LICENSE +661 -0
  11. data/README.md +24 -0
  12. data/Rakefile +16 -0
  13. data/examples/helper.rb +3 -0
  14. data/examples/listener_example.rb +25 -0
  15. data/examples/simple_example.rb +24 -0
  16. data/lib/rubirai.rb +66 -0
  17. data/lib/rubirai/auth.rb +73 -0
  18. data/lib/rubirai/errors.rb +26 -0
  19. data/lib/rubirai/event_recv.rb +83 -0
  20. data/lib/rubirai/event_resp.rb +129 -0
  21. data/lib/rubirai/events/bot_events.rb +53 -0
  22. data/lib/rubirai/events/event.rb +115 -0
  23. data/lib/rubirai/events/message_events.rb +77 -0
  24. data/lib/rubirai/events/request_events.rb +35 -0
  25. data/lib/rubirai/events/rubirai_events.rb +17 -0
  26. data/lib/rubirai/listener.rb +44 -0
  27. data/lib/rubirai/listing.rb +37 -0
  28. data/lib/rubirai/management.rb +200 -0
  29. data/lib/rubirai/message.rb +84 -0
  30. data/lib/rubirai/messages/message.rb +306 -0
  31. data/lib/rubirai/messages/message_chain.rb +119 -0
  32. data/lib/rubirai/multipart.rb +44 -0
  33. data/lib/rubirai/objects/group.rb +23 -0
  34. data/lib/rubirai/objects/info.rb +71 -0
  35. data/lib/rubirai/objects/user.rb +30 -0
  36. data/lib/rubirai/plugin_info.rb +19 -0
  37. data/lib/rubirai/retcode.rb +18 -0
  38. data/lib/rubirai/session.rb +26 -0
  39. data/lib/rubirai/utils.rb +62 -0
  40. data/lib/rubirai/version.rb +9 -0
  41. data/misc/common.css +11 -0
  42. data/rubirai.gemspec +24 -0
  43. data/spec/auth_spec.rb +118 -0
  44. data/spec/error_spec.rb +30 -0
  45. data/spec/events/event_spec.rb +78 -0
  46. data/spec/message_spec.rb +12 -0
  47. data/spec/messages/message_chain_spec.rb +32 -0
  48. data/spec/messages/message_spec.rb +171 -0
  49. data/spec/plugin_info_spec.rb +28 -0
  50. data/spec/rubirai_bot_spec.rb +45 -0
  51. data/spec/spec_helper.rb +31 -0
  52. data/spec/utils_spec.rb +70 -0
  53. metadata +121 -0
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # Rubirai
2
+ [![CI](https://github.com/Shimogawa/rubirai/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/Shimogawa/rubirai/actions/workflows/CI.yml)
3
+ [![codecov](https://codecov.io/gh/Shimogawa/rubirai/branch/master/graph/badge.svg?token=OVUVEWFPKY)](https://codecov.io/gh/Shimogawa/rubirai)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/9a9d8c887e5deb601e1e/maintainability)](https://codeclimate.com/github/Shimogawa/rubirai/maintainability)
5
+ [![Inline docs](http://inch-ci.org/github/shimogawa/rubirai.svg?branch=master)](http://inch-ci.org/github/shimogawa/rubirai)
6
+ [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FShimogawa%2Frubirai.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FShimogawa%2Frubirai?ref=badge_shield)
7
+
8
+
9
+ A light-weight Mirai QQ bot http interface lib for Ruby.
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ require 'rubirai'
15
+ # assuming your mirai http api address and port
16
+ # are 127.0.0.1 and 8080
17
+ bot = Rubirai::Bot.new('127.0.0.1', '8080')
18
+ # qq and auth key
19
+ bot.login 1145141919, 'ikisugi_key'
20
+ ```
21
+
22
+
23
+ ## License
24
+ [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FShimogawa%2Frubirai.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FShimogawa%2Frubirai?ref=badge_large)
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.rspec_opts = %w[
6
+ --force-color
7
+ --format progress
8
+ --require ./spec/spec_helper.rb
9
+ ]
10
+ t.pattern = 'spec/**/*_spec.rb'
11
+ end
12
+
13
+ require 'rubocop/rake_task'
14
+ RuboCop::RakeTask.new(:rubocop)
15
+
16
+ task default: %i[spec rubocop]
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __dir__)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+ require 'rubirai'
5
+
6
+ bot = Rubirai::Bot.new '127.0.0.1', 8080
7
+
8
+ puts 'Enter qq: '
9
+ qq = gets.chomp
10
+ puts 'Enter auth key: '
11
+ auth = gets.chomp
12
+
13
+ bot.login(qq, auth)
14
+ puts 'Login successful.'
15
+
16
+ bot.add_listener do |event|
17
+ puts "#{event.class} -> #{event.raw}"
18
+ end
19
+
20
+ begin
21
+ bot.start_listen 0.5, is_blocking: true, ignore_error: false
22
+ rescue Interrupt
23
+ bot.logout
24
+ puts 'Bye'
25
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+ require 'rubirai'
5
+
6
+ bot = Rubirai::Bot.new '127.0.0.1', 8080
7
+
8
+ puts 'Enter qq: '
9
+ qq = gets.chomp
10
+ puts 'Enter auth key: '
11
+ auth = gets.chomp
12
+
13
+ bot.login(qq, auth)
14
+
15
+ puts 'Login successful. Enter qq to send message to:'
16
+ target = gets.chomp
17
+
18
+ bot.send_friend_msg(
19
+ target,
20
+ 'hello', ' world!',
21
+ Rubirai::ImageMessage(url: 'https://i0.hdslb.com/bfs/album/67fc4e6b417d9c68ef98ba71d5e79505bbad97a1.png')
22
+ )
23
+
24
+ bot.logout
data/lib/rubirai.rb ADDED
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubirai/errors'
4
+ require 'rubirai/utils'
5
+
6
+ # Rubirai is a library for connecting Mirai http api.
7
+ module Rubirai
8
+ require 'http'
9
+
10
+ # Bot represents a QQ bot at mirai side. All functions are API calls to the http plugin.
11
+ class Bot
12
+ # @!attribute [r] base_uri
13
+ # @return [String] the base uri of mirai-api-http which the bot will send messages to
14
+ # @!attribute [r] session
15
+ # @return [String] the session key
16
+ # @!attribute [r] qq
17
+ # @return [String, Integer] the qq of the bot
18
+ attr_reader :base_uri, :session, :qq
19
+
20
+ # Initializes the bot
21
+ #
22
+ # @param host [String] the host (IP or domain)
23
+ # @param port [String, Integer, nil] the port number (default is 80 for http)
24
+ def initialize(host, port = nil)
25
+ @base_uri = "http://#{host}#{":#{port}" if port}"
26
+ @listener_funcs = []
27
+ end
28
+
29
+ def gen_uri(path)
30
+ URI.join(base_uri, path)
31
+ end
32
+
33
+ def self.ensure_type_in(type, *types)
34
+ types = types.map { |x| x.to_s.downcase }
35
+ type.to_s.downcase.must_be_one_of! types, RubiraiError, "not valid type: should be one of #{types}"
36
+ end
37
+
38
+ private
39
+
40
+ def call(method, path, **kwargs)
41
+ return unless %i[get post].include?(method) && HTTP.respond_to?(method)
42
+
43
+ resp = HTTP.send method, gen_uri(path), kwargs
44
+ raise(HttpResponseError, resp.code) unless resp.status.success?
45
+
46
+ body = JSON.parse(resp.body)
47
+ if (body.is_a? Hash) && (body.include? 'code') && (body['code'] != 0)
48
+ raise MiraiError.new(body['code'], body['msg'] || body['errorMessage'])
49
+ end
50
+
51
+ body
52
+ end
53
+ end
54
+ end
55
+
56
+ require 'rubirai/auth'
57
+ require 'rubirai/event_recv'
58
+ require 'rubirai/listener'
59
+ require 'rubirai/listing'
60
+ require 'rubirai/management'
61
+ require 'rubirai/message'
62
+ require 'rubirai/multipart'
63
+ require 'rubirai/plugin_info'
64
+ require 'rubirai/retcode'
65
+ require 'rubirai/session'
66
+ require 'rubirai/version'
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubirai
4
+ class Bot
5
+ # Start authentication. Will store the session.
6
+ # @param auth_key [String] the auth key defined in config file
7
+ # @return [String] the session key which will also be stored in the bot
8
+ def auth(auth_key)
9
+ v = call :post, '/auth', json: { "authKey": auth_key }
10
+ @session = v['session']
11
+ end
12
+
13
+ # Verify and start a session. Also bind the session to a bot with the qq id.
14
+ # @param qq [String, Integer] qq id
15
+ # @param session [String, nil] the session key. Set to `nil` will use the saved credentials.
16
+ # @return [void]
17
+ def verify(qq, session = nil)
18
+ check qq, session
19
+
20
+ call :post, '/verify', json: { "sessionKey": @session || session, "qq": qq.to_i }
21
+ @session = session if session
22
+ @qq = qq
23
+ nil
24
+ end
25
+
26
+ # Release a session.
27
+ # Only fill in the arguments when you want to control another bot on the same Mirai process.
28
+ # @param qq [String, Integer, nil] qq id. Set to `nil` will use the logged in bot id.
29
+ # @param session [String, nil] the session key. Set to `nil` will use the logged in credentials.
30
+ # @return [void]
31
+ def release(qq = nil, session = nil)
32
+ qq ||= @qq
33
+ raise RubiraiError, "not same qq: #{qq} and #{@qq}" if qq != @qq
34
+ check qq, session
35
+
36
+ call :post, '/release', json: { "sessionKey": @session || session, "qq": qq.to_i }
37
+ @session = nil
38
+ @qq = nil
39
+ nil
40
+ end
41
+
42
+ # Log you in.
43
+ #
44
+ # @param qq [String, Integer] qq id
45
+ # @param auth_key [String] the auth key set in the settings file for mirai-api-http.
46
+ # @return [void]
47
+ # @see #auth
48
+ # @see #verify
49
+ def login(qq, auth_key)
50
+ auth auth_key
51
+ verify qq
52
+ end
53
+
54
+ alias connect login
55
+
56
+ # Log you out.
57
+ #
58
+ # @return [void]
59
+ # @see #release
60
+ def logout
61
+ release
62
+ end
63
+
64
+ alias disconnect logout
65
+
66
+ private
67
+
68
+ def check(qq, session = nil)
69
+ raise RubiraiError, 'Wrong format for qq' unless qq.to_i.to_s == qq.to_s
70
+ raise RubiraiError, 'No session provided' unless @session || session
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubirai/retcode'
4
+
5
+ module Rubirai
6
+ # Represent all Rubirai errors
7
+ class RubiraiError < RuntimeError
8
+ end
9
+
10
+ # Http response error
11
+ class HttpResponseError < RubiraiError
12
+ def initialize(code)
13
+ super "Http Error: #{code}"
14
+ end
15
+ end
16
+
17
+ # Mirai error
18
+ class MiraiError < RubiraiError
19
+ def initialize(code, msg = nil)
20
+ raise(RubiraiError, 'invalid mirai error code') unless Rubirai::RETURN_CODE.key? code
21
+ str = +"Mirai error: #{code} - #{Rubirai::RETURN_CODE[code]}"
22
+ str << "\n#{msg}" if msg
23
+ super str
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubirai/events/event'
4
+
5
+ module Rubirai
6
+ class Bot
7
+ # Fetch `count` number of oldest events.
8
+ # @param count [Integer] the number of events to fetch
9
+ # @return [Array<Event>] the event objects.
10
+ def fetch_message(count = 10)
11
+ get_events '/fetchMessage', count
12
+ end
13
+
14
+ alias fetch_messages fetch_message
15
+ alias fetch_event fetch_message
16
+ alias fetch_events fetch_message
17
+
18
+ # Fetch `count` number of latest events.
19
+ # @param count [Integer] the number of events to fetch
20
+ # @return [Array<Event>] the event objects
21
+ def fetch_latest_message(count = 10)
22
+ get_events '/fetchLatestMessage', count
23
+ end
24
+
25
+ alias fetch_latest_messages fetch_latest_message
26
+ alias fetch_latest_event fetch_latest_message
27
+ alias fetch_latest_events fetch_latest_message
28
+
29
+ # Peek `count` number of oldest events. (Will not delete from cache)
30
+ # @param count [Integer] the number of events to peek
31
+ # @return [Array<Event>] the event objects
32
+ def peek_message(count = 10)
33
+ get_events '/peekMessage', count
34
+ end
35
+
36
+ alias peek_messages peek_message
37
+ alias peek_event peek_message
38
+ alias peek_events peek_message
39
+
40
+ # Peek `count` number of latest events. (Will not delete from cache)
41
+ # @param count [Integer] the number of events to peek
42
+ # @return [Array<Event>] the event objects
43
+ def peek_latest_message(count = 10)
44
+ get_events '/peekLatestMessage', count
45
+ end
46
+
47
+ alias peek_latest_messages peek_latest_message
48
+ alias peek_latest_event peek_latest_message
49
+ alias peek_latest_events peek_latest_message
50
+
51
+ # Get a message event from message id
52
+ # @param msg_id [Integer] message id
53
+ # @return [Event] the event object
54
+ def message_from_id(msg_id)
55
+ resp = call :get, '/messageFromId', params: {
56
+ sessionKey: @session,
57
+ id: msg_id
58
+ }
59
+ Event.parse resp['data'], self
60
+ end
61
+
62
+ # Get the number of cached messages in mirai-http-api
63
+ # @return [Integer] the number of cached messages
64
+ def count_cached_message
65
+ resp = call :get, '/countMessage', params: {
66
+ sessionKey: @session
67
+ }
68
+ resp['data']
69
+ end
70
+
71
+ private
72
+
73
+ def get_events(path, count)
74
+ resp = call :get, path, params: {
75
+ sessionKey: @session,
76
+ count: count
77
+ }
78
+ resp['data'].map do |event|
79
+ Event.parse event, self
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubirai/events/event'
4
+
5
+ module Rubirai
6
+ # Operations for responding to friend requests.
7
+ # Only use the values defined in this module to respond to friend requests.
8
+ module FriendRequestOperation
9
+ # Approve the request
10
+ APPROVE = 0
11
+
12
+ # Deny the request
13
+ DENY = 1
14
+
15
+ # Deny and blacklist the sender of the request
16
+ DENY_AND_BLACKLIST = 2
17
+ end
18
+
19
+ # Operations for responding to group join requests.
20
+ # Only use the values defined in this module to respond to group join requests.
21
+ module JoinGroupRequestOperation
22
+ APPROVE = 0
23
+ DENY = 1
24
+ IGNORE = 2
25
+ DENY_AND_BLACKLIST = 3
26
+ IGNORE_AND_BLACKLIST = 4
27
+ end
28
+
29
+ # Operations for responding to group invite requests.
30
+ # Only use the values defined in this module to respond to group invite requests.
31
+ module GroupInviteRequestOperation
32
+ APPROVE = 0
33
+ DENY = 1
34
+ end
35
+
36
+ class Bot
37
+ # Respond to new friend request (raw)
38
+ #
39
+ # @param event_id [Integer] the event id
40
+ # @param from_id [Integer] id of the requester
41
+ # @param operation [Integer] see {FriendRequestOperation}.
42
+ # @param group_id [Integer] the group where the request is from. 0 if not from group.
43
+ # @param message [String] the message to reply
44
+ # @return [void]
45
+ def respond_to_new_friend_request(event_id, from_id, operation, group_id = 0, message = '')
46
+ call :post, '/resp/newFriendRequestEvent', json: {
47
+ sessionKey: @session,
48
+ eventId: event_id,
49
+ fromId: from_id,
50
+ groupId: group_id,
51
+ operate: operation,
52
+ message: message
53
+ }
54
+ nil
55
+ end
56
+
57
+ # Respond to join group request (raw)
58
+ #
59
+ # @param event_id [Integer] the event id
60
+ # @param from_id [Integer] id of the requester
61
+ # @param operation [Integer] see {JoinGroupRequestOperation}
62
+ # @param group_id [Integer] the group where the request is from. 0 if not from group.
63
+ # @param message [String] the message to reply
64
+ # @return [void]
65
+ def respond_to_member_join(event_id, from_id, group_id, operation, message = '')
66
+ call :post, '/resp/memberJoinRequestEvent', json: {
67
+ sessionKey: @session,
68
+ eventId: event_id,
69
+ fromId: from_id,
70
+ groupId: group_id,
71
+ operate: operation,
72
+ message: message
73
+ }
74
+ nil
75
+ end
76
+
77
+ def respond_to_group_invite(event_id, from_id, group_id, operation, message = '')
78
+ call :post, '/resp/botInvitedJoinGroupRequestEvent', json: {
79
+ sessionKey: @session,
80
+ eventId: event_id,
81
+ fromId: from_id,
82
+ groupId: group_id,
83
+ operate: operation,
84
+ message: message
85
+ }
86
+ nil
87
+ end
88
+ end
89
+
90
+ class RequestEvent
91
+ # @abstract
92
+ def respond(operation, message)
93
+ raise NotImplementedError
94
+ end
95
+ end
96
+
97
+ class NewFriendRequestEvent
98
+ # Respond to the friend request.
99
+ #
100
+ # @param operation [Integer] see {FriendRequestOperation}
101
+ # @param message [String] the message to reply
102
+ # @return [void]
103
+ def respond(operation, message = '')
104
+ @bot.respond_to_new_friend_request @event_id, @from_id, operation, @group_id, message
105
+ end
106
+ end
107
+
108
+ class JoinGroupRequestEvent
109
+ # Respond to the friend request.
110
+ #
111
+ # @param operation [Integer] see {JoinGroupRequestOperation}
112
+ # @param message [String] the message to reply
113
+ # @return [void]
114
+ def respond(operation, message = '')
115
+ @bot.respond_to_member_join @event_id, @from_id, @group_id, operation, message
116
+ end
117
+ end
118
+
119
+ class BotInvitedToGroupEvent
120
+ # Respond to the group invitation.
121
+ #
122
+ # @param operation [Integer] see {GroupInviteRequestOperation}
123
+ # @param message [String] the message to reply
124
+ # @return [void]
125
+ def respond(operation, message = '')
126
+ @bot.respond_to_group_invite @event_id, @from_id, @group_id, operation, message
127
+ end
128
+ end
129
+ end