@@ -1 +1 @@ | |||
--protected --private --hide-void-return source/*.rb source/servers/*.rb | |||
--protected --private --hide-void-return source/*.rb source/backends/*.rb |
@@ -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 |
@@ -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 |
@@ -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}" | |||
@@ -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 | |||
@@ -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,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 | |||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |