rubirai 0.0.2

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.
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