class Rubirai::Bot
Bot
represents a QQ bot at mirai side. All functions are API calls to the http plugin.
@!attribute [r] base_uri
@return [String] the base uri of mirai-api-http which the bot will send messages to
@!attribute [r] session
@return [String] the session key
@!attribute [r] qq
@return [String, Integer] the qq of the bot
Attributes
Public Class Methods
@private
# File lib/rubirai.rb, line 46 def self.ensure_type_in(type, *types) types = types.map { |x| x.to_s.downcase } type.to_s.downcase.must_be_one_of! types, RubiraiError, "not valid type: should be one of #{types}" end
Initializes the bot
@param host [String] the host (IP or domain) @param port [String, Integer, nil] the port number (default is 80 for http)
# File lib/rubirai.rb, line 35 def initialize(host, port = nil) @base_uri = "http://#{host}#{":#{port}" if port}" @listener_funcs = [] end
Public Instance Methods
Get Mirai API plugin information.
The information is like “`ruby {
'version' => '1.0.0'
} “`
@return [Hash{String => Object}] plugin data
# File lib/rubirai/plugin_info.rb, line 15 def about v = call :get, '/about' v['data'] end
Add a listener
@return [void]
# File lib/rubirai/listener.rb, line 39 def add_listener(&listener_block) @listener_funcs << listener_block end
Start authentication. Will store the session. @param auth_key [String] the auth key defined in config file @return [String] the session key which will also be stored in the bot
# File lib/rubirai/auth.rb, line 8 def auth(auth_key) v = call :post, '/auth', json: { "authKey": auth_key } @session = v['session'] end
Clear all listeners
@return [void]
# File lib/rubirai/listener.rb, line 46 def clear_listener @listener_funcs.clear end
Get the number of cached messages in mirai-http-api @return [Integer] the number of cached messages
# File lib/rubirai/event_recv.rb, line 64 def count_cached_message resp = call :get, '/countMessage', params: { sessionKey: @session } resp['data'] end
Fetch `count` number of latest events. @param count [Integer] the number of events to fetch @return [Array<Event>] the event objects
# File lib/rubirai/event_recv.rb, line 21 def fetch_latest_message(count = 10) get_events '/fetchLatestMessage', count end
Fetch `count` number of oldest events. @param count [Integer] the number of events to fetch @return [Array<Event>] the event objects.
# File lib/rubirai/event_recv.rb, line 10 def fetch_message(count = 10) get_events '/fetchMessage', count end
Get friend list of the bot @return [Array<User>] list of friends
# File lib/rubirai/listing.rb, line 10 def friend_list resp = call :get, '/friendList', params: { sessionKey: @session } resp.map { |friend| User.new(friend, self) } end
@private
# File lib/rubirai.rb, line 41 def gen_uri(path) URI.join(base_uri, path) end
Get group config
@param group_id [Integer] group id @return [GroupConfig] the config
# File lib/rubirai/management.rb, line 93 def get_group_config(group_id) resp = call :get, '/groupConfig', params: { sessionKey: @session, target: group_id } GroupConfig.new resp, self end
Get the info about a group file @param group_id [Integer] the group id @param file_or_id [GroupFileSimple, String] the file id, e.g. `/xxx-xxx-xxx-xxx`
@return [GroupFile] the info about the file
# File lib/rubirai/management.rb, line 175 def get_group_file_info(group_id, file_or_id) file_or_id.must_be! [GroupFileSimple, String], RubiraiError, 'must be GroupFileSimple or String' raise RubiraiError, "#{file_or_id} is not a file" if file_or_id.is_a?(GroupFileSimple) && !file_or_id.is_file? id = file_or_id.is_a? String ? file_or_id : file_or_id.id resp = call :get, '/groupFileInfo', params: { sessionKey: @session, target: group_id, id: id } GroupFile.new resp, self end
Get the group files as a list
@param group_id [Integer] the group id @param dir [String, nil] the directory to get. If `nil`, then the root directory is asserted. @return [Array<GroupFileSimple>] the list of files
# File lib/rubirai/management.rb, line 160 def get_group_file_list(group_id, dir = nil) resp = call :get, '/groupFileList', params: { sessionKey: @session, target: group_id, dir: dir }.compact resp.must_be! Array # assert resp is Array resp.map { |f| GroupFileSimple.new(f, self) } end
Get member info
@param group_id [Integer] group id @param member_id [Integer] the member's qq id @return [MemberInfo] the member's info
# File lib/rubirai/management.rb, line 125 def get_member_info(group_id, member_id) resp = call :get, '/memberInfo', params: { sessionKey: @session, target: group_id, memberId: member_id } MemberInfo.new resp, self end
Get the config related to this session @return [Hash{String => Object}] the config
# File lib/rubirai/session.rb, line 7 def get_session_cfg call :get, '/config', params: { sessionKey: @session } end
# File lib/rubirai/management.rb, line 217 def group_file_delete(group_id, file_id) call :post, '/groupFileDelete', json: { sessionKey: @session, target: group_id, id: file_id } nil end
# File lib/rubirai/management.rb, line 207 def group_file_mv(group_id, file_id, path) call :post, '/groupFileMove', json: { sessionKey: @session, target: group_id, id: file_id, movePath: path } nil end
Get group list of the bot @return [Array<Group>] list of groups
# File lib/rubirai/listing.rb, line 19 def group_list resp = call :get, '/groupList', params: { sessionKey: @session } resp.map { |group| Group.new(group, self) } end
# File lib/rubirai/management.rb, line 198 def group_mkdir(group_id, dir) call :post, '/groupMkdir', json: { sessionKey: @session, group: group_id, dir: dir } nil end
# File lib/rubirai/management.rb, line 226 def group_set_essence(msg_id) call :post, '/setEssence', json: { sessionKey: @session, target: msg_id } nil end
Kick a member from a group
@param group_id [Integer] group id @param member_id [Integer] member id @param msg [String, nil] the message for the kicked @return [void]
# File lib/rubirai/management.rb, line 42 def kick(group_id, member_id, msg = nil) json = { sessionKey: @session, target: group_id, memberId: member_id } json[:msg] = msg if msg call :post, '/kick', json: json nil end
Log you in.
@param qq [String, Integer] qq id @param auth_key [String] the auth key set in the settings file for mirai-api-http. @return [void] @see auth
@see verify
# File lib/rubirai/auth.rb, line 49 def login(qq, auth_key) auth auth_key verify qq end
Get member list of a group @param group_id [Integer, String] group id @return [Array<GroupUser>] list of members
# File lib/rubirai/listing.rb, line 29 def member_list(group_id) resp = call :get, '/memberList', params: { sessionKey: @session, target: group_id } resp.map { |member| GroupUser.new(member, self) } end
Get a message event from message id @param msg_id [Integer] message id @return [Event] the event object
# File lib/rubirai/event_recv.rb, line 54 def message_from_id(msg_id) resp = call :get, '/messageFromId', params: { sessionKey: @session, id: msg_id } Event.parse resp['data'], self end
Mute a group member
@param group_id [Integer] group id @param member_id [Integer] member id @param time [Integer] the mute time
# File lib/rubirai/management.rb, line 12 def mute(group_id, member_id, time = 0) call :post, '/mute', json: { sessionKey: @session, target: group_id, memberId: member_id, time: time } nil end
Mute all in a group
@param group_id [Integer] group id @return [void]
# File lib/rubirai/management.rb, line 69 def mute_all(group_id) call :post, '/muteAll', json: { sessionKey: @session, target: group_id } nil end
Peek `count` number of latest events. (Will not delete from cache) @param count [Integer] the number of events to peek @return [Array<Event>] the event objects
# File lib/rubirai/event_recv.rb, line 43 def peek_latest_message(count = 10) get_events '/peekLatestMessage', count end
Peek `count` number of oldest events. (Will not delete from cache) @param count [Integer] the number of events to peek @return [Array<Event>] the event objects
# File lib/rubirai/event_recv.rb, line 32 def peek_message(count = 10) get_events '/peekMessage', count end
Quit a group
@param group_id [Integer] group id @return [void]
# File lib/rubirai/management.rb, line 57 def quit(group_id) call :post, '/quit', json: { sessionKey: @session, target: group_id } nil end
Recall a message
@param msg_id [Integer] message id @return [void]
# File lib/rubirai/message.rb, line 64 def recall(msg_id) call :post, '/recall', json: { sessionKey: @session, target: msg_id } nil end
Release a session. Only fill in the arguments when you want to control another bot on the same Mirai process. @param qq [String, Integer, nil] qq id. Set to `nil` will use the logged in bot id. @param session [String, nil] the session key. Set to `nil` will use the logged in credentials. @return [void]
# File lib/rubirai/auth.rb, line 31 def release(qq = nil, session = nil) qq ||= @qq raise RubiraiError, "not same qq: #{qq} and #{@qq}" if qq != @qq check qq, session call :post, '/release', json: { "sessionKey": @session || session, "qq": qq.to_i } @session = nil @qq = nil nil end
# File lib/rubirai/management.rb, line 188 def rename_group_file(group_id, file_id, new_name) call :post, '/groupFileRename', json: { sessionKey: @session, target: group_id, id: file_id, rename: new_name } nil end
Respond to group invitations (raw)
@param event_id [Integer] the event id @param from_id [Integer] id of the sender @param group_id [Integer] the group id @param operation [Integer] see {GroupInviteRequestOperation} @param message [String] the message to reply @return [void]
# File lib/rubirai/event_resp.rb, line 97 def respond_to_group_invite(event_id, from_id, group_id, operation, message = '') call :post, '/resp/botInvitedJoinGroupRequestEvent', json: { sessionKey: @session, eventId: event_id, fromId: from_id, groupId: group_id, operate: operation, message: message } nil end
Respond to join group request (raw)
@param event_id [Integer] the event id @param from_id [Integer] id of the requester @param operation [Integer] see {JoinGroupRequestOperation} @param group_id [Integer] the group where the request is from. 0 if not from group. @param message [String] the message to reply @return [void]
# File lib/rubirai/event_resp.rb, line 77 def respond_to_member_join(event_id, from_id, group_id, operation, message = '') call :post, '/resp/memberJoinRequestEvent', json: { sessionKey: @session, eventId: event_id, fromId: from_id, groupId: group_id, operate: operation, message: message } nil end
Respond to new friend request (raw)
@param event_id [Integer] the event id @param from_id [Integer] id of the requester @param operation [Integer] see {FriendRequestOperation}. @param group_id [Integer] the group where the request is from. 0 if not from group. @param message [String] the message to reply @return [void]
# File lib/rubirai/event_resp.rb, line 57 def respond_to_new_friend_request(event_id, from_id, operation, group_id = 0, message = '') call :post, '/resp/newFriendRequestEvent', json: { sessionKey: @session, eventId: event_id, fromId: from_id, groupId: group_id, operate: operation, message: message } nil end
Send friend message
@param target_qq [Integer] target qq id @param msgs [Array<Rubirai::Message, Hash
, String
, Object>] messages to form a chain, can be any type @return [Integer] message id
# File lib/rubirai/message.rb, line 47 def send_friend_msg(target_qq, *msgs) send_msg :friend, target_qq, *msgs end
Send group message
@param target_group_id [Integer] group id @param msgs [Array<Rubirai::Message, Hash
, String
, Object>] messages to form a chain, can be any type @return [Integer] message id
# File lib/rubirai/message.rb, line 56 def send_group_msg(target_group_id, *msgs) send_msg :group, target_group_id, *msgs end
Send image messages
@param urls [Array<String>] the urls of the images @param kwargs [Hash] keys are one of [target, qq, group]. @return [Array<String>] the image ids
# File lib/rubirai/message.rb, line 77 def send_image_msg(urls, **kwargs) urls.must_be! Array urls.each do |url| url.must_be! String end valid = %w[target qq group] res = { sessionKey: @session, urls: urls } kwargs.each do |k, v| res[k.to_s.downcase.to_sym] = v if valid.include? k.to_s.downcase end call :post, '/sendImageMessage', json: res end
Send friend or group message
@param type [Symbol, String] options: [group, friend] @param target_id [Integer] target qq/group id @param msgs [Array<Rubirai::Message, Hash
, String
, Object>] messages to form a chain, can be any type @return [Integer] message id
# File lib/rubirai/message.rb, line 31 def send_msg(type, target_id, *msgs) self.class.ensure_type_in type, 'group', 'friend' chain = Rubirai::MessageChain.make(*msgs, bot: self) resp = call :post, "/send#{type.to_s.snake_to_camel}Message", json: { sessionKey: @session, target: target_id.to_i, messageChain: chain.to_a } resp['messageId'] end
Send nudge
@param target_id [Integer] target id @param subject_id [Integer] the id of the place where the nudge will be seen.
Should be a group id or a friend's id
@param kind [String, Symbol] options: `[Group, Friend]` @return [void]
# File lib/rubirai/message.rb, line 100 def send_nudge(target_id, subject_id, kind) kind.to_s.downcase.must_be_one_of! %w[group friend], RubiraiError, 'kind must be one of group or friend' call :post, '/sendNudge', json: { sessionKey: @session, target: target_id, subject: subject_id, kind: kind.to_s.capitalize } nil end
Send temp message
@param target_qq [Integer] target qq id @param group_id [Integer] group id @param msgs [Array<Rubirai::Message, Hash
, String
, Object>] messages to form a chain, can be any type @return [Integer] message id
# File lib/rubirai/message.rb, line 14 def send_temp_msg(target_qq, group_id, *msgs) chain = Rubirai::MessageChain.make(*msgs, bot: self) resp = call :post, '/sendTempMessage', json: { sessionKey: @session, qq: target_qq.to_i, group: group_id.to_i, messageChain: chain.to_a } resp['messageId'] end
Set group config
@param group_id [Integer] group id @param config [GroupConfig, Hash{String, Symbol => Object}] the configuration. If given as
a hash, then it should be exactly named the same as given in `mirai-api-http` docs.
@return [void]
# File lib/rubirai/management.rb, line 108 def set_group_config(group_id, config) config.must_be! [GroupConfig, Hash], RubiraiError, 'must be GroupConfig or Hash' config.stringify_keys! if config.is_a? Hash config = config.to_h if config.is_a? GroupConfig call :post, '/groupConfig', json: { sessionKey: @session, target: group_id, config: config } nil end
Set member info
@param group_id [Integer] group id @param member_id [Integer] the member's qq id @param info [MemberInfo, Hash{String,Symbol => Object}] the info to set. If given as
a hash, then it should be exactly named the same as given in `mirai-api-http` docs.
@return [void]
# File lib/rubirai/management.rb, line 142 def set_member_info(group_id, member_id, info) info.must_be! [MemberInfo, Hash], RubiraiError, 'must be MemberInfo or Hash' info.stringify_keys! if info.is_a? Hash info = info.to_h if info.is_a? MemberInfo call :post, '/memberInfo', json: { sessionKey: @session, target: group_id, memberId: member_id, info: info } nil end
Set the config related to this session @param cache_size [Integer, nil] the cache size @param enable_websocket [Boolean, nil] if to enable websocket @return [void]
# File lib/rubirai/session.rb, line 17 def set_session_cfg(cache_size: nil, enable_websocket: nil) call :post, '/config', json: { sessionKey: @session, cacheSize: cache_size, enableWebsocket: enable_websocket }.compact nil end
Start to listen for events
@param interval [Numeric] the interval to fetch events in seconds. @param is_blocking [Boolean] if the listen thread should block the current thread @param ignore_error [Boolean] if errors should generate error events (see {RubiraiErrorEvent}) @return [void]
# File lib/rubirai/listener.rb, line 14 def start_listen(interval, is_blocking: false, ignore_error: false) raise RubiraiError, 'listener is already running' if @listener&.running? @listener_stop_event = Concurrent::Event.new if is_blocking bot = self @listener = Concurrent::TimerTask.new(execution_interval: interval) do loop do events = fetch_message events.each do |e| @listener_funcs.each { |f| f.call e } rescue RuntimeError => e @listener_funcs.each { |f| f.call RubiraiErrorEvent.new(e, bot) unless ignore_error } end break if events.length < 10 rescue RuntimeError => e @listener_funcs.each { |f| f.call RubiraiErrorEvent.new(e, bot) unless ignore_error } break end end @listener.execute @listener_stop_event.wait if is_blocking end
Stop listening to events. Will unblock the thread if `is_blocking` is `true` when calling {#start_listen}
@return [void]
# File lib/rubirai/listener.rb, line 54 def stop_listen @listener.shutdown @listener_stop_event&.set @listener_stop_event = nil end
Unmute a group member
@param group_id [Integer] group id @param member_id [Integer] member id @return [void]
# File lib/rubirai/management.rb, line 27 def unmute(group_id, member_id) call :post, '/unmute', json: { sessionKey: @session, target: group_id, memberId: member_id } nil end
Unmute all in a group
@param group_id [Integer] group id @return [void]
# File lib/rubirai/management.rb, line 81 def unmute_all(group_id) call :post, '/unmuteAll', json: { sessionKey: @session, target: group_id } nil end
Uploads a file to a group (currently only groups supported) @param path_or_io [String, Pathname, IO] path to file @param target [Integer] group id @param group_path [String] path to file in group files @return [String] the string id of the mirai file
# File lib/rubirai/multipart.rb, line 33 def upload_file_and_send(path_or_io, target, group_path) res = call :post, '/uploadFileAndSend', form: { sessionKey: @session, type: 'Group', target: target, path: group_path, file: HTTP::FormData::File.new(path_or_io) }, headers: { content_type: 'multipart/form-data' } res['id'] end
Uploads an image to QQ server @return [Hash] hash string keys are: `{ imageId, url, path }`
# File lib/rubirai/multipart.rb, line 7 def upload_image(path_or_io, type = :friend) self.class.ensure_type_in type, 'friend', 'group', 'temp' call :post, '/uploadImage', form: { sessionKey: @session, type: type.to_s.downcase, img: HTTP::FormData::File.new(path_or_io) }, headers: { content_type: 'multipart/form-data' } end
Uploads a voice file to QQ server Only group uploads available currently. @param path_or_io [String, Pathname, IO] path to voice file @return [Hash] hash string keys are: `{ voiceId, url, path }`
# File lib/rubirai/multipart.rb, line 20 def upload_voice(path_or_io) call :post, '/uploadVoice', form: { sessionKey: @session, type: 'group', img: HTTP::FormData::File.new(path_or_io) }, headers: { content_type: 'multipart/form-data' } end
Verify and start a session. Also bind the session to a bot with the qq id. @param qq [String, Integer] qq id @param session [String, nil] the session key. Set to `nil` will use the saved credentials. @return [void]
# File lib/rubirai/auth.rb, line 17 def verify(qq, session = nil) check qq, session call :post, '/verify', json: { "sessionKey": @session || session, "qq": qq.to_i } @session = session if session @qq = qq nil end
Private Instance Methods
# File lib/rubirai.rb, line 53 def call(method, path, **kwargs) return unless %i[get post].include?(method) && HTTP.respond_to?(method) resp = HTTP.send method, gen_uri(path), kwargs raise(HttpResponseError, resp.code) unless resp.status.success? body = JSON.parse(resp.body) if (body.is_a? Hash) && (body.include? 'code') && (body['code'] != 0) raise MiraiError.new(body['code'], body['msg'] || body['errorMessage']) end body end
# File lib/rubirai/auth.rb, line 68 def check(qq, session = nil) raise RubiraiError, 'Wrong format for qq' unless qq.to_i.to_s == qq.to_s raise RubiraiError, 'No session provided' unless @session || session end
# File lib/rubirai/event_recv.rb, line 73 def get_events(path, count) resp = call :get, path, params: { sessionKey: @session, count: count } resp['data'].map do |event| Event.parse event, self end end