adding theater-mode, fixes #15

This commit is contained in:
Edmund Huber 2014-03-13 09:55:46 +01:00
parent a3df727c47
commit d5bdc78d55
10 changed files with 212 additions and 31 deletions

View File

@ -9,3 +9,6 @@ password = "JDJhJDA0JHJzVFFlNXdOUXNhLmtkSGRUQVVEVHVYWXRKUmdNQ3FKVTRrczRSMTlSWGRP
[operator "root"] [operator "root"]
password = "JDJhJDA0JEhkcm10UlNFRkRXb25iOHZuSDVLZXVBWlpyY0xyNkQ4dlBVc1VMWVk1LlFjWFpQbGxZNUtl" ; 'toor' password = "JDJhJDA0JEhkcm10UlNFRkRXb25iOHZuSDVLZXVBWlpyY0xyNkQ4dlBVc1VMWVk1LlFjWFpQbGxZNUtl" ; 'toor'
[theater "#ghostbusters"]
password = "JDJhJDA0JG0yY1h4cTRFUHhkcjIzN2p1M2Nvb2VEYjAzSHh4eTB3YkZ0VFRLV1ZPVXdqeFBSRUtmRlBT" ; 'venkman'

View File

@ -14,6 +14,7 @@ type Channel struct {
server *Server server *Server
topic Text topic Text
userLimit uint64 userLimit uint64
theaterUser *Client
} }
// NewChannel creates a new channel from a `Server` and a `name` // NewChannel creates a new channel from a `Server` and a `name`
@ -405,9 +406,11 @@ func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) boo
return channel.applyModeMember(client, change.mode, change.op, return channel.applyModeMember(client, change.mode, change.op,
NewName(change.arg)) NewName(change.arg))
case Theater:
client.ErrConfiguredMode(change.mode)
default: default:
client.ErrUnknownMode(change.mode, channel) client.ErrUnknownMode(change.mode, channel)
return false
} }
return false return false
} }

View File

@ -34,6 +34,7 @@ type Client struct {
server *Server server *Server
socket *Socket socket *Socket
username Name username Name
theaterChannels []*Channel
} }
func NewClient(server *Server, conn net.Conn) *Client { func NewClient(server *Server, conn net.Conn) *Client {
@ -259,6 +260,10 @@ func (client *Client) Quit(message Text) {
return return
} }
for _, channel := range client.theaterChannels {
delete(channel.flags, Theater)
}
client.Reply(RplError("connection closed")) client.Reply(RplError("connection closed"))
client.hasQuit = true client.hasQuit = true
client.server.whoWas.Append(client) client.server.whoWas.Append(client)

View File

@ -49,6 +49,7 @@ var (
PRIVMSG: NewPrivMsgCommand, PRIVMSG: NewPrivMsgCommand,
PROXY: NewProxyCommand, PROXY: NewProxyCommand,
QUIT: NewQuitCommand, QUIT: NewQuitCommand,
THEATER: NewTheaterCommand, // nonstandard
TIME: NewTimeCommand, TIME: NewTimeCommand,
TOPIC: NewTopicCommand, TOPIC: NewTopicCommand,
USER: NewUserCommand, USER: NewUserCommand,
@ -947,6 +948,31 @@ func NewInviteCommand(args []string) (Command, error) {
}, nil }, nil
} }
func NewTheaterCommand(args []string) (Command, error) {
if len(args) < 1 {
return nil, NotEnoughArgsError
} else if upperSubCmd := strings.ToUpper(args[0]); upperSubCmd == "IDENTIFY" && len(args) == 3 {
return &TheaterIdentifyCommand{
channel: NewName(args[1]),
PassCommand: PassCommand{password: []byte(args[2])},
}, nil
} else if upperSubCmd == "PRIVMSG" && len(args) == 4 {
return &TheaterPrivMsgCommand{
channel: NewName(args[1]),
asNick: NewName(args[2]),
message: NewText(args[3]),
}, nil
} else if upperSubCmd == "ACTION" && len(args) == 4 {
return &TheaterActionCommand{
channel: NewName(args[1]),
asNick: NewName(args[2]),
action: NewText(args[3]),
}, nil
} else {
return nil, ErrParseCommand
}
}
type TimeCommand struct { type TimeCommand struct {
BaseCommand BaseCommand
target Name target Name

View File

@ -29,6 +29,8 @@ type Config struct {
} }
Operator map[string]*PassConfig Operator map[string]*PassConfig
Theater map[string]*PassConfig
} }
func (conf *Config) Operators() map[Name][]byte { func (conf *Config) Operators() map[Name][]byte {
@ -39,6 +41,18 @@ func (conf *Config) Operators() map[Name][]byte {
return operators return operators
} }
func (conf *Config) Theaters() map[Name][]byte {
theaters := make(map[Name][]byte)
for s, theaterConf := range conf.Theater {
name := NewName(s)
if !name.IsChannel() {
log.Fatal("config uses a non-channel for a theater!")
}
theaters[name] = theaterConf.PasswordBytes()
}
return theaters
}
func LoadConfig(filename string) (config *Config, err error) { func LoadConfig(filename string) (config *Config, err error) {
config = &Config{} config = &Config{}
err = gcfg.ReadFileInto(config, filename) err = gcfg.ReadFileInto(config, filename)

View File

@ -30,6 +30,7 @@ const (
PRIVMSG StringCode = "PRIVMSG" PRIVMSG StringCode = "PRIVMSG"
PROXY StringCode = "PROXY" PROXY StringCode = "PROXY"
QUIT StringCode = "QUIT" QUIT StringCode = "QUIT"
THEATER StringCode = "THEATER" // nonstandard
TIME StringCode = "TIME" TIME StringCode = "TIME"
TOPIC StringCode = "TOPIC" TOPIC StringCode = "TOPIC"
USER StringCode = "USER" USER StringCode = "USER"

View File

@ -83,6 +83,7 @@ const (
Quiet ChannelMode = 'q' // flag Quiet ChannelMode = 'q' // flag
ReOp ChannelMode = 'r' // flag ReOp ChannelMode = 'r' // flag
Secret ChannelMode = 's' // flag, deprecated Secret ChannelMode = 's' // flag, deprecated
Theater ChannelMode = 'T' // flag arg, nonstandard
UserLimit ChannelMode = 'l' // flag arg UserLimit ChannelMode = 'l' // flag arg
Voice ChannelMode = 'v' // arg Voice ChannelMode = 'v' // arg
) )
@ -90,7 +91,7 @@ const (
var ( var (
SupportedChannelModes = ChannelModes{ SupportedChannelModes = ChannelModes{
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside, BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
OpOnlyTopic, Persistent, Private, UserLimit, OpOnlyTopic, Persistent, Private, Theater, UserLimit,
} }
) )

View File

@ -548,6 +548,11 @@ func (target *Client) ErrUnknownMode(mode ChannelMode, channel *Channel) {
"%s :is unknown mode char to me for %s", mode, channel) "%s :is unknown mode char to me for %s", mode, channel)
} }
func (target *Client) ErrConfiguredMode(mode ChannelMode) {
target.NumericReply(ERR_UNKNOWNMODE,
"%s :can only change this mode in daemon configuration", mode)
}
func (target *Client) ErrChannelIsFull(channel *Channel) { func (target *Client) ErrChannelIsFull(channel *Channel) {
target.NumericReply(ERR_CHANNELISFULL, target.NumericReply(ERR_CHANNELISFULL,
"%s :Cannot join channel (+l)", channel) "%s :Cannot join channel (+l)", channel)

View File

@ -40,6 +40,7 @@ type Server struct {
password []byte password []byte
signals chan os.Signal signals chan os.Signal
whoWas *WhoWasList whoWas *WhoWasList
theaters map[Name][]byte
} }
var ( var (
@ -61,6 +62,7 @@ func NewServer(config *Config) *Server {
operators: config.Operators(), operators: config.Operators(),
signals: make(chan os.Signal, len(SERVER_SIGNALS)), signals: make(chan os.Signal, len(SERVER_SIGNALS)),
whoWas: NewWhoWasList(100), whoWas: NewWhoWasList(100),
theaters: config.Theaters(),
} }
if config.Server.Password != "" { if config.Server.Password != "" {

121
irc/theater.go Normal file
View File

@ -0,0 +1,121 @@
package irc
import (
"fmt"
)
type TheaterClient Name
func (c TheaterClient) Id() Name {
return Name(c)
}
func (c TheaterClient) Nick() Name {
return Name(c)
}
type TheaterSubCommand string
type theaterSubCommand interface {
String() string
}
type TheaterIdentifyCommand struct {
PassCommand
channel Name
}
func (m *TheaterIdentifyCommand) LoadPassword(s *Server) {
m.hash = s.theaters[m.channel]
}
func (cmd *TheaterIdentifyCommand) String() string {
return fmt.Sprintf("THEATER_IDENTIFY(channel=%s)", cmd.channel)
}
func (m *TheaterIdentifyCommand) HandleServer(s *Server) {
client := m.Client()
if !m.channel.IsChannel() {
client.ErrNoSuchChannel(m.channel)
return
}
channel := s.channels.Get(m.channel)
if channel == nil {
client.ErrNoSuchChannel(m.channel)
return
}
if (m.hash == nil) || (m.err != nil) {
client.ErrPasswdMismatch()
return
}
if channel.theaterUser == nil {
client.theaterChannels = append(client.theaterChannels, channel)
channel.flags[Theater] = true
channel.theaterUser = client
}
}
type TheaterPrivMsgCommand struct {
BaseCommand
channel Name
asNick Name
message Text
}
func (cmd *TheaterPrivMsgCommand) String() string {
return fmt.Sprintf("THEATER_PRIVMSG(channel=%s, asNick=%s, message=%s)", cmd.channel, cmd.asNick, cmd.message)
}
func (m *TheaterPrivMsgCommand) HandleServer(s *Server) {
client := m.Client()
if !m.channel.IsChannel() {
client.ErrNoSuchChannel(m.channel)
return
}
channel := s.channels.Get(m.channel)
if channel == nil {
client.ErrNoSuchChannel(m.channel)
return
}
if channel.theaterUser == client {
for member := range channel.members {
member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, m.message))
}
}
}
type TheaterActionCommand struct {
BaseCommand
channel Name
asNick Name
action Text
}
func (cmd *TheaterActionCommand) String() string {
return fmt.Sprintf("THEATER_ACTION(channel=%s, asNick=%s, action=%s)", cmd.channel, cmd.asNick, cmd.action)
}
func (m *TheaterActionCommand) HandleServer(s *Server) {
client := m.Client()
if m.channel.IsChannel() {
client.ErrNoSuchChannel(m.channel)
return
}
channel := s.channels.Get(m.channel)
if channel == nil {
client.ErrNoSuchChannel(m.channel)
return
}
if channel.theaterUser == client {
for member := range channel.members {
member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, NewText(fmt.Sprintf("\001ACTION %s\001", m.action))))
}
}
}