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

base_uri[R]
id[R]
qq[R]
session[R]

Public Class Methods

ensure_type_in(type, *types) click to toggle source

@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
new(host, port = nil) click to toggle source

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

about() click to toggle source

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_listener(&listener_block) click to toggle source

Add a listener

@return [void]

# File lib/rubirai/listener.rb, line 39
def add_listener(&listener_block)
  @listener_funcs << listener_block
end
auth(auth_key) click to toggle source

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_listener() click to toggle source

Clear all listeners

@return [void]

# File lib/rubirai/listener.rb, line 46
def clear_listener
  @listener_funcs.clear
end
connect(qq, auth_key)
Alias for: login
count_cached_message() click to toggle source

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
disconnect()
Alias for: logout
fetch_event(count = 10)
Alias for: fetch_message
fetch_events(count = 10)
Alias for: fetch_message
fetch_latest_event(count = 10)
fetch_latest_events(count = 10)
fetch_latest_message(count = 10) click to toggle source

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_latest_messages(count = 10)
fetch_message(count = 10) click to toggle source

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
fetch_messages(count = 10)
Alias for: fetch_message
friend_list() click to toggle source

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
gen_uri(path) click to toggle source

@private

# File lib/rubirai.rb, line 41
def gen_uri(path)
  URI.join(base_uri, path)
end
get_group_config(group_id) click to toggle source

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_group_file_info(group_id, file_or_id) click to toggle source

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_group_file_list(group_id, dir = nil) click to toggle source

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(group_id, member_id) click to toggle source

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_session_cfg() click to toggle source

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
group_file_delete(group_id, file_id) click to toggle source
# 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
group_file_mv(group_id, file_id, path) click to toggle source
# 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
group_list() click to toggle source

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
group_mkdir(group_id, dir) click to toggle source
# 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
group_set_essence(msg_id) click to toggle source
# 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(group_id, member_id, msg = nil) click to toggle source

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
login(qq, auth_key) click to toggle source

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
Also aliased as: connect
logout() click to toggle source

Log you out.

@return [void] @see release

# File lib/rubirai/auth.rb, line 60
def logout
  release
end
Also aliased as: disconnect
member_list(group_id) click to toggle source

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
message_from_id(msg_id) click to toggle source

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(group_id, member_id, time = 0) click to toggle source

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(group_id) click to toggle source

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_event(count = 10)
Alias for: peek_message
peek_events(count = 10)
Alias for: peek_message
peek_latest_event(count = 10)
Alias for: peek_latest_message
peek_latest_events(count = 10)
Alias for: peek_latest_message
peek_latest_message(count = 10) click to toggle source

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_latest_messages(count = 10)
Alias for: peek_latest_message
peek_message(count = 10) click to toggle source

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
Also aliased as: peek_messages, peek_event, peek_events
peek_messages(count = 10)
Alias for: peek_message
quit(group_id) click to toggle source

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(msg_id) click to toggle source

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(qq = nil, session = nil) click to toggle source

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
rename_group_file(group_id, file_id, new_name) click to toggle source
# 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_invite(event_id, from_id, group_id, operation, message = '') click to toggle source

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_member_join(event_id, from_id, group_id, operation, message = '') click to toggle source

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(event_id, from_id, operation, group_id = 0, message = '') click to toggle source

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_msg(target_qq, *msgs) click to toggle source

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_msg(target_group_id, *msgs) click to toggle source

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_msg(urls, **kwargs) click to toggle source

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_msg(type, target_id, *msgs) click to toggle source

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(target_id, subject_id, kind) click to toggle source

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_msg(target_qq, group_id, *msgs) click to toggle source

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(group_id, config) click to toggle source

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(group_id, member_id, info) click to toggle source

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_session_cfg(cache_size: nil, enable_websocket: nil) click to toggle source

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_listen(interval, is_blocking: false, ignore_error: false) click to toggle source

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_listen() click to toggle source

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(group_id, member_id) click to toggle source

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(group_id) click to toggle source

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
upload_file_and_send(path_or_io, target, group_path) click to toggle source

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
upload_image(path_or_io, type = :friend) click to toggle source

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
upload_voice(path_or_io) click to toggle source

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(qq, session = nil) click to toggle source

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

call(method, path, **kwargs) click to toggle source
# 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
check(qq, session = nil) click to toggle source
# 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
get_events(path, count) click to toggle source
# 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