mirror of
https://github.com/gissleh/irc.git
synced 2026-10-15 16:02:54 +00:00
migrate to github, add stuff.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/git.aiterp.net/gisle/irc
|
||||
path: src/github.com/gissleh/irc
|
||||
|
||||
pipeline:
|
||||
test:
|
||||
|
||||
Generated
+2
@@ -0,0 +1,2 @@
|
||||
# Default ignored files
|
||||
/workspace.xml
|
||||
Generated
+18
@@ -0,0 +1,18 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="gisle">
|
||||
<words>
|
||||
<w>chanmodes</w>
|
||||
<w>chantypes</w>
|
||||
<w>ctcp</w>
|
||||
<w>fullnick</w>
|
||||
<w>invalidcommand</w>
|
||||
<w>isupport</w>
|
||||
<w>polyfilled</w>
|
||||
<w>privmsg</w>
|
||||
<w>qcgimnprstz</w>
|
||||
<w>structs</w>
|
||||
<w>testserver</w>
|
||||
<w>tstate</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/irc.iml" filepath="$PROJECT_DIR$/.idea/irc.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+29
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectTasksOptions">
|
||||
<TaskOptions isEnabled="true">
|
||||
<option name="arguments" value="fmt $FilePath$" />
|
||||
<option name="checkSyntaxErrors" value="true" />
|
||||
<option name="description" />
|
||||
<option name="exitCodeBehavior" value="ERROR" />
|
||||
<option name="fileExtension" value="go" />
|
||||
<option name="immediateSync" value="false" />
|
||||
<option name="name" value="go fmt" />
|
||||
<option name="output" value="$FilePath$" />
|
||||
<option name="outputFilters">
|
||||
<array />
|
||||
</option>
|
||||
<option name="outputFromStdout" value="false" />
|
||||
<option name="program" value="$GoExecPath$" />
|
||||
<option name="runOnExternalChanges" value="false" />
|
||||
<option name="scopeName" value="Project Files" />
|
||||
<option name="trackOnlyRoot" value="true" />
|
||||
<option name="workingDir" value="$ProjectFileDir$" />
|
||||
<envs>
|
||||
<env name="GOROOT" value="$GOROOT$" />
|
||||
<env name="GOPATH" value="$GOPATH$" />
|
||||
<env name="PATH" value="$GoBinDirs$" />
|
||||
</envs>
|
||||
</TaskOptions>
|
||||
</component>
|
||||
</project>
|
||||
+10
-2
@@ -3,7 +3,7 @@ package irc
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.aiterp.net/gisle/irc/list"
|
||||
"github.com/gissleh/irc/list"
|
||||
)
|
||||
|
||||
// A Channel is a target that manages the userlist
|
||||
@@ -23,6 +23,14 @@ func (channel *Channel) Name() string {
|
||||
return channel.name
|
||||
}
|
||||
|
||||
func (channel *Channel) State() TargetState {
|
||||
return TargetState{
|
||||
Kind: "channel",
|
||||
Name: channel.name,
|
||||
Users: channel.userlist.Users(),
|
||||
}
|
||||
}
|
||||
|
||||
// UserList gets the channel userlist
|
||||
func (channel *Channel) UserList() list.Immutable {
|
||||
return channel.userlist.Immutable()
|
||||
@@ -33,7 +41,7 @@ func (channel *Channel) Parted() bool {
|
||||
return channel.parted
|
||||
}
|
||||
|
||||
// Handle handles messages routed to this channel by the client's event loop
|
||||
// AddHandler handles messages routed to this channel by the client's event loop
|
||||
func (channel *Channel) Handle(event *Event, client *Client) {
|
||||
switch event.Name() {
|
||||
case "packet.join":
|
||||
|
||||
@@ -11,14 +11,15 @@ import (
|
||||
"fmt"
|
||||
mathRand "math/rand"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.aiterp.net/gisle/irc/ircutil"
|
||||
"git.aiterp.net/gisle/irc/isupport"
|
||||
"git.aiterp.net/gisle/irc/list"
|
||||
"github.com/gissleh/irc/ircutil"
|
||||
"github.com/gissleh/irc/isupport"
|
||||
"github.com/gissleh/irc/list"
|
||||
)
|
||||
|
||||
var supportedCaps = []string{
|
||||
@@ -31,6 +32,7 @@ var supportedCaps = []string{
|
||||
"extended-join",
|
||||
"chghost",
|
||||
"account-tag",
|
||||
"echo-message",
|
||||
}
|
||||
|
||||
// ErrNoConnection is returned if you try to do something requiring a connection,
|
||||
@@ -82,7 +84,7 @@ type Client struct {
|
||||
|
||||
status *Status
|
||||
targets []Target
|
||||
targteIds map[Target]string
|
||||
targetIds map[Target]string
|
||||
}
|
||||
|
||||
// New creates a new client. The context can be context.Background if you want manually to
|
||||
@@ -96,7 +98,7 @@ func New(ctx context.Context, config Config) *Client {
|
||||
capEnabled: make(map[string]bool),
|
||||
capData: make(map[string]string),
|
||||
config: config.WithDefaults(),
|
||||
targteIds: make(map[Target]string, 16),
|
||||
targetIds: make(map[Target]string, 16),
|
||||
status: &Status{},
|
||||
}
|
||||
|
||||
@@ -170,6 +172,39 @@ func (client *Client) Ready() bool {
|
||||
return client.ready
|
||||
}
|
||||
|
||||
func (client *Client) State() ClientState {
|
||||
client.mutex.RLock()
|
||||
state := ClientState{
|
||||
Nick: client.nick,
|
||||
User: client.user,
|
||||
Host: client.host,
|
||||
Connected: client.conn != nil,
|
||||
Ready: client.ready,
|
||||
ISupport: client.isupport.State(),
|
||||
Caps: make([]string, 0, len(client.capEnabled)),
|
||||
Targets: make([]TargetState, 0, len(client.targets)),
|
||||
}
|
||||
|
||||
for key, enabled := range client.capEnabled {
|
||||
if enabled {
|
||||
state.Caps = append(state.Caps, key)
|
||||
}
|
||||
}
|
||||
|
||||
for _, target := range client.targets {
|
||||
tstate := target.State()
|
||||
tstate.ID = client.targetIds[target]
|
||||
|
||||
state.Targets = append(state.Targets, tstate)
|
||||
}
|
||||
|
||||
client.mutex.RUnlock()
|
||||
|
||||
sort.Strings(state.Caps)
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
// Connect connects to the server by addr.
|
||||
func (client *Client) Connect(addr string, ssl bool) (err error) {
|
||||
var conn net.Conn
|
||||
@@ -411,12 +446,12 @@ func (client *Client) EmitInput(line string, target Target) context.Context {
|
||||
if target != nil {
|
||||
client.mutex.RLock()
|
||||
event.targets = append(event.targets, target)
|
||||
event.targetIds[target] = client.targteIds[target]
|
||||
event.targetIds[target] = client.targetIds[target]
|
||||
client.mutex.RUnlock()
|
||||
} else {
|
||||
client.mutex.RLock()
|
||||
event.targets = append(event.targets, client.status)
|
||||
event.targetIds[client.status] = client.targteIds[client.status]
|
||||
event.targetIds[client.status] = client.targetIds[client.status]
|
||||
client.mutex.RUnlock()
|
||||
}
|
||||
|
||||
@@ -576,7 +611,7 @@ func (client *Client) AddTarget(target Target) (id string, err error) {
|
||||
|
||||
id = generateClientID()
|
||||
client.targets = append(client.targets, target)
|
||||
client.targteIds[target] = id
|
||||
client.targetIds[target] = id
|
||||
|
||||
return
|
||||
}
|
||||
@@ -592,11 +627,11 @@ func (client *Client) RemoveTarget(target Target) (id string, err error) {
|
||||
|
||||
for i := range client.targets {
|
||||
if target == client.targets[i] {
|
||||
id = client.targteIds[target]
|
||||
id = client.targetIds[target]
|
||||
|
||||
client.targets[i] = client.targets[len(client.targets)-1]
|
||||
client.targets = client.targets[:len(client.targets)-1]
|
||||
delete(client.targteIds, target)
|
||||
delete(client.targetIds, target)
|
||||
|
||||
// Ensure the channel has been parted
|
||||
if channel, ok := target.(*Channel); ok && !channel.parted {
|
||||
@@ -828,7 +863,7 @@ func (client *Client) handleEvent(event *Event) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle ISupport
|
||||
// AddHandler ISupport
|
||||
case "packet.005":
|
||||
{
|
||||
for _, token := range event.Args[1:] {
|
||||
@@ -1002,6 +1037,7 @@ func (client *Client) handleEvent(event *Event) {
|
||||
}
|
||||
|
||||
if event.Nick == client.nick {
|
||||
channel.parted = true
|
||||
client.RemoveTarget(channel)
|
||||
} else {
|
||||
client.handleInTarget(channel, event)
|
||||
@@ -1016,6 +1052,7 @@ func (client *Client) handleEvent(event *Event) {
|
||||
}
|
||||
|
||||
if event.Arg(1) == client.nick {
|
||||
channel.parted = true
|
||||
client.RemoveTarget(channel)
|
||||
} else {
|
||||
client.handleInTarget(channel, event)
|
||||
@@ -1159,7 +1196,7 @@ func (client *Client) handleEvent(event *Event) {
|
||||
channels = append(channels, channel.Name())
|
||||
|
||||
rejoinEvent.targets = append(rejoinEvent.targets, target)
|
||||
rejoinEvent.targetIds[target] = client.targteIds[target]
|
||||
rejoinEvent.targetIds[target] = client.targetIds[target]
|
||||
}
|
||||
}
|
||||
client.mutex.RUnlock()
|
||||
@@ -1197,7 +1234,7 @@ func (client *Client) handleInTargets(nick string, event *Event) {
|
||||
target.Handle(event, client)
|
||||
|
||||
event.targets = append(event.targets, target)
|
||||
event.targetIds[target] = client.targteIds[target]
|
||||
event.targetIds[target] = client.targetIds[target]
|
||||
}
|
||||
case *Query:
|
||||
{
|
||||
@@ -1205,7 +1242,7 @@ func (client *Client) handleInTargets(nick string, event *Event) {
|
||||
target.Handle(event, client)
|
||||
|
||||
event.targets = append(event.targets, target)
|
||||
event.targetIds[target] = client.targteIds[target]
|
||||
event.targetIds[target] = client.targetIds[target]
|
||||
}
|
||||
}
|
||||
case *Status:
|
||||
@@ -1214,7 +1251,7 @@ func (client *Client) handleInTargets(nick string, event *Event) {
|
||||
target.Handle(event, client)
|
||||
|
||||
event.targets = append(event.targets, target)
|
||||
event.targetIds[target] = client.targteIds[target]
|
||||
event.targetIds[target] = client.targetIds[target]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1233,7 +1270,7 @@ func (client *Client) handleInTarget(target Target, event *Event) {
|
||||
target.Handle(event, client)
|
||||
|
||||
event.targets = append(event.targets, target)
|
||||
event.targetIds[target] = client.targteIds[target]
|
||||
event.targetIds[target] = client.targetIds[target]
|
||||
|
||||
client.mutex.RUnlock()
|
||||
}
|
||||
|
||||
+120
-111
@@ -5,15 +5,15 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"git.aiterp.net/gisle/irc"
|
||||
"git.aiterp.net/gisle/irc/handlers"
|
||||
"git.aiterp.net/gisle/irc/internal/irctest"
|
||||
"github.com/gissleh/irc"
|
||||
"github.com/gissleh/irc/handlers"
|
||||
"github.com/gissleh/irc/internal/irctest"
|
||||
)
|
||||
|
||||
// Integration test below, brace yourself.
|
||||
func TestClient(t *testing.T) {
|
||||
irc.Handle(handlers.Input)
|
||||
irc.Handle(handlers.MRoleplay)
|
||||
irc.AddHandler(handlers.Input)
|
||||
irc.AddHandler(handlers.MRoleplay)
|
||||
|
||||
client := irc.New(context.Background(), irc.Config{
|
||||
Nick: "Test",
|
||||
@@ -31,13 +31,13 @@ func TestClient(t *testing.T) {
|
||||
interaction := irctest.Interaction{
|
||||
Strict: false,
|
||||
Lines: []irctest.InteractionLine{
|
||||
{Kind: 'C', Data: "CAP LS 302"},
|
||||
{Kind: 'C', Data: "NICK Test"},
|
||||
{Kind: 'C', Data: "USER Tester 8 * :..."},
|
||||
{Kind: 'S', Data: ":testserver.example.com CAP * LS :multi-prefix chghost userhost-in-names vendorname/custom-stuff echo-message =malformed vendorname/advanced-custom-stuff=things,and,items"},
|
||||
{Kind: 'C', Data: "CAP REQ :multi-prefix chghost userhost-in-names"},
|
||||
{Kind: 'S', Data: ":testserver.example.com CAP * ACK :multi-prefix userhost-in-names"},
|
||||
{Kind: 'C', Data: "CAP END"},
|
||||
{Client: "CAP LS 302"},
|
||||
{Client: "NICK Test"},
|
||||
{Client: "USER Tester 8 * :..."},
|
||||
{Server: ":testserver.example.com CAP * LS :multi-prefix chghost userhost-in-names vendorname/custom-stuff echo-message =malformed vendorname/advanced-custom-stuff=things,and,items"},
|
||||
{Client: "CAP REQ :multi-prefix chghost userhost-in-names"},
|
||||
{Server: ":testserver.example.com CAP * ACK :multi-prefix userhost-in-names"},
|
||||
{Client: "CAP END"},
|
||||
{Callback: func() error {
|
||||
if !client.CapEnabled("multi-prefix") {
|
||||
return errors.New("multi-prefix cap should be enabled.")
|
||||
@@ -54,39 +54,39 @@ func TestClient(t *testing.T) {
|
||||
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'S', Data: ":testserver.example.com 433 * Test :Nick is not available"},
|
||||
{Kind: 'C', Data: "NICK Test2"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 433 * Test2 :Nick is not available"},
|
||||
{Kind: 'C', Data: "NICK Test3"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 433 * Test3 :Nick is not available"},
|
||||
{Kind: 'C', Data: "NICK Test4"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 433 * Test4 :Nick is not available"},
|
||||
{Kind: 'C', Data: "NICK Test768"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 001 Test768 :Welcome to the TestServer Internet Relay Chat Network test"},
|
||||
{Kind: 'C', Data: "WHO Test768*"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 002 Test768 :Your host is testserver.example.com[testserver.example.com/6667], running version charybdis-4-rc3"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 003 Test768 :This server was created Fri Nov 25 2016 at 17:28:20 CET"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 004 Test768 testserver.example.com charybdis-4-rc3 DQRSZagiloswxz CFILNPQbcefgijklmnopqrstvz bkloveqjfI"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 005 Test768 FNC SAFELIST ELIST=CTU MONITOR=100 WHOX ETRACE KNOCK CHANTYPES=#& EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz CHANLIMIT=#&:15 :are supported by this server"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 005 Test768 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=TestServer STATUSMSG=@+ CALLERID=g CASEMAPPING=rfc1459 NICKLEN=30 MAXNICKLEN=31 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 005 Test768 TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,&acjmorsuxz| CLIENTVER=3.0 :are supported by this server"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 251 Test768 :There are 0 users and 2 invisible on 1 servers"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 254 Test768 1 :channels formed"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 255 Test768 :I have 2 clients and 0 servers"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 265 Test768 2 2 :Current local users 2, max 2"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 266 Test768 2 2 :Current global users 2, max 2"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 250 Test768 :Highest connection count: 2 (2 clients) (8 connections received)"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 375 Test768 :- testserver.example.com Message of the Day - "},
|
||||
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- This server is only for testing irce, not chatting. If you happen"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- to connect to it by accident, please disconnect immediately."},
|
||||
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- "},
|
||||
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- - #Test :: Test Channel"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- - #Test2 :: Other Test Channel"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 376 Test768 :End of /MOTD command."},
|
||||
{Kind: 'S', Data: ":testserver.example.com 352 Test768 * ~Tester testclient.example.com testserver.example.com Test768 H :0 ..."},
|
||||
{Kind: 'S', Data: ":Test768 MODE Test768 :+i"},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com"},
|
||||
{Server: ":testserver.example.com 433 * Test :Nick is not available"},
|
||||
{Client: "NICK Test2"},
|
||||
{Server: ":testserver.example.com 433 * Test2 :Nick is not available"},
|
||||
{Client: "NICK Test3"},
|
||||
{Server: ":testserver.example.com 433 * Test3 :Nick is not available"},
|
||||
{Client: "NICK Test4"},
|
||||
{Server: ":testserver.example.com 433 * Test4 :Nick is not available"},
|
||||
{Client: "NICK Test768"},
|
||||
{Server: ":testserver.example.com 001 Test768 :Welcome to the TestServer Internet Relay Chat Network test"},
|
||||
{Client: "WHO Test768*"},
|
||||
{Server: ":testserver.example.com 002 Test768 :Your host is testserver.example.com[testserver.example.com/6667], running version charybdis-4-rc3"},
|
||||
{Server: ":testserver.example.com 003 Test768 :This server was created Fri Nov 25 2016 at 17:28:20 CET"},
|
||||
{Server: ":testserver.example.com 004 Test768 testserver.example.com charybdis-4-rc3 DQRSZagiloswxz CFILNPQbcefgijklmnopqrstvz bkloveqjfI"},
|
||||
{Server: ":testserver.example.com 005 Test768 FNC SAFELIST ELIST=CTU MONITOR=100 WHOX ETRACE KNOCK CHANTYPES=#& EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz CHANLIMIT=#&:15 :are supported by this server"},
|
||||
{Server: ":testserver.example.com 005 Test768 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=TestServer STATUSMSG=@+ CALLERID=g CASEMAPPING=rfc1459 NICKLEN=30 MAXNICKLEN=31 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server"},
|
||||
{Server: ":testserver.example.com 005 Test768 TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,&acjmorsuxz| CLIENTVER=3.0 :are supported by this server"},
|
||||
{Server: ":testserver.example.com 251 Test768 :There are 0 users and 2 invisible on 1 servers"},
|
||||
{Server: ":testserver.example.com 254 Test768 1 :channels formed"},
|
||||
{Server: ":testserver.example.com 255 Test768 :I have 2 clients and 0 servers"},
|
||||
{Server: ":testserver.example.com 265 Test768 2 2 :Current local users 2, max 2"},
|
||||
{Server: ":testserver.example.com 266 Test768 2 2 :Current global users 2, max 2"},
|
||||
{Server: ":testserver.example.com 250 Test768 :Highest connection count: 2 (2 clients) (8 connections received)"},
|
||||
{Server: ":testserver.example.com 375 Test768 :- testserver.example.com Message of the Day - "},
|
||||
{Server: ":testserver.example.com 372 Test768 :- This server is only for testing irce, not chatting. If you happen"},
|
||||
{Server: ":testserver.example.com 372 Test768 :- to connect to it by accident, please disconnect immediately."},
|
||||
{Server: ":testserver.example.com 372 Test768 :- "},
|
||||
{Server: ":testserver.example.com 372 Test768 :- - #Test :: Test Channel"},
|
||||
{Server: ":testserver.example.com 372 Test768 :- - #Test2 :: Other Test Channel"},
|
||||
{Server: ":testserver.example.com 376 Test768 :End of /MOTD command."},
|
||||
{Server: ":testserver.example.com 352 Test768 * ~Tester testclient.example.com testserver.example.com Test768 H :0 ..."},
|
||||
{Server: ":Test768 MODE Test768 :+i"},
|
||||
{Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com"},
|
||||
{Callback: func() error {
|
||||
if client.Nick() != "Test768" {
|
||||
return errors.New("client.Nick shouldn't be " + client.Nick())
|
||||
@@ -104,12 +104,12 @@ func TestClient(t *testing.T) {
|
||||
client.Join("#Test")
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'C', Data: "JOIN #Test"},
|
||||
{Kind: 'S', Data: ":Test768!~Tester@127.0.0.1 JOIN #Test *"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 353 Test768 = #Test :Test768!~Tester@127.0.0.1 @+Gisle!irce@10.32.0.1"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 366 Test768 #Test :End of /NAMES list."},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com"},
|
||||
{Client: "JOIN #Test"},
|
||||
{Server: ":Test768!~Tester@127.0.0.1 JOIN #Test *"},
|
||||
{Server: ":testserver.example.com 353 Test768 = #Test :Test768!~Tester@127.0.0.1 @+Gisle!irce@10.32.0.1"},
|
||||
{Server: ":testserver.example.com 366 Test768 #Test :End of /NAMES list."},
|
||||
{Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com"},
|
||||
{Callback: func() error {
|
||||
if client.Channel("#Test") == nil {
|
||||
return errors.New("Channel #Test not found")
|
||||
@@ -117,13 +117,13 @@ func TestClient(t *testing.T) {
|
||||
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 MODE #Test +osv Test768 Test768"},
|
||||
{Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 MODE #Test +N-s "},
|
||||
{Kind: 'S', Data: ":Test1234!~test2@172.17.37.1 JOIN #Test Test1234"},
|
||||
{Kind: 'S', Data: ":Test4321!~test2@172.17.37.1 JOIN #Test Test1234"},
|
||||
{Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 MODE #Test +v Test1234"},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com"},
|
||||
{Server: ":Gisle!~irce@10.32.0.1 MODE #Test +osv Test768 Test768"},
|
||||
{Server: ":Gisle!~irce@10.32.0.1 MODE #Test +N-s "},
|
||||
{Server: ":Test1234!~test2@172.17.37.1 JOIN #Test Test1234"},
|
||||
{Server: ":Test4321!~test2@172.17.37.1 JOIN #Test Test1234"},
|
||||
{Server: ":Gisle!~irce@10.32.0.1 MODE #Test +v Test1234"},
|
||||
{Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com"},
|
||||
{Callback: func() error {
|
||||
channel := client.Channel("#Test")
|
||||
if channel == nil {
|
||||
@@ -145,14 +145,14 @@ func TestClient(t *testing.T) {
|
||||
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'S', Data: ":Test1234!~test2@172.17.37.1 NICK Hunter2"},
|
||||
{Kind: 'S', Data: ":Hunter2!~test2@172.17.37.1 AWAY :Doing stuff"},
|
||||
{Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 AWAY"},
|
||||
{Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 PART #Test :Leaving the channel"},
|
||||
{Kind: 'S', Data: ":Hunter2!~test2@172.17.37.1 CHGHOST test2 some.awesome.virtual.host"},
|
||||
{Kind: 'S', Data: "@account=Hunter2 :Test4321!~test2@172.17.37.1 PRIVMSG #Test :Hello World."},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com"},
|
||||
{Server: ":Test1234!~test2@172.17.37.1 NICK Hunter2"},
|
||||
{Server: ":Hunter2!~test2@172.17.37.1 AWAY :Doing stuff"},
|
||||
{Server: ":Gisle!~irce@10.32.0.1 AWAY"},
|
||||
{Server: ":Gisle!~irce@10.32.0.1 PART #Test :Leaving the channel"},
|
||||
{Server: ":Hunter2!~test2@172.17.37.1 CHGHOST test2 some.awesome.virtual.host"},
|
||||
{Server: "@account=Hunter2 :Test4321!~test2@172.17.37.1 PRIVMSG #Test :Hello World."},
|
||||
{Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com"},
|
||||
{Callback: func() error {
|
||||
channel := client.Channel("#Test")
|
||||
if channel == nil {
|
||||
@@ -188,9 +188,9 @@ func TestClient(t *testing.T) {
|
||||
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'S', Data: ":Hunter2!~test2@172.17.37.1 PRIVMSG Test768 :Hello, World"},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com"},
|
||||
{Server: ":Hunter2!~test2@172.17.37.1 PRIVMSG Test768 :Hello, World"},
|
||||
{Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com"},
|
||||
{Callback: func() error {
|
||||
query := client.Query("Hunter2")
|
||||
if query == nil {
|
||||
@@ -199,12 +199,12 @@ func TestClient(t *testing.T) {
|
||||
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'S', Data: ":Hunter2!~test2@172.17.37.1 NICK SevenAsterisks"},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com"},
|
||||
{Server: ":Hunter2!~test2@172.17.37.1 NICK SevenAsterisks"},
|
||||
{Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com"},
|
||||
{Callback: func() error {
|
||||
oldQuerry := client.Query("Hunter2")
|
||||
if oldQuerry != nil {
|
||||
oldQuery := client.Query("Hunter2")
|
||||
if oldQuery != nil {
|
||||
return errors.New("Did find query by old name")
|
||||
}
|
||||
|
||||
@@ -219,8 +219,8 @@ func TestClient(t *testing.T) {
|
||||
client.EmitInput("/invalidcommand stuff and things", nil)
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'C', Data: "INVALIDCOMMAND stuff and things"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 421 Test768 INVALIDCOMMAND :Unknown command"},
|
||||
{Client: "INVALIDCOMMAND stuff and things"},
|
||||
{Server: ":testserver.example.com 421 Test768 INVALIDCOMMAND :Unknown command"},
|
||||
{Callback: func() error {
|
||||
channel := client.Channel("#Test")
|
||||
if channel == nil {
|
||||
@@ -233,14 +233,14 @@ func TestClient(t *testing.T) {
|
||||
client.EmitInput("Hello again", channel)
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'C', Data: "PRIVMSG #Test :\x01ACTION does stuff\x01"},
|
||||
{Kind: 'C', Data: "PRIVMSG #Test :\x01ACTION describes stuff\x01"},
|
||||
{Kind: 'C', Data: "PRIVMSG #Test :Hello, World"},
|
||||
{Kind: 'C', Data: "PRIVMSG #Test :Hello again"},
|
||||
{Kind: 'S', Data: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :\x01ACTION does stuff\x01"},
|
||||
{Kind: 'S', Data: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :\x01ACTION describes stuff\x01"},
|
||||
{Kind: 'S', Data: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :Hello, World"},
|
||||
{Kind: 'S', Data: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :Hello again"},
|
||||
{Client: "PRIVMSG #Test :\x01ACTION does stuff\x01"},
|
||||
{Client: "PRIVMSG #Test :\x01ACTION describes stuff\x01"},
|
||||
{Client: "PRIVMSG #Test :Hello, World"},
|
||||
{Client: "PRIVMSG #Test :Hello again"},
|
||||
{Server: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :\x01ACTION does stuff\x01"},
|
||||
{Server: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :\x01ACTION describes stuff\x01"},
|
||||
{Server: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :Hello, World"},
|
||||
{Server: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :Hello again"},
|
||||
{Callback: func() error {
|
||||
channel := client.Channel("#Test")
|
||||
if channel == nil {
|
||||
@@ -251,10 +251,10 @@ func TestClient(t *testing.T) {
|
||||
client.EmitInput("/npcac Test_NPC stuffs things", channel)
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'C', Data: "MODE #Test +N"},
|
||||
{Kind: 'C', Data: "NPCA #Test Test_NPC :stuffs things"},
|
||||
{Kind: 'S', Data: ":Test768!~Tester@127.0.0.1 MODE #Test +N"},
|
||||
{Kind: 'S', Data: ":\x1FTest_NPC\x1F!Test768@npc.fakeuser.invalid PRIVMSG #Test :\x01ACTION stuffs things\x01"},
|
||||
{Client: "MODE #Test +N"},
|
||||
{Client: "NPCA #Test Test_NPC :stuffs things"},
|
||||
{Server: ":Test768!~Tester@127.0.0.1 MODE #Test +N"},
|
||||
{Server: ":\x1FTest_NPC\x1F!Test768@npc.fakeuser.invalid PRIVMSG #Test :\x01ACTION stuffs things\x01"},
|
||||
{Callback: func() error {
|
||||
channel := client.Channel("#Test")
|
||||
if channel == nil {
|
||||
@@ -265,16 +265,21 @@ func TestClient(t *testing.T) {
|
||||
client.Sayf(channel.Name(), "Hello, %s", "World")
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'C', Data: "PRIVMSG #Test :\x01ACTION does stuff with 42 things\x01"},
|
||||
{Kind: 'C', Data: "PRIVMSG #Test :Hello, World"},
|
||||
{Client: "PRIVMSG #Test :\x01ACTION does stuff with 42 things\x01"},
|
||||
{Client: "PRIVMSG #Test :Hello, World"},
|
||||
{Callback: func() error {
|
||||
client.Part("#Test")
|
||||
return nil
|
||||
channel := client.Channel("#Test")
|
||||
if channel == nil {
|
||||
return errors.New("#Test doesn't exist")
|
||||
}
|
||||
|
||||
_, err := client.RemoveTarget(channel)
|
||||
return err
|
||||
}},
|
||||
{Kind: 'C', Data: "PART #Test"},
|
||||
{Kind: 'S', Data: ":Test768!~Tester@127.0.0.1 PART #Test"},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com"},
|
||||
{Client: "PART #Test"},
|
||||
{Server: ":Test768!~Tester@127.0.0.1 PART #Test"},
|
||||
{Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com"},
|
||||
{Callback: func() error {
|
||||
if client.Channel("#Test") != nil {
|
||||
return errors.New("#Test is still there.")
|
||||
@@ -286,12 +291,12 @@ func TestClient(t *testing.T) {
|
||||
client.Join("#Test2")
|
||||
return nil
|
||||
}},
|
||||
{Kind: 'C', Data: "JOIN #Test2"},
|
||||
{Kind: 'S', Data: ":Test768!~Tester@127.0.0.1 JOIN #Test2 *"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 353 Test768 = #Test2 :Test768!~Tester@127.0.0.1 +DoomedUser!doom@example.com @+ZealousMod!zeal@example.com"},
|
||||
{Kind: 'S', Data: ":testserver.example.com 366 Test768 #Test2 :End of /NAMES list."},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com"},
|
||||
{Client: "JOIN #Test2"},
|
||||
{Server: ":Test768!~Tester@127.0.0.1 JOIN #Test2 *"},
|
||||
{Server: ":testserver.example.com 353 Test768 = #Test2 :Test768!~Tester@127.0.0.1 +DoomedUser!doom@example.com @+ZealousMod!zeal@example.com"},
|
||||
{Server: ":testserver.example.com 366 Test768 #Test2 :End of /NAMES list."},
|
||||
{Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com"},
|
||||
{Callback: func() error {
|
||||
channel := client.Channel("#Test2")
|
||||
if channel == nil {
|
||||
@@ -300,9 +305,9 @@ func TestClient(t *testing.T) {
|
||||
|
||||
return irctest.AssertUserlist(t, channel, "@ZealousMod", "+DoomedUser", "Test768")
|
||||
}},
|
||||
{Kind: 'S', Data: ":ZealousMod!zeal@example.com KICK #Test2 DoomedUser :Kickety kick"},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com sync"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com sync"},
|
||||
{Server: ":ZealousMod!zeal@example.com KICK #Test2 DoomedUser :Kickety kick"},
|
||||
{Server: "PING :testserver.example.com sync"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com sync"},
|
||||
{Callback: func() error {
|
||||
channel := client.Channel("#Test2")
|
||||
if channel == nil {
|
||||
@@ -311,9 +316,9 @@ func TestClient(t *testing.T) {
|
||||
|
||||
return irctest.AssertUserlist(t, channel, "@ZealousMod", "Test768")
|
||||
}},
|
||||
{Kind: 'S', Data: ":ZealousMod!zeal@example.com KICK #Test2 Test768 :Kickety kick"},
|
||||
{Kind: 'S', Data: "PING :testserver.example.com sync"}, // Ping/Pong to sync.
|
||||
{Kind: 'C', Data: "PONG :testserver.example.com sync"},
|
||||
{Server: ":ZealousMod!zeal@example.com KICK #Test2 Test768 :Kickety kick"},
|
||||
{Server: "PING :testserver.example.com sync"}, // Ping/Pong to sync.
|
||||
{Client: "PONG :testserver.example.com sync"},
|
||||
{Callback: func() error {
|
||||
if client.Channel("#Test2") != nil {
|
||||
return errors.New("#Test2 is still there.")
|
||||
@@ -348,8 +353,12 @@ func TestClient(t *testing.T) {
|
||||
t.Error("CBErr:", fail.CBErr)
|
||||
t.Error("Result:", fail.Result)
|
||||
if fail.Index >= 0 {
|
||||
t.Error("Line.Kind:", interaction.Lines[fail.Index].Kind)
|
||||
t.Error("Line.Data:", interaction.Lines[fail.Index].Data)
|
||||
if interaction.Lines[fail.Index].Server != "" {
|
||||
t.Error("Line.Server:", interaction.Lines[fail.Index].Server)
|
||||
}
|
||||
if interaction.Lines[fail.Index].Client != "" {
|
||||
t.Error("Line.Client:", interaction.Lines[fail.Index].Client)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +377,7 @@ func TestParenthesesBug(t *testing.T) {
|
||||
Nick: "Stuff",
|
||||
})
|
||||
|
||||
irc.Handle(func(event *irc.Event, client *irc.Client) {
|
||||
irc.AddHandler(func(event *irc.Event, client *irc.Client) {
|
||||
if event.Name() == "packet.privmsg" || event.Nick == "Dante" {
|
||||
gotMessage = true
|
||||
|
||||
|
||||
+46
-5
@@ -6,10 +6,12 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/gissleh/irc/handlers"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.aiterp.net/gisle/irc"
|
||||
"github.com/gissleh/irc"
|
||||
)
|
||||
|
||||
var flagNick = flag.String("nick", "Test", "The client nick")
|
||||
@@ -25,6 +27,9 @@ func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
irc.AddHandler(handlers.Input)
|
||||
irc.AddHandler(handlers.MRoleplay)
|
||||
|
||||
client := irc.New(ctx, irc.Config{
|
||||
Nick: *flagNick,
|
||||
User: *flagUser,
|
||||
@@ -37,13 +42,49 @@ func main() {
|
||||
fmt.Fprintf(os.Stderr, "Failed to connect: %s", err)
|
||||
}
|
||||
|
||||
irc.Handle(func(event *irc.Event, client *irc.Client) {
|
||||
json, err := json.MarshalIndent(event, "", " ")
|
||||
var target irc.Target
|
||||
irc.AddHandler(func(event *irc.Event, client *irc.Client) {
|
||||
if event.Name() == "input.target" {
|
||||
name := event.Arg(0)
|
||||
|
||||
if client.ISupport().IsChannel(name) {
|
||||
log.Println("Set target channel", name)
|
||||
target = client.Channel(name)
|
||||
} else if len(name) > 0 {
|
||||
log.Println("Set target query", name)
|
||||
target = client.Query(name)
|
||||
} else {
|
||||
log.Println("Set target status")
|
||||
target = client.Status()
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
log.Println("Target does not exist, set to status")
|
||||
target = client.Status()
|
||||
}
|
||||
|
||||
event.PreventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
if event.Name() == "input.clientstatus" {
|
||||
j, err := json.MarshalIndent(client.State(), "", " ")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(j))
|
||||
|
||||
event.PreventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
j, err := json.MarshalIndent(event, "", " ")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(json))
|
||||
fmt.Println(string(j))
|
||||
})
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
@@ -53,6 +94,6 @@ func main() {
|
||||
break
|
||||
}
|
||||
|
||||
client.EmitInput(string(line[:len(line)-1]), client.Status())
|
||||
client.EmitInput(string(line[:len(line)-1]), target)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module github.com/gissleh/irc
|
||||
|
||||
go 1.12
|
||||
|
||||
require github.com/stretchr/testify v1.4.0
|
||||
@@ -0,0 +1,13 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gissleh/irc v0.0.0-20190420194256-9a9cd33fd83c h1:SK61/nAM56ylhrhYi1289yOBpsupGEOQTZuFYxDNhrw=
|
||||
github.com/gissleh/irc v0.0.0-20190420194256-9a9cd33fd83c/go.mod h1:7uklwlzI0XI2O8ttooTH0suUpUmQ8Pviplg36smc1Aw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -14,12 +14,12 @@ func emit(event *Event, client *Client) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle adds a new handler to the irc handling. The handler may be called from multiple threads at the same
|
||||
// AddHandler adds a new handler to the irc handling. The handler may be called from multiple threads at the same
|
||||
// time, so external resources should be locked if there are multiple clients. Adding handlers is not thread
|
||||
// safe and should be done prior to clients being created.Handle. Also, this handler will block the individual
|
||||
// safe and should be done prior to clients being created.AddHandler. Also, this handler will block the individual
|
||||
// client's event loop, so long operations that include network requests and the like should be done in a
|
||||
// goroutine with the needed data **copied** from the handler function.
|
||||
func Handle(handler Handler) {
|
||||
func AddHandler(handler Handler) {
|
||||
eventHandler.handlers = append(eventHandler.handlers, handler)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.aiterp.net/gisle/irc"
|
||||
"github.com/gissleh/irc"
|
||||
)
|
||||
|
||||
func TestHandle(t *testing.T) {
|
||||
@@ -18,7 +18,7 @@ func TestHandle(t *testing.T) {
|
||||
event := irc.NewEvent("test", eventName)
|
||||
handled := false
|
||||
|
||||
irc.Handle(func(event *irc.Event, client *irc.Client) {
|
||||
irc.AddHandler(func(event *irc.Event, client *irc.Client) {
|
||||
t.Log("Got:", event.Kind(), event.Verb())
|
||||
|
||||
if event.Kind() == "test" && event.Verb() == eventName {
|
||||
|
||||
+27
-2
@@ -1,8 +1,9 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"git.aiterp.net/gisle/irc"
|
||||
"git.aiterp.net/gisle/irc/ircutil"
|
||||
"github.com/gissleh/irc"
|
||||
"github.com/gissleh/irc/ircutil"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Input handles the default input.
|
||||
@@ -70,6 +71,18 @@ func Input(event *irc.Event, client *irc.Client) {
|
||||
cuts := ircutil.CutMessage(event.Text, overhead)
|
||||
for _, cut := range cuts {
|
||||
client.SendCTCP("ACTION", target.Name(), false, cut)
|
||||
|
||||
if !client.CapEnabled("echo-message") {
|
||||
event := irc.NewEvent("echo", "action")
|
||||
event.Time = time.Now()
|
||||
event.Nick = client.Nick()
|
||||
event.User = client.User()
|
||||
event.Host = client.Host()
|
||||
event.Args = []string{target.Name()}
|
||||
event.Text = cut
|
||||
|
||||
client.EmitNonBlocking(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +101,18 @@ func Input(event *irc.Event, client *irc.Client) {
|
||||
cuts := ircutil.CutMessage(text, overhead)
|
||||
for _, cut := range cuts {
|
||||
client.SendCTCP("ACTION", targetName, false, cut)
|
||||
|
||||
if !client.CapEnabled("echo-message") {
|
||||
event := irc.NewEvent("echo", "action")
|
||||
event.Time = time.Now()
|
||||
event.Nick = client.Nick()
|
||||
event.User = client.User()
|
||||
event.Host = client.Host()
|
||||
event.Args = []string{targetName}
|
||||
event.Text = cut
|
||||
|
||||
client.EmitNonBlocking(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package handlers
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.aiterp.net/gisle/irc"
|
||||
"git.aiterp.net/gisle/irc/ircutil"
|
||||
"github.com/gissleh/irc"
|
||||
"github.com/gissleh/irc/ircutil"
|
||||
)
|
||||
|
||||
// MRoleplay is a handler that adds commands for cutting NPC commands, as well as cleaning up
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.aiterp.net/gisle/irc"
|
||||
"github.com/gissleh/irc"
|
||||
)
|
||||
|
||||
// AssertUserlist compares the userlist to a list of prefixed nicks
|
||||
|
||||
@@ -49,58 +49,49 @@ func (interaction *Interaction) Listen() (addr string, err error) {
|
||||
for i := 0; i < len(lines); i++ {
|
||||
line := lines[i]
|
||||
|
||||
if line.Data != "" {
|
||||
switch line.Kind {
|
||||
case 'S':
|
||||
{
|
||||
_, err := conn.Write(append([]byte(line.Data), '\r', '\n'))
|
||||
if err != nil {
|
||||
interaction.Failure = &InteractionFailure{
|
||||
Index: i, NetErr: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
case 'C':
|
||||
{
|
||||
conn.SetReadDeadline(time.Now().Add(time.Second * 2))
|
||||
input, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
interaction.Failure = &InteractionFailure{
|
||||
Index: i, NetErr: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
input = strings.Replace(input, "\r", "", -1)
|
||||
input = strings.Replace(input, "\n", "", 1)
|
||||
|
||||
match := line.Data
|
||||
success := false
|
||||
|
||||
if strings.HasSuffix(match, "*") {
|
||||
success = strings.HasPrefix(input, match[:len(match)-1])
|
||||
} else {
|
||||
success = match == input
|
||||
}
|
||||
|
||||
interaction.Log = append(interaction.Log, input)
|
||||
|
||||
if !success {
|
||||
if !interaction.Strict {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
interaction.Failure = &InteractionFailure{
|
||||
Index: i, Result: input,
|
||||
}
|
||||
return
|
||||
}
|
||||
if line.Server != "" {
|
||||
_, err := conn.Write(append([]byte(line.Server), '\r', '\n'))
|
||||
if err != nil {
|
||||
interaction.Failure = &InteractionFailure{
|
||||
Index: i, NetErr: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if line.Client != "" {
|
||||
conn.SetReadDeadline(time.Now().Add(time.Second * 2))
|
||||
input, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
interaction.Failure = &InteractionFailure{
|
||||
Index: i, NetErr: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
input = strings.Replace(input, "\r", "", -1)
|
||||
input = strings.Replace(input, "\n", "", 1)
|
||||
|
||||
if line.Callback != nil {
|
||||
match := line.Client
|
||||
success := false
|
||||
|
||||
if strings.HasSuffix(match, "*") {
|
||||
success = strings.HasPrefix(input, match[:len(match)-1])
|
||||
} else {
|
||||
success = match == input
|
||||
}
|
||||
|
||||
interaction.Log = append(interaction.Log, input)
|
||||
|
||||
if !success {
|
||||
if !interaction.Strict {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
interaction.Failure = &InteractionFailure{
|
||||
Index: i, Result: input,
|
||||
}
|
||||
return
|
||||
}
|
||||
} else if line.Callback != nil {
|
||||
err := line.Callback()
|
||||
if err != nil {
|
||||
interaction.Failure = &InteractionFailure{
|
||||
@@ -132,7 +123,7 @@ type InteractionFailure struct {
|
||||
// InteractionLine is part of an interaction, whether it is a line
|
||||
// that is sent to a client or a line expected from a client.
|
||||
type InteractionLine struct {
|
||||
Kind byte
|
||||
Data string
|
||||
Client string
|
||||
Server string
|
||||
Callback func() error
|
||||
}
|
||||
|
||||
@@ -4,15 +4,15 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"git.aiterp.net/gisle/irc/internal/irctest"
|
||||
"github.com/gissleh/irc/internal/irctest"
|
||||
)
|
||||
|
||||
func TestInteraction(t *testing.T) {
|
||||
interaction := irctest.Interaction{
|
||||
Lines: []irctest.InteractionLine{
|
||||
{Kind: 'C', Data: "FIRST MESSAGE"},
|
||||
{Kind: 'S', Data: "SERVER MESSAGE"},
|
||||
{Kind: 'C', Data: "SECOND MESSAGE"},
|
||||
{Client: "FIRST MESSAGE"},
|
||||
{Server: "SERVER MESSAGE"},
|
||||
{Client: "SECOND MESSAGE"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.aiterp.net/gisle/irc/ircutil"
|
||||
"github.com/gissleh/irc/ircutil"
|
||||
)
|
||||
|
||||
func TestCuts(t *testing.T) {
|
||||
|
||||
+39
-39
@@ -12,20 +12,15 @@ import (
|
||||
// of it. It's thread-safe through a reader/writer lock, so the locks will
|
||||
// only block in the short duration post-registration when the 005s come in
|
||||
type ISupport struct {
|
||||
lock sync.RWMutex
|
||||
raw map[string]string
|
||||
|
||||
prefixes map[rune]rune
|
||||
modeOrder string
|
||||
prefixOrder string
|
||||
chanModes []string
|
||||
lock sync.RWMutex
|
||||
state State
|
||||
}
|
||||
|
||||
// Get gets an isupport key. This is unprocessed data, and a helper should
|
||||
// be used if available.
|
||||
func (isupport *ISupport) Get(key string) (value string, ok bool) {
|
||||
isupport.lock.RLock()
|
||||
value, ok = isupport.raw[key]
|
||||
value, ok = isupport.state.Raw[key]
|
||||
isupport.lock.RUnlock()
|
||||
return
|
||||
}
|
||||
@@ -33,7 +28,7 @@ func (isupport *ISupport) Get(key string) (value string, ok bool) {
|
||||
// Number gets a key and converts it to a number.
|
||||
func (isupport *ISupport) Number(key string) (value int, ok bool) {
|
||||
isupport.lock.RLock()
|
||||
strValue, ok := isupport.raw[key]
|
||||
strValue, ok := isupport.state.Raw[key]
|
||||
isupport.lock.RUnlock()
|
||||
|
||||
if !ok {
|
||||
@@ -54,12 +49,12 @@ func (isupport *ISupport) ParsePrefixedNick(fullnick string) (nick, modes, prefi
|
||||
isupport.lock.RLock()
|
||||
defer isupport.lock.RUnlock()
|
||||
|
||||
if fullnick == "" || isupport.prefixes == nil {
|
||||
if fullnick == "" || isupport.state.Prefixes == nil {
|
||||
return fullnick, "", ""
|
||||
}
|
||||
|
||||
for i, ch := range fullnick {
|
||||
if mode, ok := isupport.prefixes[ch]; ok {
|
||||
if mode, ok := isupport.state.Prefixes[ch]; ok {
|
||||
modes += string(mode)
|
||||
prefixes += string(ch)
|
||||
} else {
|
||||
@@ -80,7 +75,7 @@ func (isupport *ISupport) HighestPrefix(prefixes string) rune {
|
||||
return rune(prefixes[0])
|
||||
}
|
||||
|
||||
for _, prefix := range isupport.prefixOrder {
|
||||
for _, prefix := range isupport.state.PrefixOrder {
|
||||
if strings.ContainsRune(prefixes, prefix) {
|
||||
return prefix
|
||||
}
|
||||
@@ -98,7 +93,7 @@ func (isupport *ISupport) HighestMode(modes string) rune {
|
||||
return rune(modes[0])
|
||||
}
|
||||
|
||||
for _, mode := range isupport.modeOrder {
|
||||
for _, mode := range isupport.state.ModeOrder {
|
||||
if strings.ContainsRune(modes, mode) {
|
||||
return mode
|
||||
}
|
||||
@@ -122,7 +117,7 @@ func (isupport *ISupport) IsModeHigher(current rune, other rune) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, mode := range isupport.modeOrder {
|
||||
for _, mode := range isupport.state.ModeOrder {
|
||||
if mode == current {
|
||||
return true
|
||||
} else if mode == other {
|
||||
@@ -137,7 +132,7 @@ func (isupport *ISupport) IsModeHigher(current rune, other rune) bool {
|
||||
func (isupport *ISupport) SortModes(modes string) string {
|
||||
result := ""
|
||||
|
||||
for _, ch := range isupport.modeOrder {
|
||||
for _, ch := range isupport.state.ModeOrder {
|
||||
for _, ch2 := range modes {
|
||||
if ch2 == ch {
|
||||
result += string(ch)
|
||||
@@ -152,7 +147,7 @@ func (isupport *ISupport) SortModes(modes string) string {
|
||||
func (isupport *ISupport) SortPrefixes(prefixes string) string {
|
||||
result := ""
|
||||
|
||||
for _, ch := range isupport.prefixOrder {
|
||||
for _, ch := range isupport.state.PrefixOrder {
|
||||
for _, ch2 := range prefixes {
|
||||
if ch2 == ch {
|
||||
result += string(ch)
|
||||
@@ -168,7 +163,7 @@ func (isupport *ISupport) Mode(prefix rune) rune {
|
||||
isupport.lock.RLock()
|
||||
defer isupport.lock.RUnlock()
|
||||
|
||||
return isupport.prefixes[prefix]
|
||||
return isupport.state.Prefixes[prefix]
|
||||
}
|
||||
|
||||
// Prefix gets the prefix for the mode. It's a bit slower
|
||||
@@ -178,7 +173,7 @@ func (isupport *ISupport) Prefix(mode rune) rune {
|
||||
isupport.lock.RLock()
|
||||
defer isupport.lock.RUnlock()
|
||||
|
||||
for prefix, mappedMode := range isupport.prefixes {
|
||||
for prefix, mappedMode := range isupport.state.Prefixes {
|
||||
if mappedMode == mode {
|
||||
return prefix
|
||||
}
|
||||
@@ -207,7 +202,7 @@ func (isupport *ISupport) IsChannel(targetName string) bool {
|
||||
isupport.lock.RLock()
|
||||
defer isupport.lock.RUnlock()
|
||||
|
||||
return strings.Contains(isupport.raw["CHANTYPES"], string(targetName[0]))
|
||||
return strings.Contains(isupport.state.Raw["CHANTYPES"], string(targetName[0]))
|
||||
}
|
||||
|
||||
// IsPermissionMode returns whether the flag is a permission mode
|
||||
@@ -215,7 +210,7 @@ func (isupport *ISupport) IsPermissionMode(flag rune) bool {
|
||||
isupport.lock.RLock()
|
||||
defer isupport.lock.RUnlock()
|
||||
|
||||
return strings.ContainsRune(isupport.modeOrder, flag)
|
||||
return strings.ContainsRune(isupport.state.ModeOrder, flag)
|
||||
}
|
||||
|
||||
// ModeTakesArgument returns true if the mode takes an argument
|
||||
@@ -224,17 +219,17 @@ func (isupport *ISupport) ModeTakesArgument(flag rune, plus bool) bool {
|
||||
defer isupport.lock.RUnlock()
|
||||
|
||||
// Permission modes always take an argument.
|
||||
if strings.ContainsRune(isupport.modeOrder, flag) {
|
||||
if strings.ContainsRune(isupport.state.ModeOrder, flag) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Modes in category A and B always takes an argument
|
||||
if strings.ContainsRune(isupport.chanModes[0], flag) || strings.ContainsRune(isupport.chanModes[1], flag) {
|
||||
if strings.ContainsRune(isupport.state.ChannelModes[0], flag) || strings.ContainsRune(isupport.state.ChannelModes[1], flag) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Modes in category C only takes one when added
|
||||
if plus && strings.ContainsRune(isupport.chanModes[1], flag) {
|
||||
if plus && strings.ContainsRune(isupport.state.ChannelModes[1], flag) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -251,11 +246,11 @@ func (isupport *ISupport) ChannelModeType(mode rune) int {
|
||||
|
||||
// User permission modes function exactly like the first block
|
||||
// when it comes to add/remove
|
||||
if strings.ContainsRune(isupport.modeOrder, mode) {
|
||||
if strings.ContainsRune(isupport.state.ModeOrder, mode) {
|
||||
return 0
|
||||
}
|
||||
|
||||
for i, block := range isupport.chanModes {
|
||||
for i, block := range isupport.state.ChannelModes {
|
||||
if strings.ContainsRune(block, mode) {
|
||||
return i
|
||||
}
|
||||
@@ -272,43 +267,48 @@ func (isupport *ISupport) Set(key, value string) {
|
||||
|
||||
isupport.lock.Lock()
|
||||
|
||||
if isupport.raw == nil {
|
||||
isupport.raw = make(map[string]string, 32)
|
||||
if isupport.state.Raw == nil {
|
||||
isupport.state.Raw = make(map[string]string, 32)
|
||||
}
|
||||
|
||||
isupport.raw[key] = value
|
||||
isupport.state.Raw[key] = value
|
||||
|
||||
switch key {
|
||||
case "PREFIX": // PREFIX=(ov)@+
|
||||
{
|
||||
split := strings.SplitN(value[1:], ")", 2)
|
||||
|
||||
isupport.prefixOrder = split[1]
|
||||
isupport.modeOrder = split[0]
|
||||
isupport.prefixes = make(map[rune]rune, len(split[0]))
|
||||
isupport.state.PrefixOrder = split[1]
|
||||
isupport.state.ModeOrder = split[0]
|
||||
isupport.state.Prefixes = make(map[rune]rune, len(split[0]))
|
||||
for i, ch := range split[0] {
|
||||
isupport.prefixes[rune(split[1][i])] = ch
|
||||
isupport.state.Prefixes[rune(split[1][i])] = ch
|
||||
}
|
||||
}
|
||||
case "CHANMODES": // CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz
|
||||
{
|
||||
isupport.chanModes = strings.Split(value, ",")
|
||||
isupport.state.ChannelModes = strings.Split(value, ",")
|
||||
}
|
||||
}
|
||||
|
||||
isupport.lock.Unlock()
|
||||
}
|
||||
|
||||
// State gets a copy of the isupport state.
|
||||
func (isupport *ISupport) State() *State {
|
||||
return isupport.state.Copy()
|
||||
}
|
||||
|
||||
// Reset clears everything.
|
||||
func (isupport *ISupport) Reset() {
|
||||
isupport.lock.Lock()
|
||||
isupport.prefixOrder = ""
|
||||
isupport.modeOrder = ""
|
||||
isupport.prefixes = nil
|
||||
isupport.chanModes = nil
|
||||
isupport.state.PrefixOrder = ""
|
||||
isupport.state.ModeOrder = ""
|
||||
isupport.state.Prefixes = nil
|
||||
isupport.state.ChannelModes = nil
|
||||
|
||||
for key := range isupport.raw {
|
||||
delete(isupport.raw, key)
|
||||
for key := range isupport.state.Raw {
|
||||
delete(isupport.state.Raw, key)
|
||||
}
|
||||
isupport.lock.Unlock()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package isupport_test
|
||||
|
||||
import (
|
||||
"github.com/gissleh/irc/isupport"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var isupportMessages = "FNC SAFELIST ELIST=CTU MONITOR=100 WHOX ETRACE KNOCK CHANTYPES=#& EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz CHANLIMIT=#&:15 PREFIX=(aovh)~@+% MAXLIST=bqeI:100 MODES=4 NETWORK=TestServer STATUSMSG=@+% CALLERID=g CASEMAPPING=rfc1459 NICKLEN=30 MAXNICKLEN=31 CHANNELLEN=50 TOPICLEN=390 DEAF=D TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,&acjmorsuxz| CLIENTVER=3.0"
|
||||
|
||||
var is isupport.ISupport
|
||||
|
||||
func init() {
|
||||
for _, token := range strings.Split(isupportMessages, " ") {
|
||||
pair := strings.SplitN(token, "=", 2)
|
||||
if len(pair) == 2 {
|
||||
is.Set(pair[0], pair[1])
|
||||
} else {
|
||||
is.Set(pair[0], "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestISupport_ParsePrefixedNick(t *testing.T) {
|
||||
table := []struct {
|
||||
Full string
|
||||
Prefixes string
|
||||
Modes string
|
||||
Nick string
|
||||
}{
|
||||
{"User", "", "", "User"},
|
||||
{"+User", "+", "v", "User"},
|
||||
{"@%+User", "@%+", "ohv", "User"},
|
||||
{"~User", "~", "a", "User"},
|
||||
}
|
||||
|
||||
for _, row := range table {
|
||||
t.Run(row.Full, func(t *testing.T) {
|
||||
nick, modes, prefixes := is.ParsePrefixedNick(row.Full)
|
||||
|
||||
assert.Equal(t, row.Nick, nick)
|
||||
assert.Equal(t, row.Modes, modes)
|
||||
assert.Equal(t, row.Prefixes, prefixes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestISupport_IsChannel(t *testing.T) {
|
||||
table := map[string]bool{
|
||||
"#Test": true,
|
||||
"&Test": true,
|
||||
"User": false,
|
||||
"+Stuff": false,
|
||||
"#TestAndSuch": true,
|
||||
"@astrwef": false,
|
||||
}
|
||||
|
||||
for channelName, isChannel := range table {
|
||||
t.Run(channelName, func(t *testing.T) {
|
||||
assert.Equal(t, isChannel, is.IsChannel(channelName))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestISupport_IsPermissionMode(t *testing.T) {
|
||||
table := map[rune]bool{
|
||||
'#': false,
|
||||
'+': false,
|
||||
'o': true,
|
||||
'v': true,
|
||||
'h': true,
|
||||
'a': true,
|
||||
'g': false,
|
||||
'p': false,
|
||||
}
|
||||
|
||||
for flag, expected := range table {
|
||||
t.Run(string(flag), func(t *testing.T) {
|
||||
assert.Equal(t, expected, is.IsPermissionMode(flag))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package isupport
|
||||
|
||||
type State struct {
|
||||
Raw map[string]string `json:"raw"`
|
||||
Prefixes map[rune]rune `json:"-"`
|
||||
ModeOrder string `json:"modeOrder"`
|
||||
PrefixOrder string `json:"prefixOrder"`
|
||||
ChannelModes []string `json:"channelModes"`
|
||||
}
|
||||
|
||||
func (state *State) Copy() *State {
|
||||
stateCopy := *state
|
||||
stateCopy.Raw = make(map[string]string, len(state.Raw))
|
||||
for key, value := range state.Raw {
|
||||
stateCopy.Raw[key] = value
|
||||
}
|
||||
|
||||
return &stateCopy
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.aiterp.net/gisle/irc/isupport"
|
||||
"github.com/gissleh/irc/isupport"
|
||||
)
|
||||
|
||||
// The List of users in a channel. It has all operations one would perform on
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.aiterp.net/gisle/irc/isupport"
|
||||
"git.aiterp.net/gisle/irc/list"
|
||||
"github.com/gissleh/irc/isupport"
|
||||
"github.com/gissleh/irc/list"
|
||||
)
|
||||
|
||||
var testISupport isupport.ISupport
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"git.aiterp.net/gisle/irc/list"
|
||||
"github.com/gissleh/irc/list"
|
||||
)
|
||||
|
||||
// A Query is a target for direct messages to and from a specific nick.
|
||||
@@ -19,7 +19,15 @@ func (query *Query) Name() string {
|
||||
return query.user.Nick
|
||||
}
|
||||
|
||||
// Handle handles messages routed to this channel by the client's event loop
|
||||
func (query *Query) State() TargetState {
|
||||
return TargetState{
|
||||
Kind: "query",
|
||||
Name: query.user.Nick,
|
||||
Users: []list.User{query.user},
|
||||
}
|
||||
}
|
||||
|
||||
// AddHandler handles messages routed to this channel by the client's event loop
|
||||
func (query *Query) Handle(event *Event, client *Client) {
|
||||
switch event.Name() {
|
||||
case "packet.nick":
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"github.com/gissleh/irc/isupport"
|
||||
"github.com/gissleh/irc/list"
|
||||
)
|
||||
|
||||
type ClientState struct {
|
||||
Nick string `json:"nick"`
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Connected bool `json:"connected"`
|
||||
Ready bool `json:"quit"`
|
||||
ISupport *isupport.State `json:"isupport"`
|
||||
Caps []string `json:"caps"`
|
||||
Targets []TargetState `json:"targets"`
|
||||
}
|
||||
|
||||
type TargetState struct {
|
||||
ID string `json:"id"`
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Users []list.User `json:"users,omitempty"`
|
||||
}
|
||||
@@ -14,7 +14,15 @@ func (status *Status) Name() string {
|
||||
return "Status"
|
||||
}
|
||||
|
||||
// Handle handles messages routed to this status by the client's event loop
|
||||
func (status *Status) State() TargetState {
|
||||
return TargetState{
|
||||
Kind: "status",
|
||||
Name: "Status",
|
||||
Users: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// AddHandler handles messages routed to this status by the client's event loop
|
||||
func (status *Status) Handle(event *Event, client *Client) {
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user