ソースを参照

adding theater-mode, fixes #15

tags/v0.1.0
Edmund Huber 10年前
コミット
d5bdc78d55
10個のファイルの変更212行の追加31行の削除
  1. 3
    0
      ergonomadic.conf
  2. 12
    9
      irc/channel.go
  3. 26
    21
      irc/client.go
  4. 26
    0
      irc/commands.go
  5. 14
    0
      irc/config.go
  6. 1
    0
      irc/constants.go
  7. 2
    1
      irc/modes.go
  8. 5
    0
      irc/reply.go
  9. 2
    0
      irc/server.go
  10. 121
    0
      irc/theater.go

+ 3
- 0
ergonomadic.conf ファイルの表示

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

+ 12
- 9
irc/channel.go ファイルの表示

@@ -6,14 +6,15 @@ import (
6 6
 )
7 7
 
8 8
 type Channel struct {
9
-	flags     ChannelModeSet
10
-	lists     map[ChannelMode]*UserMaskSet
11
-	key       Text
12
-	members   MemberSet
13
-	name      Name
14
-	server    *Server
15
-	topic     Text
16
-	userLimit uint64
9
+	flags       ChannelModeSet
10
+	lists       map[ChannelMode]*UserMaskSet
11
+	key         Text
12
+	members     MemberSet
13
+	name        Name
14
+	server      *Server
15
+	topic       Text
16
+	userLimit   uint64
17
+	theaterUser *Client
17 18
 }
18 19
 
19 20
 // NewChannel creates a new channel from a `Server` and a `name`
@@ -405,9 +406,11 @@ func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) boo
405 406
 		return channel.applyModeMember(client, change.mode, change.op,
406 407
 			NewName(change.arg))
407 408
 
409
+	case Theater:
410
+		client.ErrConfiguredMode(change.mode)
411
+
408 412
 	default:
409 413
 		client.ErrUnknownMode(change.mode, channel)
410
-		return false
411 414
 	}
412 415
 	return false
413 416
 }

+ 26
- 21
irc/client.go ファイルの表示

@@ -13,27 +13,28 @@ const (
13 13
 )
14 14
 
15 15
 type Client struct {
16
-	atime        time.Time
17
-	authorized   bool
18
-	awayMessage  Text
19
-	capabilities CapabilitySet
20
-	capState     CapState
21
-	channels     ChannelSet
22
-	commands     chan Command
23
-	ctime        time.Time
24
-	flags        map[UserMode]bool
25
-	hasQuit      bool
26
-	hops         uint
27
-	hostname     Name
28
-	idleTimer    *time.Timer
29
-	loginTimer   *time.Timer
30
-	nick         Name
31
-	quitTimer    *time.Timer
32
-	realname     Text
33
-	registered   bool
34
-	server       *Server
35
-	socket       *Socket
36
-	username     Name
16
+	atime           time.Time
17
+	authorized      bool
18
+	awayMessage     Text
19
+	capabilities    CapabilitySet
20
+	capState        CapState
21
+	channels        ChannelSet
22
+	commands        chan Command
23
+	ctime           time.Time
24
+	flags           map[UserMode]bool
25
+	hasQuit         bool
26
+	hops            uint
27
+	hostname        Name
28
+	idleTimer       *time.Timer
29
+	loginTimer      *time.Timer
30
+	nick            Name
31
+	quitTimer       *time.Timer
32
+	realname        Text
33
+	registered      bool
34
+	server          *Server
35
+	socket          *Socket
36
+	username        Name
37
+	theaterChannels []*Channel
37 38
 }
38 39
 
39 40
 func NewClient(server *Server, conn net.Conn) *Client {
@@ -259,6 +260,10 @@ func (client *Client) Quit(message Text) {
259 260
 		return
260 261
 	}
261 262
 
263
+	for _, channel := range client.theaterChannels {
264
+		delete(channel.flags, Theater)
265
+	}
266
+
262 267
 	client.Reply(RplError("connection closed"))
263 268
 	client.hasQuit = true
264 269
 	client.server.whoWas.Append(client)

+ 26
- 0
irc/commands.go ファイルの表示

@@ -49,6 +49,7 @@ var (
49 49
 		PRIVMSG: NewPrivMsgCommand,
50 50
 		PROXY:   NewProxyCommand,
51 51
 		QUIT:    NewQuitCommand,
52
+		THEATER: NewTheaterCommand, // nonstandard
52 53
 		TIME:    NewTimeCommand,
53 54
 		TOPIC:   NewTopicCommand,
54 55
 		USER:    NewUserCommand,
@@ -947,6 +948,31 @@ func NewInviteCommand(args []string) (Command, error) {
947 948
 	}, nil
948 949
 }
949 950
 
951
+func NewTheaterCommand(args []string) (Command, error) {
952
+	if len(args) < 1 {
953
+		return nil, NotEnoughArgsError
954
+	} else if upperSubCmd := strings.ToUpper(args[0]); upperSubCmd == "IDENTIFY" && len(args) == 3 {
955
+		return &TheaterIdentifyCommand{
956
+			channel:     NewName(args[1]),
957
+			PassCommand: PassCommand{password: []byte(args[2])},
958
+		}, nil
959
+	} else if upperSubCmd == "PRIVMSG" && len(args) == 4 {
960
+		return &TheaterPrivMsgCommand{
961
+			channel: NewName(args[1]),
962
+			asNick:  NewName(args[2]),
963
+			message: NewText(args[3]),
964
+		}, nil
965
+	} else if upperSubCmd == "ACTION" && len(args) == 4 {
966
+		return &TheaterActionCommand{
967
+			channel: NewName(args[1]),
968
+			asNick:  NewName(args[2]),
969
+			action:  NewText(args[3]),
970
+		}, nil
971
+	} else {
972
+		return nil, ErrParseCommand
973
+	}
974
+}
975
+
950 976
 type TimeCommand struct {
951 977
 	BaseCommand
952 978
 	target Name

+ 14
- 0
irc/config.go ファイルの表示

@@ -29,6 +29,8 @@ type Config struct {
29 29
 	}
30 30
 
31 31
 	Operator map[string]*PassConfig
32
+
33
+	Theater map[string]*PassConfig
32 34
 }
33 35
 
34 36
 func (conf *Config) Operators() map[Name][]byte {
@@ -39,6 +41,18 @@ func (conf *Config) Operators() map[Name][]byte {
39 41
 	return operators
40 42
 }
41 43
 
44
+func (conf *Config) Theaters() map[Name][]byte {
45
+	theaters := make(map[Name][]byte)
46
+	for s, theaterConf := range conf.Theater {
47
+		name := NewName(s)
48
+		if !name.IsChannel() {
49
+			log.Fatal("config uses a non-channel for a theater!")
50
+		}
51
+		theaters[name] = theaterConf.PasswordBytes()
52
+	}
53
+	return theaters
54
+}
55
+
42 56
 func LoadConfig(filename string) (config *Config, err error) {
43 57
 	config = &Config{}
44 58
 	err = gcfg.ReadFileInto(config, filename)

+ 1
- 0
irc/constants.go ファイルの表示

@@ -30,6 +30,7 @@ const (
30 30
 	PRIVMSG StringCode = "PRIVMSG"
31 31
 	PROXY   StringCode = "PROXY"
32 32
 	QUIT    StringCode = "QUIT"
33
+	THEATER StringCode = "THEATER" // nonstandard
33 34
 	TIME    StringCode = "TIME"
34 35
 	TOPIC   StringCode = "TOPIC"
35 36
 	USER    StringCode = "USER"

+ 2
- 1
irc/modes.go ファイルの表示

@@ -83,6 +83,7 @@ const (
83 83
 	Quiet           ChannelMode = 'q' // flag
84 84
 	ReOp            ChannelMode = 'r' // flag
85 85
 	Secret          ChannelMode = 's' // flag, deprecated
86
+	Theater         ChannelMode = 'T' // flag arg, nonstandard
86 87
 	UserLimit       ChannelMode = 'l' // flag arg
87 88
 	Voice           ChannelMode = 'v' // arg
88 89
 )
@@ -90,7 +91,7 @@ const (
90 91
 var (
91 92
 	SupportedChannelModes = ChannelModes{
92 93
 		BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
93
-		OpOnlyTopic, Persistent, Private, UserLimit,
94
+		OpOnlyTopic, Persistent, Private, Theater, UserLimit,
94 95
 	}
95 96
 )
96 97
 

+ 5
- 0
irc/reply.go ファイルの表示

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

+ 2
- 0
irc/server.go ファイルの表示

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

+ 121
- 0
irc/theater.go ファイルの表示

@@ -0,0 +1,121 @@
1
+package irc
2
+
3
+import (
4
+	"fmt"
5
+)
6
+
7
+type TheaterClient Name
8
+
9
+func (c TheaterClient) Id() Name {
10
+	return Name(c)
11
+}
12
+
13
+func (c TheaterClient) Nick() Name {
14
+	return Name(c)
15
+}
16
+
17
+type TheaterSubCommand string
18
+
19
+type theaterSubCommand interface {
20
+	String() string
21
+}
22
+
23
+type TheaterIdentifyCommand struct {
24
+	PassCommand
25
+	channel Name
26
+}
27
+
28
+func (m *TheaterIdentifyCommand) LoadPassword(s *Server) {
29
+	m.hash = s.theaters[m.channel]
30
+}
31
+
32
+func (cmd *TheaterIdentifyCommand) String() string {
33
+	return fmt.Sprintf("THEATER_IDENTIFY(channel=%s)", cmd.channel)
34
+}
35
+
36
+func (m *TheaterIdentifyCommand) HandleServer(s *Server) {
37
+	client := m.Client()
38
+	if !m.channel.IsChannel() {
39
+		client.ErrNoSuchChannel(m.channel)
40
+		return
41
+	}
42
+
43
+	channel := s.channels.Get(m.channel)
44
+	if channel == nil {
45
+		client.ErrNoSuchChannel(m.channel)
46
+		return
47
+	}
48
+
49
+	if (m.hash == nil) || (m.err != nil) {
50
+		client.ErrPasswdMismatch()
51
+		return
52
+	}
53
+
54
+	if channel.theaterUser == nil {
55
+		client.theaterChannels = append(client.theaterChannels, channel)
56
+		channel.flags[Theater] = true
57
+		channel.theaterUser = client
58
+	}
59
+}
60
+
61
+type TheaterPrivMsgCommand struct {
62
+	BaseCommand
63
+	channel Name
64
+	asNick  Name
65
+	message Text
66
+}
67
+
68
+func (cmd *TheaterPrivMsgCommand) String() string {
69
+	return fmt.Sprintf("THEATER_PRIVMSG(channel=%s, asNick=%s, message=%s)", cmd.channel, cmd.asNick, cmd.message)
70
+
71
+}
72
+func (m *TheaterPrivMsgCommand) HandleServer(s *Server) {
73
+	client := m.Client()
74
+	if !m.channel.IsChannel() {
75
+		client.ErrNoSuchChannel(m.channel)
76
+		return
77
+	}
78
+
79
+	channel := s.channels.Get(m.channel)
80
+	if channel == nil {
81
+		client.ErrNoSuchChannel(m.channel)
82
+		return
83
+	}
84
+
85
+	if channel.theaterUser == client {
86
+		for member := range channel.members {
87
+			member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, m.message))
88
+		}
89
+	}
90
+}
91
+
92
+type TheaterActionCommand struct {
93
+	BaseCommand
94
+	channel Name
95
+	asNick  Name
96
+	action  Text
97
+}
98
+
99
+func (cmd *TheaterActionCommand) String() string {
100
+	return fmt.Sprintf("THEATER_ACTION(channel=%s, asNick=%s, action=%s)", cmd.channel, cmd.asNick, cmd.action)
101
+}
102
+
103
+func (m *TheaterActionCommand) HandleServer(s *Server) {
104
+	client := m.Client()
105
+	if m.channel.IsChannel() {
106
+		client.ErrNoSuchChannel(m.channel)
107
+		return
108
+	}
109
+
110
+	channel := s.channels.Get(m.channel)
111
+	if channel == nil {
112
+		client.ErrNoSuchChannel(m.channel)
113
+		return
114
+	}
115
+
116
+	if channel.theaterUser == client {
117
+		for member := range channel.members {
118
+			member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, NewText(fmt.Sprintf("\001ACTION %s\001", m.action))))
119
+		}
120
+	}
121
+}

読み込み中…
キャンセル
保存