Browse Source

fix conflation of server and bot

master
Alison Watson 1 year ago
parent
commit
8f79a79b4e
11 changed files with 388 additions and 316 deletions
  1. +1
    -1
      .yardopts
  2. +187
    -0
      source/backends/discord.rb
  3. +143
    -0
      source/backends/irc.rb
  4. +12
    -17
      source/main.rb
  5. +1
    -5
      source/module.rb
  6. +1
    -1
      source/modules/base.rb
  7. +1
    -1
      source/modules/relay.rb
  8. +37
    -0
      source/robots.rb
  9. +5
    -20
      source/server.rb
  10. +0
    -147
      source/servers/discord.rb
  11. +0
    -124
      source/servers/irc.rb

+ 1
- 1
.yardopts View File

@@ -1 +1 @@
--protected --private --hide-void-return source/*.rb source/servers/*.rb
--protected --private --hide-void-return source/*.rb source/backends/*.rb

+ 187
- 0
source/backends/discord.rb View File

@@ -0,0 +1,187 @@
# Discord backend.
module Backend_Discord
require 'discordrb'

# Helper for channel permissions handling.
class ChannelPerms
def initialize() @cprm = {} end

# @param chan [Bot_Discord::Channel] the channel for this permission
# @return [Boolean] true if the channel is enabled, false otherwise.
def [](chan) @cprm[chan.name] or @cprm[chan.real.id] end

# Sets a channel's permission on/off.
# @param chan [Bot_Discord::Channel] the channel for this permission
# @param set [Boolean] if this permission should be set
# @return [void]
def []=(chan, set) @cprm[chan] = set end
end

# A Discord user.
class User < Vrobot4::Server::User
attr_reader :real # The Discordrb::User instance.

# @param user [Discordrb::User] the discord user
# @param ops [Array] list of operator role IDs
# @param hop [Array] list of half-operator role IDs
def initialize user, ops, hop
@real = user
@name = user.name
@id = user.id
@roles = "v"
if user.is_a? Discordrb::Member
if user.owner?
@roles += "Ooh"
elsif ops and ops.any? do |role| user.role? role end
@roles += "oh"
elsif hop and hop.any? do |role| user.role? role end
@roles += "h"
end
end
end
end

# A Discord channel.
class Channel < Vrobot4::Server::Channel
attr_reader :real # The Discordrb::Channel instance.

# @param chan [Discordrb::Channel] the discord channel
def initialize chan
@real = chan
@name = ?# + chan.name
@id = chan.id
end
end

# A Discord server.
class Server < Vrobot4::Server::AudioServer
# The server type name.
def self.type() "Discord" end

# (see Vrobot4::Server::Server#initialize)
def initialize info, bot, id
super info, bot

@id = id
@ops = info["admins"] || []
@hop = info["halfop"] || []

if mods = info["modules"]
for mod in mods
load_mod mod
end
end
end

# (see Vrobot4::Server::AudioServer#voice_quit)
def voice_quit m
@bot.real.voice_destroy m.chan.real.server.id
end

# (see Vrobot4::Server::AudioServer#voice_play)
def voice_play m, fname
vc = @bot.real.voice m.chan.real
raise RuntimeError, "I'm not in a voice channel" unless vc
vc.play_file fname
end

# (see Vrobot4::Server::AudioServer#voice_play_io)
def voice_play_io m, io
vc = @bot.real.voice m.chan.real
raise ArgumentError, "Invalid IO stream" unless io
raise RuntimeError, "I'm not in a voice channel" unless vc
Thread.new do
vc.play_io io
end

# HACK
sleep 1
oldst = nil
while vc.stream_time != oldst
oldst = vc.stream_time
sleep 5
end
end

# (see Vrobot4::Server::Server#flags)
def flags
?A
end

protected

# (see Vrobot4::Server::Server#load_permissions)
def load_permissions pinf
@mprm = {chan: {}, role: {}, glob: {}}
return unless pinf
pinf.each do |pr|
mod = Vrobot4::Module.get_module_type(pr["module"])[:type]
if pr.key? "channel"
@mprm[:chan][mod] = ChannelPerms.new unless @mprm[:chan][mod]
@mprm[:chan][mod][pr["channel"]] = true
elsif pr.key? "roles"
@mprm[:role][mod] = pr["roles"]
elsif pr.key? "enable"
@mprm[:glob][mod] = pr["enable"]
end
end
end
end

# A bot implementation for Discord using discordrb.
class Bot < Vrobot4::Robots::Bot
# The bot type name.
def self.type() "Discord" end
Vrobot4::Robots.add_bot_type self

attr_reader :real

def initialize info
super
@servers = {}

@real = Discordrb::Bot.new \
token: info["apikey"],
client_id: info["client"]

@real.message do |evt|
if evt.server
serv = server_by_id evt.server.id
m = Vrobot4::Server::Message.new \
msg: evt.message.content,
user: User.new(evt.user, @ops, @hop),
chan: Channel.new(evt.channel),
serv: serv,
reply: -> (text) {evt.respond text},
reply_b: -> (text) {evt.respond "```\n#{text}```"}

serv.handle_text_cmd m
end
end
end

# (see Vrobot4::Robots::Bot#connect)
def connect
@real.run
end

protected

# Adds a server to the hash.
# @param id [Integer] id of the server
# @return [Discordrb::Server]
def add_server id
Vrobot4.log :DEBUG, "initializing server #{id}"
@servers[id] = Server.new @info[id] || {}, self, id
end

# Finds a server in the hash, potentially adding it.
# @param id [Integer] id of the server
# @return [Discordrb::Server]
def server_by_id id
@servers[id] || add_server(id)
end
end
end

## EOF

+ 143
- 0
source/backends/irc.rb View File

@@ -0,0 +1,143 @@
# IRC backend.
module Backend_IRC
require 'cinch'

# Helper for channel permissions.
class ChannelPerms
def initialize() @cprm = {} end

# @param chan [Backend_IRC::Channel] the channel for this permission
# @return [Boolean] true if the channel is enabled, false otherwise.
def [](chan) @cprm[chan.name] end

# Sets a channel's permission on/off.
# @param name [String] the channel for this permission
# @param set [Boolean] if this permission should be set
# @return [void]
def []=(name, set) @cprm[name] = set end
end

# An IRC user.
class User < Vrobot4::Server::User
attr_reader :real # The Cinch::User instance.

# @param user [Cinch::User] the irc user
# @param chan [Cinch::Channel] the irc channel this object was made in
def initialize user, chan
@real = user
@name = user.nick
@id = Vrobot4.hash_str user.nick.downcase
if user.oper? then @roles = "Oohv"
elsif chan.opped? user then @roles = "ohv"
elsif chan.half_opped? user then @roles = "hv"
elsif chan.voiced? user then @roles = "v"
else @roles = "" end
end
end

# An IRC channel.
class Channel < Vrobot4::Server::Channel
attr_reader :real # The Cinch::Channel instance.

# @param chan [Cinch::Channel] the irc channel
def initialize chan
@real = chan
@name = chan.name.downcase
@id = Vrobot4.hash_str chan.name.downcase
end
end

# An IRC server.
class Server < Vrobot4::Server::Server
# The server type name.
def self.type() "IRC" end

# (see Vrobot4::Server::Server#initialize)
def initialize info, bot
super
@id = Vrobot4.hash_str info["server"].downcase

for mod in info["modules"] do
load_mod mod
end
end

# (see Vrobot4::Server::Server#flags)
def flags
?L
end

protected

# (see Vrobot4::Server::Server#load_permissions)
def load_permissions pinf
@mprm = {chan: {}, role: {}, glob: {}}
return unless pinf
pinf.each do |pr|
mod = Vrobot4::Module.get_module_type(pr["module"])[:type]
if pr.key? "channel"
@mprm[:chan][mod] = ChannelPerms.new unless @mprm[:chan][mod]
@mprm[:chan][mod][pr["channel"].downcase] = true
elsif pr.key? "roles"
@mprm[:role][mod] = pr["roles"]
elsif pr.key? "enable"
@mprm[:glob][mod] = pr["enable"]
end
end
end
end

# A bot implementation for IRC using cinch.
class Bot < Vrobot4::Robots::Bot
# The bot type name.
def self.type() "IRC" end
Vrobot4::Robots.add_bot_type self

attr_accessor :serv # @return [Vrobot4::Server::Server] the server

# (see Vrobot4::Robots::Bot#initialize)
def initialize info
super
this = self

@bot = Cinch::Bot.new do
configure do |cfg|
cfg.server = info["server"]
cfg.nick = info["nick"] || "vrobot4"
cfg.port = info["port"] if info.key? "port"
cfg.password = info["pass"] if info.key? "pass"
cfg.modes = info["modes"] if info.key? "modes"
cfg.channels = info["channels"] if info.key? "channels"
cfg.realname = "vrobot4"
cfg.user = "vrobot4"
cfg.message_split_start = "… "
cfg.message_split_end = " …"
end

on :message do |evt|
return unless evt.channel

m = Vrobot4::Server::Message.new \
msg: evt.message,
user: User.new(evt.user, evt.channel),
chan: Channel.new(evt.channel),
serv: this.serv,
reply: -> (text) {evt.reply text},
reply_b: -> (text) {evt.reply text}

this.serv.handle_text_cmd m
end
end

@bot.loggers.level = :warn
@serv = Server.new info, self
end

# (see Vrobot4::Robots::Bot#connect)
def connect
@bot.start
end
end
end

## EOF

+ 12
- 17
source/main.rb View File

@@ -1,36 +1,31 @@
require './common.rb'
require './module.rb'
require './server.rb'
require './robots.rb'

require 'yaml'

module Vrobot4
private

# Loads a bot from its configuration +botinfo+.
# @param botinfo [Hash] arbitrary bot information
# Loads a bot from its configuration +info+.
# @param info [Hash] arbitrary bot information
# ["type"] The type by name of the bot.
# @return [Vrobot4::Server] the loaded bot
def self.load_bot botinfo
type = botinfo["type"]
serv = Server::get_server_type(type).new botinfo

for mod in botinfo["modules"] do
serv.load_mod mod
end

serv
# @return [Vrobot4::Robots::Bot] the loaded bot
def self.load_bot info
type = info["type"]
Robots::get_bot_type(type).new info
end

# Runs all bots in +bots+ in separate threads.
# @param bots [Array<Vrobot4::Server>] all bots to run
# @param bots [Array<Vrobot4::Robots::Bot>] all bots to run
# @return [void]
def self.run_bots bots
threads = []

bots.each do |server|
bots.each do |bot|
threads << Thread.new do
server.connect
bot.connect
end
end

@@ -65,8 +60,8 @@ module Vrobot4
begin
bots = []

cfg["bots"].each do |botinfo|
bots << load_bot(botinfo)
cfg["bots"].each do |info|
bots << load_bot(info)
end

log :DEBUGV, "bots: #{bots.to_s}"


+ 1
- 5
source/module.rb View File

@@ -134,11 +134,7 @@ module Vrobot4::Module
check_arg argv[argn], argn, opt[i]
end
else
if opt.length
check_arg argv, 0, opt[0] unless argv.empty?
else
check_arg argv, 0, req[0]
end
check_arg argv, 0, req[0]
end
argv
end


+ 1
- 1
source/modules/base.rb View File

@@ -59,7 +59,7 @@ class Mod_Base < Vrobot4::Module::Module

def c_dbg m, argv
check_args argv, "N"
Vrobot4.debug = argv.to_i
Vrobot4.set_debug argv.to_i
end

def c_info m, argv


+ 1
- 1
source/modules/relay.rb View File

@@ -1,6 +1,6 @@
class Mod_Relay < Vrobot4::Module::Module
def self.type() "Relay" end
Vrobot4::Module.add_module_type self, server: "Discord"
Vrobot4::Module.add_module_type self
def initialize info
super


+ 37
- 0
source/robots.rb View File

@@ -0,0 +1,37 @@
# Module for vrobot4 bot interfaces.
module Vrobot4::Robots
# Generic bot interface.
class Bot
# @param info [Hash] arbitrary extra information for this bot
def initialize info
@info = info
end

# Connect to all servers.
# @abstract
# @return [void]
def connect
raise NotImplementedError, "Bot#connect not implemented"
end
end

# Adds a bot type to the global list.
# @param type [Class] bot type to add
# @return [void]
def self.add_bot_type type
@@bot_types[type.type] = type
Vrobot4.log :INFO, "added bot type: #{type.type}"
end

# Gets a bot type by name from the global list.
# @param name [String] name of the bot type to find
# @return [Class] the bot type
def self.get_bot_type name
@@bot_types[name]
end

# The set of all bot types.
@@bot_types = {}
end

## EOF

+ 5
- 20
source/server.rb View File

@@ -57,9 +57,12 @@ module Vrobot4::Server
class Server
attr_reader :mprm # @return [Hash] module permissions for this server
attr_reader :id # @return [Integer] unique identifier for the server
attr_reader :bot # @return [Vrobot4::Robots::Bot] the bot we belong to

# @param info [Hash] arbitrary extra information for this server
def initialize info
# @param bot [Vrobot4::Robots::Bot] the bot we belong to
def initialize info, bot
@bot = bot
@info = info
@modules = [Mod_Base.new(nil)]
load_permissions info["permissions"]
@@ -72,7 +75,7 @@ module Vrobot4::Server
mt = Vrobot4::Module.get_module_type(mod)
if mt[:server] and mt[:server] != self.class.type or
mt[:servflags] and mt[:servflags] !~ flags then
raise ArgumentError, "Module " + mod + " not valid for this server"
raise ArgumentError, "Module #{mod} is not valid for this server"
end
@modules << mt[:type].new(@info[mod])
end
@@ -188,24 +191,6 @@ module Vrobot4::Server
raise NotImplementedError, "AudioServer#voice_play_io not implemented"
end
end

# Adds a server type to the global list.
# @param type [Class] server type to add
# @return [void]
def self.add_server_type type
@@server_types[type.type] = type
Vrobot4.log :INFO, "added server type: #{type.type}"
end

# Gets a server type by name from the global list.
# @param name [String] name of the server type to find
# @return [Vrobot4::Server::Server] the server type
def self.get_server_type name
@@server_types[name]
end

# The set of all server types.
@@server_types = {}
end

## EOF

+ 0
- 147
source/servers/discord.rb View File

@@ -1,147 +0,0 @@
require 'discordrb'

# A server implementation for Discord using discordrb.
class Sv_Discord < Vrobot4::Server::AudioServer
# The server type name.
def self.type() "Discord" end
Vrobot4::Server.add_server_type self

attr_reader :bot # The Discordrb::Bot instance.

# (see Vrobot4::Server::Server#initialize)
def initialize info
super

@id = info["client"]

@ops = info["admins"] || []
@hop = info["halfop"] || []

@bot = Discordrb::Bot.new \
token: info["apikey"],
client_id: info["client"]

@bot.message do |evt|
m = Vrobot4::Server::Message.new \
msg: evt.message.content,
user: User.new(evt.user, @ops, @hop),
chan: Channel.new(evt.channel),
serv: self,
reply: -> (text) {evt.respond text},
reply_b: -> (text) {evt.respond "```\n#{text}```"}

handle_text_cmd m
end
end

# (see Vrobot4::Server::AudioServer#voice_quit)
def voice_quit m
@bot.voice_destroy m.chan.real.server.id
end

# (see Vrobot4::Server::AudioServer#voice_play)
def voice_play m, fname
vc = @bot.voice m.chan.real
raise RuntimeError, "I'm not in a voice channel" unless vc
vc.play_file fname
end

# (see Vrobot4::Server::AudioServer#voice_play_io)
def voice_play_io m, io
vc = @bot.voice m.chan.real
raise ArgumentError, "Invalid IO stream" unless io
raise RuntimeError, "I'm not in a voice channel" unless vc
Thread.new do
vc.play_io io
end
# HACK
sleep 1
oldst = nil
while vc.stream_time != oldst
oldst = vc.stream_time
sleep 5
end
end

# (see Vrobot4::Server::Server#connect)
def connect
@bot.run
end

# (see Vrobot4::Server::Server#flags)
def flags
?A
end

protected

# (see Vrobot4::Server::Server#load_permissions)
def load_permissions pinf
@mprm = {chan: {}, role: {}, glob: {}}
pinf.each do |pr|
mod = Vrobot4::Module.get_module_type(pr["module"])[:type]
if pr.key? "channel"
@mprm[:chan][mod] = ChannelPerms.new unless @mprm[:chan].key? mod
@mprm[:chan][mod][pr["channel"]] = true
elsif pr.key? "roles"
@mprm[:role][mod] = pr["roles"]
elsif pr.key? "enable"
@mprm[:glob][mod] = pr["enable"]
end
end if pinf
end

# Helper for channel permissions handling.
class ChannelPerms
def initialize() @cprm = {} end

# @param chan [Sv_Discord::Channel] the channel for this permission
# @return [Boolean] true if the channel is enabled, false otherwise.
def [](chan) @cprm[chan.name] or @cprm[chan.real.id] end

# Sets a channel's permission on/off.
# @param chan [Sv_Discord::Channel] the channel for this permission
# @param set [Boolean] if this permission should be set
# @return [void]
def []=(chan, set) @cprm[chan] = set end
end
private_constant :ChannelPerms

# A Discord user.
class User < Vrobot4::Server::User
attr_reader :real # The Discordrb::User instance.

# @param user [Discordrb::User] the discord user
# @param ops [Array] list of operator role IDs
# @param hop [Array] list of half-operator role IDs
def initialize user, ops, hop
@real = user
@name = user.name
@id = user.id
@roles = "v"
if user.is_a? Discordrb::Member
if user.owner?
@roles += "Ooh"
elsif ops and ops.any? do |role| user.role? role end
@roles += "oh"
elsif hop and hop.any? do |role| user.role? role end
@roles += "h"
end
end
end
end

# A Discord channel.
class Channel < Vrobot4::Server::Channel
attr_reader :real # The Discordrb::Channel instance.

# @param chan [Discordrb::Channel] the discord channel
def initialize chan
@real = chan
@name = ?# + chan.name
@id = chan.id
end
end
end

## EOF

+ 0
- 124
source/servers/irc.rb View File

@@ -1,124 +0,0 @@
require 'cinch'

# A server implementation for IRC using cinch.
class Sv_IRC < Vrobot4::Server::Server
# The server type name.
def self.type() "IRC" end
Vrobot4::Server.add_server_type self

attr_reader :bot # The Cinch::Bot instance.

# (see Vrobot4::Server::Server#initialize)
def initialize info
super

this = self
@bot = Cinch::Bot.new do
configure do |cfg|
cfg.server = info["server"]
cfg.nick = info["nick"] || "vrobot4"
cfg.port = info["port"] if info.key? "port"
cfg.password = info["pass"] if info.key? "pass"
cfg.modes = info["modes"] if info.key? "modes"
cfg.channels = info["channels"] if info.key? "channels"
cfg.realname = "vrobot4"
cfg.user = "vrobot4"
cfg.message_split_start = "… "
cfg.message_split_end = " …"
end

on :message do |evt|
return unless evt.channel

m = Vrobot4::Server::Message.new \
msg: evt.message,
user: User.new(evt.user, evt.channel),
chan: Channel.new(evt.channel),
serv: this,
reply: -> (text) {evt.reply text},
reply_b: -> (text) {evt.reply text}

this.handle_text_cmd m
end
end

@bot.loggers.level = :warn
@id = info["server"].downcase.sum
end

# (see Vrobot4::Server::Server#connect)
def connect
@bot.start
end

# (see Vrobot4::Server::Server#flags)
def flags
?L
end

protected

# (see Vrobot4::Server::Server#load_permissions)
def load_permissions pinf
@mprm = {chan: {}, role: {}, glob: {}}
pinf.each do |pr|
mod = Vrobot4::Module.get_module_type(pr["module"])[:type]
if pr.key? "channel"
@mprm[:chan][mod] = ChannelPerms.new unless @mprm[:chan].key? mod
@mprm[:chan][mod][pr["channel"].downcase] = true
elsif pr.key? "roles"
@mprm[:role][mod] = pr["roles"]
elsif pr.key? "enable"
@mprm[:glob][mod] = pr["enable"]
end
end if pinf
end

# Helper for channel permissions.
class ChannelPerms
def initialize() @cprm = {} end

# @param chan [Sv_IRC::Channel] the channel for this permission
# @return [Boolean] true if the channel is enabled, false otherwise.
def [](chan) @cprm[chan.name] end

# Sets a channel's permission on/off.
# @param name [String] the channel for this permission
# @param set [Boolean] if this permission should be set
# @return [void]
def []=(name, set) @cprm[name] = set end
end
private_constant :ChannelPerms

# An IRC user.
class User < Vrobot4::Server::User
attr_reader :real # The Cinch::User instance.

# @param user [Cinch::User] the irc user
# @param chan [Cinch::Channel] the irc channel this object was made in
def initialize user, chan
@real = user
@name = user.nick
@id = user.nick.downcase.sum
if user.oper? then @roles = "Oohv"
elsif chan.opped? user then @roles = "ohv"
elsif chan.half_opped? user then @roles = "hv"
elsif chan.voiced? user then @roles = "v"
else @roles = "" end
end
end

# An IRC channel.
class Channel < Vrobot4::Server::Channel
attr_reader :real # The Cinch::Channel instance.

# @param chan [Cinch::Channel] the irc channel
def initialize chan
@real = chan
@name = chan.name.downcase
@id = chan.name.downcase.sum
end
end
end

## EOF

Loading…
Cancel
Save