mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-22 20:09:41 +01:00
Merge pull request #25 from edmund-huber/theater_mode
WIP: adding theater-mode, fixes #15
This commit is contained in:
commit
6403e79a5b
@ -9,3 +9,6 @@ password = "JDJhJDA0JHJzVFFlNXdOUXNhLmtkSGRUQVVEVHVYWXRKUmdNQ3FKVTRrczRSMTlSWGRP
|
|||||||
|
|
||||||
[operator "root"]
|
[operator "root"]
|
||||||
password = "JDJhJDA0JEhkcm10UlNFRkRXb25iOHZuSDVLZXVBWlpyY0xyNkQ4dlBVc1VMWVk1LlFjWFpQbGxZNUtl" ; 'toor'
|
password = "JDJhJDA0JEhkcm10UlNFRkRXb25iOHZuSDVLZXVBWlpyY0xyNkQ4dlBVc1VMWVk1LlFjWFpQbGxZNUtl" ; 'toor'
|
||||||
|
|
||||||
|
[theater "#ghostbusters"]
|
||||||
|
password = "JDJhJDA0JG0yY1h4cTRFUHhkcjIzN2p1M2Nvb2VEYjAzSHh4eTB3YkZ0VFRLV1ZPVXdqeFBSRUtmRlBT" ; 'venkman'
|
||||||
|
@ -6,14 +6,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
flags ChannelModeSet
|
flags ChannelModeSet
|
||||||
lists map[ChannelMode]*UserMaskSet
|
lists map[ChannelMode]*UserMaskSet
|
||||||
key Text
|
key Text
|
||||||
members MemberSet
|
members MemberSet
|
||||||
name Name
|
name Name
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -13,27 +13,28 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
atime time.Time
|
atime time.Time
|
||||||
authorized bool
|
authorized bool
|
||||||
awayMessage Text
|
awayMessage Text
|
||||||
capabilities CapabilitySet
|
capabilities CapabilitySet
|
||||||
capState CapState
|
capState CapState
|
||||||
channels ChannelSet
|
channels ChannelSet
|
||||||
commands chan Command
|
commands chan Command
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
flags map[UserMode]bool
|
flags map[UserMode]bool
|
||||||
hasQuit bool
|
hasQuit bool
|
||||||
hops uint
|
hops uint
|
||||||
hostname Name
|
hostname Name
|
||||||
idleTimer *time.Timer
|
idleTimer *time.Timer
|
||||||
loginTimer *time.Timer
|
loginTimer *time.Timer
|
||||||
nick Name
|
nick Name
|
||||||
quitTimer *time.Timer
|
quitTimer *time.Timer
|
||||||
realname Text
|
realname Text
|
||||||
registered bool
|
registered bool
|
||||||
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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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"
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
121
irc/theater.go
Normal 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))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user