Handle kick, rename server to network
This commit is contained in:
parent
a33157ff84
commit
6985dd16da
65 changed files with 2650 additions and 2179 deletions
|
@ -23,7 +23,7 @@ type dispatchVersion struct {
|
|||
|
||||
type indexData struct {
|
||||
Defaults connectDefaults
|
||||
Servers []Server
|
||||
Networks []*storage.Network
|
||||
Channels []*storage.Channel
|
||||
OpenDMs []storage.Tab
|
||||
HexIP bool
|
||||
|
@ -42,8 +42,11 @@ func (d *Dispatch) getIndexData(r *http.Request, state *State) *indexData {
|
|||
cfg := d.Config()
|
||||
|
||||
data := indexData{
|
||||
Defaults: connectDefaults{Defaults: &cfg.Defaults},
|
||||
HexIP: cfg.HexIP,
|
||||
Defaults: connectDefaults{
|
||||
Defaults: &cfg.Defaults,
|
||||
ServerPassword: cfg.Defaults.ServerPassword != "",
|
||||
},
|
||||
HexIP: cfg.HexIP,
|
||||
Version: dispatchVersion{
|
||||
Tag: version.Tag,
|
||||
Commit: version.Commit,
|
||||
|
@ -51,77 +54,53 @@ func (d *Dispatch) getIndexData(r *http.Request, state *State) *indexData {
|
|||
},
|
||||
}
|
||||
|
||||
data.Defaults.ServerPassword = cfg.Defaults.ServerPassword != ""
|
||||
|
||||
if state == nil {
|
||||
data.Settings = storage.DefaultClientSettings()
|
||||
return &data
|
||||
}
|
||||
|
||||
data.Settings = state.user.GetClientSettings()
|
||||
data.Settings = state.user.ClientSettings()
|
||||
|
||||
servers, err := state.user.GetServers()
|
||||
if err != nil {
|
||||
return nil
|
||||
state.lock.Lock()
|
||||
for _, network := range state.networks {
|
||||
network = network.Copy()
|
||||
network.Password = ""
|
||||
network.Username = ""
|
||||
network.Realname = ""
|
||||
|
||||
data.Networks = append(data.Networks, network)
|
||||
data.Channels = append(data.Channels, network.Channels()...)
|
||||
}
|
||||
connections := state.getConnectionStates()
|
||||
for _, server := range servers {
|
||||
server.Password = ""
|
||||
server.Username = ""
|
||||
server.Realname = ""
|
||||
state.lock.Unlock()
|
||||
|
||||
s := Server{
|
||||
Server: server,
|
||||
Status: newConnectionUpdate(server.Host, connections[server.Host]),
|
||||
}
|
||||
|
||||
if i, ok := state.irc[server.Host]; ok {
|
||||
s.Features = i.Features.Map()
|
||||
}
|
||||
|
||||
data.Servers = append(data.Servers, s)
|
||||
openDMs, err := state.user.OpenDMs()
|
||||
if err == nil {
|
||||
data.OpenDMs = openDMs
|
||||
}
|
||||
|
||||
channels, err := state.user.GetChannels()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for i, channel := range channels {
|
||||
if client, ok := state.getIRC(channel.Server); ok {
|
||||
channels[i].Topic = client.ChannelTopic(channel.Name)
|
||||
}
|
||||
}
|
||||
data.Channels = channels
|
||||
|
||||
openDMs, err := state.user.GetOpenDMs()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
data.OpenDMs = openDMs
|
||||
|
||||
tab, err := tabFromRequest(r)
|
||||
if err == nil && hasTab(channels, openDMs, tab.Server, tab.Name) {
|
||||
data.addUsersAndMessages(tab.Server, tab.Name, state)
|
||||
if err == nil && hasTab(data.Channels, openDMs, tab.Network, tab.Name) {
|
||||
data.addUsersAndMessages(tab.Network, tab.Name, state)
|
||||
}
|
||||
|
||||
return &data
|
||||
}
|
||||
|
||||
func (d *indexData) addUsersAndMessages(server, name string, state *State) {
|
||||
if i, ok := state.getIRC(server); ok && isChannel(name) {
|
||||
func (d *indexData) addUsersAndMessages(network, name string, state *State) {
|
||||
if i, ok := state.client(network); ok && isChannel(name) {
|
||||
if users := i.ChannelUsers(name); len(users) > 0 {
|
||||
d.Users = &Userlist{
|
||||
Server: server,
|
||||
Network: network,
|
||||
Channel: name,
|
||||
Users: users,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messages, hasMore, err := state.user.GetLastMessages(server, name, 50)
|
||||
messages, hasMore, err := state.user.LastMessages(network, name, 50)
|
||||
if err == nil && len(messages) > 0 {
|
||||
m := Messages{
|
||||
Server: server,
|
||||
Network: network,
|
||||
To: name,
|
||||
Messages: messages,
|
||||
}
|
||||
|
@ -134,16 +113,16 @@ func (d *indexData) addUsersAndMessages(server, name string, state *State) {
|
|||
}
|
||||
}
|
||||
|
||||
func hasTab(channels []*storage.Channel, openDMs []storage.Tab, server, name string) bool {
|
||||
func hasTab(channels []*storage.Channel, openDMs []storage.Tab, network, name string) bool {
|
||||
if name != "" {
|
||||
for _, ch := range channels {
|
||||
if server == ch.Server && name == ch.Name {
|
||||
if network == ch.Network && name == ch.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, tab := range openDMs {
|
||||
if server == tab.Server && name == tab.Name {
|
||||
if network == tab.Network && name == tab.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +158,7 @@ func tabFromRequest(r *http.Request) (Tab, error) {
|
|||
|
||||
parts := strings.SplitN(v, ";", 2)
|
||||
if len(parts) == 2 {
|
||||
tab.Server = parts[0]
|
||||
tab.Network = parts[0]
|
||||
tab.Name = parts[1]
|
||||
}
|
||||
} else {
|
||||
|
@ -194,7 +173,7 @@ func tabFromRequest(r *http.Request) (Tab, error) {
|
|||
tab.Name = name
|
||||
}
|
||||
|
||||
tab.Server = parts[0]
|
||||
tab.Network = parts[0]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,27 +42,35 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer(in *jlexer.Lexer, out
|
|||
if data := in.Raw(); in.Ok() {
|
||||
in.AddError((out.Defaults).UnmarshalJSON(data))
|
||||
}
|
||||
case "servers":
|
||||
case "networks":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
out.Servers = nil
|
||||
out.Networks = nil
|
||||
} else {
|
||||
in.Delim('[')
|
||||
if out.Servers == nil {
|
||||
if out.Networks == nil {
|
||||
if !in.IsDelim(']') {
|
||||
out.Servers = make([]Server, 0, 0)
|
||||
out.Networks = make([]*storage.Network, 0, 8)
|
||||
} else {
|
||||
out.Servers = []Server{}
|
||||
out.Networks = []*storage.Network{}
|
||||
}
|
||||
} else {
|
||||
out.Servers = (out.Servers)[:0]
|
||||
out.Networks = (out.Networks)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v1 Server
|
||||
if data := in.Raw(); in.Ok() {
|
||||
in.AddError((v1).UnmarshalJSON(data))
|
||||
var v1 *storage.Network
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
v1 = nil
|
||||
} else {
|
||||
if v1 == nil {
|
||||
v1 = new(storage.Network)
|
||||
}
|
||||
if data := in.Raw(); in.Ok() {
|
||||
in.AddError((*v1).UnmarshalJSON(data))
|
||||
}
|
||||
}
|
||||
out.Servers = append(out.Servers, v1)
|
||||
out.Networks = append(out.Networks, v1)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
|
@ -91,7 +99,9 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer(in *jlexer.Lexer, out
|
|||
if v2 == nil {
|
||||
v2 = new(storage.Channel)
|
||||
}
|
||||
easyjson7e607aefDecodeGithubComKhliengDispatchStorage(in, v2)
|
||||
if data := in.Raw(); in.Ok() {
|
||||
in.AddError((*v2).UnmarshalJSON(data))
|
||||
}
|
||||
}
|
||||
out.Channels = append(out.Channels, v2)
|
||||
in.WantComma()
|
||||
|
@ -115,7 +125,7 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer(in *jlexer.Lexer, out
|
|||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v3 storage.Tab
|
||||
easyjson7e607aefDecodeGithubComKhliengDispatchStorage1(in, &v3)
|
||||
easyjson7e607aefDecodeGithubComKhliengDispatchStorage(in, &v3)
|
||||
out.OpenDMs = append(out.OpenDMs, v3)
|
||||
in.WantComma()
|
||||
}
|
||||
|
@ -183,8 +193,8 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
|
|||
out.RawString(prefix[1:])
|
||||
out.Raw((in.Defaults).MarshalJSON())
|
||||
}
|
||||
if len(in.Servers) != 0 {
|
||||
const prefix string = ",\"servers\":"
|
||||
if len(in.Networks) != 0 {
|
||||
const prefix string = ",\"networks\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
|
@ -193,11 +203,15 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
|
|||
}
|
||||
{
|
||||
out.RawByte('[')
|
||||
for v4, v5 := range in.Servers {
|
||||
for v4, v5 := range in.Networks {
|
||||
if v4 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.Raw((v5).MarshalJSON())
|
||||
if v5 == nil {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
out.Raw((*v5).MarshalJSON())
|
||||
}
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
|
@ -219,7 +233,7 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
|
|||
if v7 == nil {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out, *v7)
|
||||
out.Raw((*v7).MarshalJSON())
|
||||
}
|
||||
}
|
||||
out.RawByte(']')
|
||||
|
@ -239,7 +253,7 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
|
|||
if v8 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
easyjson7e607aefEncodeGithubComKhliengDispatchStorage1(out, v9)
|
||||
easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out, v9)
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
|
@ -320,7 +334,7 @@ func (v *indexData) UnmarshalJSON(data []byte) error {
|
|||
func (v *indexData) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson7e607aefDecodeGithubComKhliengDispatchServer(l, v)
|
||||
}
|
||||
func easyjson7e607aefDecodeGithubComKhliengDispatchStorage1(in *jlexer.Lexer, out *storage.Tab) {
|
||||
func easyjson7e607aefDecodeGithubComKhliengDispatchStorage(in *jlexer.Lexer, out *storage.Tab) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
|
@ -339,8 +353,8 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchStorage1(in *jlexer.Lexer, ou
|
|||
continue
|
||||
}
|
||||
switch key {
|
||||
case "server":
|
||||
out.Server = string(in.String())
|
||||
case "network":
|
||||
out.Network = string(in.String())
|
||||
case "name":
|
||||
out.Name = string(in.String())
|
||||
default:
|
||||
|
@ -353,15 +367,15 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchStorage1(in *jlexer.Lexer, ou
|
|||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson7e607aefEncodeGithubComKhliengDispatchStorage1(out *jwriter.Writer, in storage.Tab) {
|
||||
func easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out *jwriter.Writer, in storage.Tab) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
if in.Server != "" {
|
||||
const prefix string = ",\"server\":"
|
||||
if in.Network != "" {
|
||||
const prefix string = ",\"network\":"
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
out.String(string(in.Server))
|
||||
out.String(string(in.Network))
|
||||
}
|
||||
if in.Name != "" {
|
||||
const prefix string = ",\"name\":"
|
||||
|
@ -375,73 +389,6 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchStorage1(out *jwriter.Writer,
|
|||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
func easyjson7e607aefDecodeGithubComKhliengDispatchStorage(in *jlexer.Lexer, out *storage.Channel) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "server":
|
||||
out.Server = string(in.String())
|
||||
case "name":
|
||||
out.Name = string(in.String())
|
||||
case "topic":
|
||||
out.Topic = string(in.String())
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out *jwriter.Writer, in storage.Channel) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
if in.Server != "" {
|
||||
const prefix string = ",\"server\":"
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
out.String(string(in.Server))
|
||||
}
|
||||
if in.Name != "" {
|
||||
const prefix string = ",\"name\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
out.String(string(in.Name))
|
||||
}
|
||||
if in.Topic != "" {
|
||||
const prefix string = ",\"topic\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
out.String(string(in.Topic))
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
func easyjson7e607aefDecodeGithubComKhliengDispatchServer1(in *jlexer.Lexer, out *dispatchVersion) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
|
|
|
@ -19,25 +19,25 @@ func TestGetTabFromPath(t *testing.T) {
|
|||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23r%2Fstuff%2F"}},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#r/stuff/"}},
|
||||
Tab{storage.Tab{Network: "chat.freenode.net", Name: "#r/stuff/"}},
|
||||
}, {
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23r%2Fstuff"}},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#r/stuff"}},
|
||||
Tab{storage.Tab{Network: "chat.freenode.net", Name: "#r/stuff"}},
|
||||
}, {
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23stuff"}},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#stuff"}},
|
||||
Tab{storage.Tab{Network: "chat.freenode.net", Name: "#stuff"}},
|
||||
}, {
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/chat.freenode.net/stuff"}},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net", Name: "stuff"}},
|
||||
Tab{storage.Tab{Network: "chat.freenode.net", Name: "stuff"}},
|
||||
}, {
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
|
@ -48,7 +48,7 @@ func TestGetTabFromPath(t *testing.T) {
|
|||
&http.Request{
|
||||
URL: &url.URL{Path: "/ws/chat.freenode.net"},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net"}},
|
||||
Tab{storage.Tab{Network: "chat.freenode.net"}},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/khlieng/dispatch/pkg/irc"
|
||||
"github.com/khlieng/dispatch/storage"
|
||||
"github.com/khlieng/dispatch/version"
|
||||
)
|
||||
|
||||
func createNickInUseHandler(i *irc.Client, state *State) func(string) string {
|
||||
|
@ -16,12 +15,12 @@ func createNickInUseHandler(i *irc.Client, state *State) func(string) string {
|
|||
|
||||
if newNick == i.GetNick() {
|
||||
state.sendJSON("nick_fail", NickFail{
|
||||
Server: i.Host(),
|
||||
Network: i.Host(),
|
||||
})
|
||||
}
|
||||
|
||||
state.sendJSON("error", IRCError{
|
||||
Server: i.Host(),
|
||||
Network: i.Host(),
|
||||
Message: fmt.Sprintf("Nickname %s is unavailable, trying %s instead", nick, newNick),
|
||||
})
|
||||
|
||||
|
@ -29,23 +28,11 @@ func createNickInUseHandler(i *irc.Client, state *State) func(string) string {
|
|||
}
|
||||
}
|
||||
|
||||
func connectIRC(server *storage.Server, state *State, srcIP []byte) *irc.Client {
|
||||
func connectIRC(network *storage.Network, state *State, srcIP []byte) *irc.Client {
|
||||
cfg := state.srv.Config()
|
||||
ircCfg := network.IRCConfig()
|
||||
|
||||
ircCfg := irc.Config{
|
||||
Host: server.Host,
|
||||
Port: server.Port,
|
||||
TLS: server.TLS,
|
||||
Nick: server.Nick,
|
||||
Username: server.Username,
|
||||
Realname: server.Realname,
|
||||
Account: server.Account,
|
||||
Password: server.Password,
|
||||
Version: fmt.Sprintf("Dispatch %s (git: %s)", version.Tag, version.Commit),
|
||||
Source: "https://github.com/khlieng/dispatch",
|
||||
}
|
||||
|
||||
if server.TLS {
|
||||
if ircCfg.TLS {
|
||||
ircCfg.TLSConfig = &tls.Config{
|
||||
InsecureSkipVerify: !cfg.VerifyCertificates,
|
||||
}
|
||||
|
@ -59,18 +46,16 @@ func connectIRC(server *storage.Server, state *State, srcIP []byte) *irc.Client
|
|||
ircCfg.Username = hex.EncodeToString(srcIP)
|
||||
}
|
||||
|
||||
if server.ServerPassword == "" &&
|
||||
if ircCfg.ServerPassword == "" &&
|
||||
cfg.Defaults.ServerPassword != "" &&
|
||||
server.Host == cfg.Defaults.Host {
|
||||
ircCfg.Host == cfg.Defaults.Host {
|
||||
ircCfg.ServerPassword = cfg.Defaults.ServerPassword
|
||||
} else {
|
||||
ircCfg.ServerPassword = server.ServerPassword
|
||||
}
|
||||
|
||||
i := irc.NewClient(&ircCfg)
|
||||
i := irc.NewClient(ircCfg)
|
||||
i.Config.HandleNickInUse = createNickInUseHandler(i, state)
|
||||
|
||||
state.setIRC(server.Host, i)
|
||||
state.setNetwork(network.Host, state.user.NewNetwork(network, i))
|
||||
i.Connect()
|
||||
go newIRCHandler(i, state).run()
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ func (i *ircHandler) run() {
|
|||
select {
|
||||
case msg, ok := <-i.client.Messages:
|
||||
if !ok {
|
||||
i.state.deleteIRC(i.client.Host())
|
||||
i.state.deleteNetwork(i.client.Host())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,14 @@ func (i *ircHandler) run() {
|
|||
|
||||
case state := <-i.client.ConnectionChanged:
|
||||
i.state.sendJSON("connection_update", newConnectionUpdate(i.client.Host(), state))
|
||||
i.state.setConnectionState(i.client.Host(), state)
|
||||
|
||||
if network, ok := i.state.network(i.client.Host()); ok {
|
||||
var err string
|
||||
if state.Error != nil {
|
||||
err = state.Error.Error()
|
||||
}
|
||||
network.SetStatus(state.Connected, err)
|
||||
}
|
||||
|
||||
if state.Error != nil && (lastConnErr == nil ||
|
||||
state.Error.Error() != lastConnErr.Error()) {
|
||||
|
@ -87,7 +94,7 @@ func (i *ircHandler) run() {
|
|||
func (i *ircHandler) dispatchMessage(msg *irc.Message) {
|
||||
if msg.Command[0] == '4' && !isExcludedError(msg.Command) {
|
||||
err := IRCError{
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
Message: msg.LastParam(),
|
||||
}
|
||||
|
||||
|
@ -109,25 +116,30 @@ func (i *ircHandler) dispatchMessage(msg *irc.Message) {
|
|||
}
|
||||
|
||||
func (i *ircHandler) nick(msg *irc.Message) {
|
||||
i.state.sendJSON("nick", Nick{
|
||||
Server: i.client.Host(),
|
||||
Old: msg.Sender,
|
||||
New: msg.LastParam(),
|
||||
})
|
||||
nick := Nick{
|
||||
Network: i.client.Host(),
|
||||
Old: msg.Sender,
|
||||
New: msg.LastParam(),
|
||||
}
|
||||
|
||||
if i.client.Is(msg.LastParam()) {
|
||||
go i.state.user.SetNick(msg.LastParam(), i.client.Host())
|
||||
i.state.sendJSON("nick", nick)
|
||||
|
||||
if i.client.Is(nick.New) {
|
||||
if network, ok := i.state.network(nick.Network); ok {
|
||||
network.SetNick(nick.New)
|
||||
go network.Save()
|
||||
}
|
||||
}
|
||||
|
||||
channels := irc.GetNickChannels(msg)
|
||||
go i.state.user.LogEvent(i.client.Host(), "nick", []string{msg.Sender, msg.LastParam()}, channels...)
|
||||
go i.state.user.LogEvent(nick.Network, "nick", []string{nick.Old, nick.New}, channels...)
|
||||
}
|
||||
|
||||
func (i *ircHandler) join(msg *irc.Message) {
|
||||
host := i.client.Host()
|
||||
|
||||
i.state.sendJSON("join", Join{
|
||||
Server: host,
|
||||
Network: host,
|
||||
User: msg.Sender,
|
||||
Channels: msg.Params,
|
||||
})
|
||||
|
@ -138,37 +150,74 @@ func (i *ircHandler) join(msg *irc.Message) {
|
|||
// In case no topic is set and there's a cached one that needs to be cleared
|
||||
i.client.Topic(channel)
|
||||
|
||||
i.state.sendLastMessages(host, channel, 50)
|
||||
if network, ok := i.state.network(host); ok {
|
||||
if ch := network.Channel(channel); ch != nil {
|
||||
ch.SetJoined(true)
|
||||
} else {
|
||||
i.state.sendLastMessages(host, channel, 50)
|
||||
|
||||
go i.state.user.AddChannel(&storage.Channel{
|
||||
Server: host,
|
||||
Name: channel,
|
||||
})
|
||||
ch = network.NewChannel(channel)
|
||||
ch.SetJoined(true)
|
||||
network.AddChannel(ch)
|
||||
go ch.Save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go i.state.user.LogEvent(host, "join", []string{msg.Sender}, channel)
|
||||
}
|
||||
|
||||
func (i *ircHandler) part(msg *irc.Message) {
|
||||
host := i.client.Host()
|
||||
channel := msg.Params[0]
|
||||
part := Part{
|
||||
Server: host,
|
||||
Network: i.client.Host(),
|
||||
User: msg.Sender,
|
||||
Channel: channel,
|
||||
Channel: msg.Params[0],
|
||||
}
|
||||
|
||||
params := []string{part.User}
|
||||
|
||||
if len(msg.Params) == 2 {
|
||||
part.Reason = msg.Params[1]
|
||||
params = append(params, part.Reason)
|
||||
}
|
||||
|
||||
i.state.sendJSON("part", part)
|
||||
|
||||
if i.client.Is(msg.Sender) {
|
||||
go i.state.user.RemoveChannel(host, part.Channel)
|
||||
go i.state.user.RemoveChannel(part.Network, part.Channel)
|
||||
}
|
||||
|
||||
go i.state.user.LogEvent(host, "part", []string{msg.Sender}, channel)
|
||||
go i.state.user.LogEvent(part.Network, "part", params, part.Channel)
|
||||
}
|
||||
|
||||
func (i *ircHandler) kick(msg *irc.Message) {
|
||||
if len(msg.Params) < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
kick := Kick{
|
||||
Network: i.client.Host(),
|
||||
Channel: msg.Params[0],
|
||||
Sender: msg.Sender,
|
||||
User: msg.Params[1],
|
||||
}
|
||||
|
||||
params := []string{kick.User, kick.Sender}
|
||||
|
||||
if len(msg.Params) > 2 {
|
||||
kick.Reason = msg.Params[2]
|
||||
params = append(params, kick.Reason)
|
||||
}
|
||||
|
||||
i.state.sendJSON("kick", kick)
|
||||
|
||||
go i.state.user.LogEvent(kick.Network, "kick", params, kick.Channel)
|
||||
|
||||
if i.client.Is(kick.User) {
|
||||
if network, ok := i.state.network(kick.Network); ok {
|
||||
network.Channel(kick.Channel).SetJoined(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ircHandler) mode(msg *irc.Message) {
|
||||
|
@ -193,7 +242,7 @@ func (i *ircHandler) message(msg *irc.Message) {
|
|||
|
||||
message := Message{
|
||||
ID: betterguid.New(),
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
From: msg.Sender,
|
||||
Content: msg.LastParam(),
|
||||
}
|
||||
|
@ -215,7 +264,7 @@ func (i *ircHandler) message(msg *irc.Message) {
|
|||
if target != "*" && !msg.IsFromServer() {
|
||||
go i.state.user.LogMessage(&storage.Message{
|
||||
ID: message.ID,
|
||||
Server: message.Server,
|
||||
Network: message.Network,
|
||||
From: message.From,
|
||||
To: target,
|
||||
Content: message.Content,
|
||||
|
@ -225,9 +274,9 @@ func (i *ircHandler) message(msg *irc.Message) {
|
|||
|
||||
func (i *ircHandler) quit(msg *irc.Message) {
|
||||
i.state.sendJSON("quit", Quit{
|
||||
Server: i.client.Host(),
|
||||
User: msg.Sender,
|
||||
Reason: msg.LastParam(),
|
||||
Network: i.client.Host(),
|
||||
User: msg.Sender,
|
||||
Reason: msg.LastParam(),
|
||||
})
|
||||
|
||||
channels := irc.GetQuitChannels(msg)
|
||||
|
@ -238,8 +287,8 @@ func (i *ircHandler) quit(msg *irc.Message) {
|
|||
func (i *ircHandler) info(msg *irc.Message) {
|
||||
if msg.Command == irc.RPL_WELCOME {
|
||||
i.state.sendJSON("nick", Nick{
|
||||
Server: i.client.Host(),
|
||||
New: msg.Params[0],
|
||||
Network: i.client.Host(),
|
||||
New: msg.Params[0],
|
||||
})
|
||||
|
||||
_, needsUpdate := channelIndexes.Get(i.client.Host())
|
||||
|
@ -252,25 +301,27 @@ func (i *ircHandler) info(msg *irc.Message) {
|
|||
}
|
||||
|
||||
i.state.sendJSON("pm", Message{
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
From: msg.Sender,
|
||||
Content: strings.Join(msg.Params[1:], " "),
|
||||
})
|
||||
}
|
||||
|
||||
func (i *ircHandler) features(msg *irc.Message) {
|
||||
features := i.client.Features.Map()
|
||||
|
||||
i.state.sendJSON("features", Features{
|
||||
Server: i.client.Host(),
|
||||
Features: i.client.Features.Map(),
|
||||
Network: i.client.Host(),
|
||||
Features: features,
|
||||
})
|
||||
|
||||
if name := i.client.Features.String("NETWORK"); name != "" {
|
||||
go func() {
|
||||
server, err := i.state.user.GetServer(i.client.Host())
|
||||
if err == nil && server.Name == "" {
|
||||
i.state.user.SetServerName(name, server.Host)
|
||||
}
|
||||
}()
|
||||
if network, ok := i.state.network(i.client.Host()); ok {
|
||||
network.SetFeatures(features)
|
||||
|
||||
if name := i.client.Features.String("NETWORK"); name != "" {
|
||||
network.SetName(name)
|
||||
go network.Save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,30 +361,41 @@ func (i *ircHandler) topic(msg *irc.Message) {
|
|||
}
|
||||
|
||||
i.state.sendJSON("topic", Topic{
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
Channel: channel,
|
||||
Topic: msg.LastParam(),
|
||||
Nick: nick,
|
||||
})
|
||||
|
||||
if network, ok := i.state.network(i.client.Host()); ok {
|
||||
network.Channel(channel).SetTopic(msg.LastParam())
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ircHandler) noTopic(msg *irc.Message) {
|
||||
channel := msg.Params[1]
|
||||
|
||||
i.state.sendJSON("topic", Topic{
|
||||
Server: i.client.Host(),
|
||||
Channel: msg.Params[1],
|
||||
Network: i.client.Host(),
|
||||
Channel: channel,
|
||||
})
|
||||
|
||||
if network, ok := i.state.network(i.client.Host()); ok {
|
||||
network.Channel(channel).SetTopic("")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (i *ircHandler) namesEnd(msg *irc.Message) {
|
||||
i.state.sendJSON("users", Userlist{
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
Channel: msg.Params[1],
|
||||
Users: irc.GetNamreplyUsers(msg),
|
||||
})
|
||||
}
|
||||
|
||||
func (i *ircHandler) motdStart(msg *irc.Message) {
|
||||
i.motdBuffer.Server = i.client.Host()
|
||||
i.motdBuffer.Network = i.client.Host()
|
||||
i.motdBuffer.Title = msg.LastParam()
|
||||
}
|
||||
|
||||
|
@ -376,23 +438,23 @@ func (i *ircHandler) listEnd(msg *irc.Message) {
|
|||
|
||||
func (i *ircHandler) badNick(msg *irc.Message) {
|
||||
i.state.sendJSON("nick_fail", NickFail{
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
})
|
||||
}
|
||||
|
||||
func (i *ircHandler) forward(msg *irc.Message) {
|
||||
if len(msg.Params) > 2 {
|
||||
i.state.sendJSON("channel_forward", ChannelForward{
|
||||
Server: i.client.Host(),
|
||||
Old: msg.Params[1],
|
||||
New: msg.Params[2],
|
||||
Network: i.client.Host(),
|
||||
Old: msg.Params[1],
|
||||
New: msg.Params[2],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ircHandler) error(msg *irc.Message) {
|
||||
i.state.sendJSON("error", IRCError{
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
Message: msg.LastParam(),
|
||||
})
|
||||
}
|
||||
|
@ -413,7 +475,7 @@ func (i *ircHandler) receiveDCCSend(pack *irc.DCCSend, msg *irc.Message) {
|
|||
i.state.setPendingDCC(pack.File, pack)
|
||||
|
||||
i.state.sendJSON("dcc_send", DCCSend{
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
From: msg.Sender,
|
||||
Filename: pack.File,
|
||||
URL: fmt.Sprintf("%s://%s/downloads/%s/%s",
|
||||
|
@ -431,6 +493,7 @@ func (i *ircHandler) initHandlers() {
|
|||
irc.NICK: i.nick,
|
||||
irc.JOIN: i.join,
|
||||
irc.PART: i.part,
|
||||
irc.KICK: i.kick,
|
||||
irc.MODE: i.mode,
|
||||
irc.PRIVMSG: i.message,
|
||||
irc.NOTICE: i.message,
|
||||
|
@ -469,16 +532,16 @@ func (i *ircHandler) log(v ...interface{}) {
|
|||
|
||||
func (i *ircHandler) sendDCCInfo(message string, log bool, a ...interface{}) {
|
||||
msg := Message{
|
||||
Server: i.client.Host(),
|
||||
Network: i.client.Host(),
|
||||
From: "@dcc",
|
||||
Content: fmt.Sprintf(message, a...),
|
||||
}
|
||||
i.state.sendJSON("pm", msg)
|
||||
|
||||
if log {
|
||||
i.state.user.AddOpenDM(msg.Server, msg.From)
|
||||
i.state.user.AddOpenDM(msg.Network, msg.From)
|
||||
i.state.user.LogMessage(&storage.Message{
|
||||
Server: msg.Server,
|
||||
Network: msg.Network,
|
||||
From: msg.From,
|
||||
Content: msg.Content,
|
||||
})
|
||||
|
|
|
@ -76,9 +76,9 @@ func TestHandleIRCNick(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "nick", Nick{
|
||||
Server: "host.com",
|
||||
Old: "old",
|
||||
New: "new",
|
||||
Network: "host.com",
|
||||
Old: "old",
|
||||
New: "new",
|
||||
}, res)
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ func TestHandleIRCJoin(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "join", Join{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
User: "joining",
|
||||
Channels: []string{"#chan"},
|
||||
}, res)
|
||||
|
@ -104,7 +104,7 @@ func TestHandleIRCPart(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "part", Part{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
User: "parting",
|
||||
Channel: "#chan",
|
||||
Reason: "the reason",
|
||||
|
@ -117,7 +117,7 @@ func TestHandleIRCPart(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "part", Part{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
User: "parting",
|
||||
Channel: "#chan",
|
||||
}, res)
|
||||
|
@ -159,9 +159,9 @@ func TestHandleIRCQuit(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "quit", Quit{
|
||||
Server: "host.com",
|
||||
User: "nick",
|
||||
Reason: "the reason",
|
||||
Network: "host.com",
|
||||
User: "nick",
|
||||
Reason: "the reason",
|
||||
}, res)
|
||||
}
|
||||
|
||||
|
@ -173,12 +173,12 @@ func TestHandleIRCWelcome(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "nick", Nick{
|
||||
Server: "host.com",
|
||||
New: "nick",
|
||||
Network: "host.com",
|
||||
New: "nick",
|
||||
}, <-res)
|
||||
|
||||
checkResponse(t, "pm", Message{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
From: "nick",
|
||||
Content: "some text",
|
||||
}, <-res)
|
||||
|
@ -224,7 +224,7 @@ func TestHandleIRCTopic(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "topic", Topic{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
Channel: "#chan",
|
||||
Topic: "the topic",
|
||||
}, res)
|
||||
|
@ -236,7 +236,7 @@ func TestHandleIRCTopic(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "topic", Topic{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
Channel: "#chan",
|
||||
Topic: "the topic",
|
||||
Nick: "bob",
|
||||
|
@ -250,7 +250,7 @@ func TestHandleIRCNoTopic(t *testing.T) {
|
|||
})
|
||||
|
||||
checkResponse(t, "topic", Topic{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
Channel: "#chan",
|
||||
}, res)
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ func TestHandleIRCMotd(t *testing.T) {
|
|||
i.dispatchMessage(&irc.Message{Command: irc.RPL_ENDOFMOTD})
|
||||
|
||||
checkResponse(t, "motd", MOTD{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
Title: "motd title",
|
||||
Content: []string{"line 1", "line 2"},
|
||||
}, <-s.broadcast)
|
||||
|
@ -302,6 +302,6 @@ func TestHandleIRCBadNick(t *testing.T) {
|
|||
<-s.broadcast
|
||||
|
||||
checkResponse(t, "nick_fail", NickFail{
|
||||
Server: "host.com",
|
||||
Network: "host.com",
|
||||
}, <-s.broadcast)
|
||||
}
|
||||
|
|
|
@ -19,37 +19,31 @@ type WSResponse struct {
|
|||
Data interface{}
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
*storage.Server
|
||||
Status ConnectionUpdate
|
||||
Features map[string]interface{}
|
||||
}
|
||||
|
||||
type Features struct {
|
||||
Server string
|
||||
Network string
|
||||
Features map[string]interface{}
|
||||
}
|
||||
|
||||
type ServerName struct {
|
||||
Server string
|
||||
Name string
|
||||
type NetworkName struct {
|
||||
Network string
|
||||
Name string
|
||||
}
|
||||
|
||||
type ReconnectSettings struct {
|
||||
Server string
|
||||
Network string
|
||||
SkipVerify bool
|
||||
}
|
||||
|
||||
type ConnectionUpdate struct {
|
||||
Server string
|
||||
Network string
|
||||
Connected bool
|
||||
Error string
|
||||
ErrorType string
|
||||
}
|
||||
|
||||
func newConnectionUpdate(server string, state irc.ConnectionState) ConnectionUpdate {
|
||||
func newConnectionUpdate(network string, state irc.ConnectionState) ConnectionUpdate {
|
||||
status := ConnectionUpdate{
|
||||
Server: server,
|
||||
Network: network,
|
||||
Connected: state.Connected,
|
||||
}
|
||||
if state.Error != nil {
|
||||
|
@ -62,23 +56,23 @@ func newConnectionUpdate(server string, state irc.ConnectionState) ConnectionUpd
|
|||
}
|
||||
|
||||
type Nick struct {
|
||||
Server string
|
||||
Old string `json:"oldNick,omitempty"`
|
||||
New string `json:"newNick,omitempty"`
|
||||
Network string
|
||||
Old string `json:"oldNick,omitempty"`
|
||||
New string `json:"newNick,omitempty"`
|
||||
}
|
||||
|
||||
type NickFail struct {
|
||||
Server string
|
||||
Network string
|
||||
}
|
||||
|
||||
type Join struct {
|
||||
Server string
|
||||
Network string
|
||||
User string
|
||||
Channels []string
|
||||
}
|
||||
|
||||
type Part struct {
|
||||
Server string
|
||||
Network string
|
||||
User string
|
||||
Channel string
|
||||
Channels []string
|
||||
|
@ -90,14 +84,14 @@ type Mode struct {
|
|||
}
|
||||
|
||||
type Quit struct {
|
||||
Server string
|
||||
User string
|
||||
Reason string
|
||||
Network string
|
||||
User string
|
||||
Reason string
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
ID string
|
||||
Server string
|
||||
Network string
|
||||
From string
|
||||
To string
|
||||
Content string
|
||||
|
@ -105,7 +99,7 @@ type Message struct {
|
|||
}
|
||||
|
||||
type Messages struct {
|
||||
Server string
|
||||
Network string
|
||||
To string
|
||||
Messages []storage.Message
|
||||
Prepend bool
|
||||
|
@ -113,39 +107,41 @@ type Messages struct {
|
|||
}
|
||||
|
||||
type Topic struct {
|
||||
Server string
|
||||
Network string
|
||||
Channel string
|
||||
Topic string
|
||||
Nick string
|
||||
}
|
||||
|
||||
type Userlist struct {
|
||||
Server string
|
||||
Network string
|
||||
Channel string
|
||||
Users []string
|
||||
}
|
||||
|
||||
type MOTD struct {
|
||||
Server string
|
||||
Network string
|
||||
Title string
|
||||
Content []string
|
||||
}
|
||||
|
||||
type Invite struct {
|
||||
Server string
|
||||
Network string
|
||||
Channel string
|
||||
User string
|
||||
}
|
||||
|
||||
type Kick struct {
|
||||
Server string
|
||||
Network string
|
||||
Channel string
|
||||
Sender string
|
||||
User string
|
||||
Reason string
|
||||
}
|
||||
|
||||
type Whois struct {
|
||||
Server string
|
||||
User string
|
||||
Network string
|
||||
User string
|
||||
}
|
||||
|
||||
type WhoisReply struct {
|
||||
|
@ -158,23 +154,23 @@ type WhoisReply struct {
|
|||
}
|
||||
|
||||
type Away struct {
|
||||
Server string
|
||||
Network string
|
||||
Message string
|
||||
}
|
||||
|
||||
type Raw struct {
|
||||
Server string
|
||||
Network string
|
||||
Message string
|
||||
}
|
||||
|
||||
type SearchRequest struct {
|
||||
Server string
|
||||
Network string
|
||||
Channel string
|
||||
Phrase string
|
||||
}
|
||||
|
||||
type SearchResult struct {
|
||||
Server string
|
||||
Network string
|
||||
Channel string
|
||||
Results []storage.Message
|
||||
}
|
||||
|
@ -185,26 +181,26 @@ type ClientCert struct {
|
|||
}
|
||||
|
||||
type FetchMessages struct {
|
||||
Server string
|
||||
Network string
|
||||
Channel string
|
||||
Next string
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Server string
|
||||
Network string
|
||||
Message string
|
||||
}
|
||||
|
||||
type IRCError struct {
|
||||
Server string
|
||||
Network string
|
||||
Target string
|
||||
Message string
|
||||
}
|
||||
|
||||
type ChannelSearch struct {
|
||||
Server string
|
||||
Q string
|
||||
Start int
|
||||
Network string
|
||||
Q string
|
||||
Start int
|
||||
}
|
||||
|
||||
type ChannelSearchResult struct {
|
||||
|
@ -213,13 +209,13 @@ type ChannelSearchResult struct {
|
|||
}
|
||||
|
||||
type ChannelForward struct {
|
||||
Server string
|
||||
Old string
|
||||
New string
|
||||
Network string
|
||||
Old string
|
||||
New string
|
||||
}
|
||||
|
||||
type DCCSend struct {
|
||||
Server string
|
||||
Network string
|
||||
From string
|
||||
Filename string
|
||||
URL string
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -87,22 +87,23 @@ func (d *Dispatch) loadUser(user *storage.User) {
|
|||
d.states.set(state)
|
||||
go state.run()
|
||||
|
||||
channels, err := user.GetChannels()
|
||||
networks, err := user.Networks()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
servers, err := user.GetServers()
|
||||
channels, err := user.Channels()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, server := range servers {
|
||||
i := connectIRC(server, state, user.GetLastIP())
|
||||
for _, network := range networks {
|
||||
i := connectIRC(network, state, user.GetLastIP())
|
||||
|
||||
var joining []string
|
||||
for _, channel := range channels {
|
||||
if channel.Server == server.Host {
|
||||
if channel.Network == network.Host {
|
||||
network.AddChannel(network.NewChannel(channel.Name))
|
||||
joining = append(joining, channel.Name)
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +116,7 @@ func (d *Dispatch) startHTTP() {
|
|||
|
||||
port := cfg.Port
|
||||
if cfg.Dev {
|
||||
// The node dev server will proxy index page requests and
|
||||
// The node dev network will proxy index page requests and
|
||||
// websocket connections to this port
|
||||
port = "1337"
|
||||
}
|
||||
|
@ -190,7 +191,7 @@ func (d *Dispatch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
filename := params[2]
|
||||
w.Header().Set("Content-Disposition", "attachment; filename="+filename)
|
||||
|
||||
if pack, ok := state.getPendingDCC(filename); ok {
|
||||
if pack, ok := state.pendingDCC(filename); ok {
|
||||
state.deletePendingDCC(filename)
|
||||
|
||||
w.Header().Set("Content-Length", strconv.FormatUint(pack.Length, 10))
|
||||
|
|
122
server/state.go
122
server/state.go
|
@ -20,26 +20,23 @@ const (
|
|||
type State struct {
|
||||
stateData
|
||||
|
||||
irc map[string]*irc.Client
|
||||
connectionState map[string]irc.ConnectionState
|
||||
networks map[string]*storage.Network
|
||||
pendingDCCSends map[string]*irc.DCCSend
|
||||
ircLock sync.Mutex
|
||||
|
||||
ws map[string]*wsConn
|
||||
wsLock sync.Mutex
|
||||
broadcast chan WSResponse
|
||||
|
||||
srv *Dispatch
|
||||
user *storage.User
|
||||
expiration *time.Timer
|
||||
reset chan time.Duration
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func NewState(user *storage.User, srv *Dispatch) *State {
|
||||
return &State{
|
||||
stateData: stateData{m: map[string]interface{}{}},
|
||||
irc: make(map[string]*irc.Client),
|
||||
connectionState: make(map[string]irc.ConnectionState),
|
||||
networks: make(map[string]*storage.Network),
|
||||
pendingDCCSends: make(map[string]*irc.DCCSend),
|
||||
ws: make(map[string]*wsConn),
|
||||
broadcast: make(chan WSResponse, 32),
|
||||
|
@ -50,99 +47,84 @@ func NewState(user *storage.User, srv *Dispatch) *State {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *State) getIRC(server string) (*irc.Client, bool) {
|
||||
s.ircLock.Lock()
|
||||
i, ok := s.irc[server]
|
||||
s.ircLock.Unlock()
|
||||
func (s *State) network(host string) (*storage.Network, bool) {
|
||||
s.lock.Lock()
|
||||
n, ok := s.networks[host]
|
||||
s.lock.Unlock()
|
||||
|
||||
return i, ok
|
||||
return n, ok
|
||||
}
|
||||
|
||||
func (s *State) setIRC(server string, i *irc.Client) {
|
||||
s.ircLock.Lock()
|
||||
s.irc[server] = i
|
||||
s.connectionState[server] = irc.ConnectionState{
|
||||
Connected: false,
|
||||
func (s *State) client(host string) (*irc.Client, bool) {
|
||||
if network, ok := s.network(host); ok {
|
||||
return network.Client(), true
|
||||
}
|
||||
s.ircLock.Unlock()
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s *State) setNetwork(host string, network *storage.Network) {
|
||||
s.lock.Lock()
|
||||
s.networks[host] = network
|
||||
s.lock.Unlock()
|
||||
|
||||
s.reset <- 0
|
||||
}
|
||||
|
||||
func (s *State) deleteIRC(server string) {
|
||||
s.ircLock.Lock()
|
||||
delete(s.irc, server)
|
||||
delete(s.connectionState, server)
|
||||
s.ircLock.Unlock()
|
||||
func (s *State) deleteNetwork(host string) {
|
||||
s.lock.Lock()
|
||||
delete(s.networks, host)
|
||||
s.lock.Unlock()
|
||||
|
||||
s.resetExpirationIfEmpty()
|
||||
}
|
||||
|
||||
func (s *State) numIRC() int {
|
||||
s.ircLock.Lock()
|
||||
n := len(s.irc)
|
||||
s.ircLock.Unlock()
|
||||
s.lock.Lock()
|
||||
n := len(s.networks)
|
||||
s.lock.Unlock()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (s *State) getConnectionStates() map[string]irc.ConnectionState {
|
||||
s.ircLock.Lock()
|
||||
state := make(map[string]irc.ConnectionState, len(s.connectionState))
|
||||
|
||||
for k, v := range s.connectionState {
|
||||
state[k] = v
|
||||
}
|
||||
s.ircLock.Unlock()
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
func (s *State) setConnectionState(server string, state irc.ConnectionState) {
|
||||
s.ircLock.Lock()
|
||||
s.connectionState[server] = state
|
||||
s.ircLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) getPendingDCC(filename string) (*irc.DCCSend, bool) {
|
||||
s.ircLock.Lock()
|
||||
func (s *State) pendingDCC(filename string) (*irc.DCCSend, bool) {
|
||||
s.lock.Lock()
|
||||
pack, ok := s.pendingDCCSends[filename]
|
||||
s.ircLock.Unlock()
|
||||
s.lock.Unlock()
|
||||
return pack, ok
|
||||
}
|
||||
|
||||
func (s *State) setPendingDCC(filename string, pack *irc.DCCSend) {
|
||||
s.ircLock.Lock()
|
||||
s.lock.Lock()
|
||||
s.pendingDCCSends[filename] = pack
|
||||
s.ircLock.Unlock()
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) deletePendingDCC(filename string) {
|
||||
s.ircLock.Lock()
|
||||
s.lock.Lock()
|
||||
delete(s.pendingDCCSends, filename)
|
||||
s.ircLock.Unlock()
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) setWS(addr string, w *wsConn) {
|
||||
s.wsLock.Lock()
|
||||
s.lock.Lock()
|
||||
s.ws[addr] = w
|
||||
s.wsLock.Unlock()
|
||||
s.lock.Unlock()
|
||||
|
||||
s.reset <- 0
|
||||
}
|
||||
|
||||
func (s *State) deleteWS(addr string) {
|
||||
s.wsLock.Lock()
|
||||
s.lock.Lock()
|
||||
delete(s.ws, addr)
|
||||
s.wsLock.Unlock()
|
||||
s.lock.Unlock()
|
||||
|
||||
s.resetExpirationIfEmpty()
|
||||
}
|
||||
|
||||
func (s *State) numWS() int {
|
||||
s.ircLock.Lock()
|
||||
s.lock.Lock()
|
||||
n := len(s.ws)
|
||||
s.ircLock.Unlock()
|
||||
s.lock.Unlock()
|
||||
|
||||
return n
|
||||
}
|
||||
|
@ -151,11 +133,11 @@ func (s *State) sendJSON(t string, v interface{}) {
|
|||
s.broadcast <- WSResponse{t, v}
|
||||
}
|
||||
|
||||
func (s *State) sendLastMessages(server, channel string, count int) {
|
||||
messages, hasMore, err := s.user.GetLastMessages(server, channel, count)
|
||||
func (s *State) sendLastMessages(network, channel string, count int) {
|
||||
messages, hasMore, err := s.user.LastMessages(network, channel, count)
|
||||
if err == nil && len(messages) > 0 {
|
||||
res := Messages{
|
||||
Server: server,
|
||||
Network: network,
|
||||
To: channel,
|
||||
Messages: messages,
|
||||
}
|
||||
|
@ -168,11 +150,11 @@ func (s *State) sendLastMessages(server, channel string, count int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *State) sendMessages(server, channel string, count int, fromID string) {
|
||||
messages, hasMore, err := s.user.GetMessages(server, channel, count, fromID)
|
||||
func (s *State) sendMessages(network, channel string, count int, fromID string) {
|
||||
messages, hasMore, err := s.user.Messages(network, channel, count, fromID)
|
||||
if err == nil && len(messages) > 0 {
|
||||
res := Messages{
|
||||
Server: server,
|
||||
Network: network,
|
||||
To: channel,
|
||||
Messages: messages,
|
||||
Prepend: true,
|
||||
|
@ -193,27 +175,25 @@ func (s *State) resetExpirationIfEmpty() {
|
|||
}
|
||||
|
||||
func (s *State) kill() {
|
||||
s.wsLock.Lock()
|
||||
s.lock.Lock()
|
||||
for _, ws := range s.ws {
|
||||
ws.conn.Close()
|
||||
}
|
||||
s.wsLock.Unlock()
|
||||
s.ircLock.Lock()
|
||||
for _, i := range s.irc {
|
||||
i.Quit()
|
||||
for _, network := range s.networks {
|
||||
network.Client().Quit()
|
||||
}
|
||||
s.ircLock.Unlock()
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) run() {
|
||||
for {
|
||||
select {
|
||||
case res := <-s.broadcast:
|
||||
s.wsLock.Lock()
|
||||
s.lock.Lock()
|
||||
for _, ws := range s.ws {
|
||||
ws.out <- res
|
||||
}
|
||||
s.wsLock.Unlock()
|
||||
s.lock.Unlock()
|
||||
|
||||
case <-s.expiration.C:
|
||||
s.srv.states.delete(s.user.ID)
|
||||
|
@ -283,7 +263,7 @@ func newStateStore(sessionStore storage.SessionStore) *stateStore {
|
|||
sessionStore: sessionStore,
|
||||
}
|
||||
|
||||
sessions, err := sessionStore.GetSessions()
|
||||
sessions, err := sessionStore.Sessions()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -24,13 +24,6 @@ func newWSHandler(conn *websocket.Conn, state *State, r *http.Request) *wsHandle
|
|||
addr: conn.RemoteAddr(),
|
||||
}
|
||||
|
||||
if r.Header.Get("X-Forwarded-For") != "" {
|
||||
ip := net.ParseIP(r.Header.Get("X-Forwarded-For"))
|
||||
if ip != nil {
|
||||
h.addr.(*net.TCPAddr).IP = ip
|
||||
}
|
||||
}
|
||||
|
||||
h.init(r)
|
||||
h.initHandlers()
|
||||
return h
|
||||
|
@ -61,6 +54,13 @@ func (h *wsHandler) dispatchRequest(req WSRequest) {
|
|||
}
|
||||
|
||||
func (h *wsHandler) init(r *http.Request) {
|
||||
if r.Header.Get("X-Forwarded-For") != "" {
|
||||
ip := net.ParseIP(r.Header.Get("X-Forwarded-For"))
|
||||
if ip != nil {
|
||||
h.addr.(*net.TCPAddr).IP = ip
|
||||
}
|
||||
}
|
||||
|
||||
h.state.setWS(h.addr.String(), h.ws)
|
||||
h.state.user.SetLastIP(addrToIPBytes(h.addr))
|
||||
if r.TLS != nil {
|
||||
|
@ -74,58 +74,65 @@ func (h *wsHandler) init(r *http.Request) {
|
|||
h.state.numIRC(), "IRC connections |",
|
||||
h.state.numWS(), "WebSocket connections")
|
||||
|
||||
tab, err := tabFromRequest(r)
|
||||
go h.sendData(r)
|
||||
}
|
||||
|
||||
channels, err := h.state.user.GetChannels()
|
||||
func (h *wsHandler) sendData(r *http.Request) {
|
||||
tab, err := tabFromRequest(r)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
for _, channel := range channels {
|
||||
if channel.Server == tab.Server && channel.Name == tab.Name {
|
||||
// Userlist and messages for this channel gets embedded in the index page
|
||||
continue
|
||||
h.state.lock.Lock()
|
||||
for _, network := range h.state.networks {
|
||||
for _, channel := range network.ChannelNames() {
|
||||
if network.Host == tab.Network && channel == tab.Name {
|
||||
// Userlist and messages for this channel gets embedded in the index page
|
||||
continue
|
||||
}
|
||||
|
||||
if users := network.Client().ChannelUsers(channel); len(users) > 0 {
|
||||
h.state.sendJSON("users", Userlist{
|
||||
Network: network.Host,
|
||||
Channel: channel,
|
||||
Users: users,
|
||||
})
|
||||
}
|
||||
|
||||
h.state.sendLastMessages(network.Host, channel, 50)
|
||||
}
|
||||
|
||||
if i, ok := h.state.getIRC(channel.Server); ok {
|
||||
h.state.sendJSON("users", Userlist{
|
||||
Server: channel.Server,
|
||||
Channel: channel.Name,
|
||||
Users: i.ChannelUsers(channel.Name),
|
||||
})
|
||||
}
|
||||
|
||||
h.state.sendLastMessages(channel.Server, channel.Name, 50)
|
||||
}
|
||||
h.state.lock.Unlock()
|
||||
|
||||
openDMs, err := h.state.user.GetOpenDMs()
|
||||
openDMs, err := h.state.user.OpenDMs()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
for _, openDM := range openDMs {
|
||||
if openDM.Server == tab.Server && openDM.Name == tab.Name {
|
||||
if openDM.Network == tab.Network && openDM.Name == tab.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
h.state.sendLastMessages(openDM.Server, openDM.Name, 50)
|
||||
h.state.sendLastMessages(openDM.Network, openDM.Name, 50)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *wsHandler) connect(b []byte) {
|
||||
var data Server
|
||||
data.UnmarshalJSON(b)
|
||||
var network storage.Network
|
||||
network.UnmarshalJSON(b)
|
||||
|
||||
data.Host = strings.ToLower(data.Host)
|
||||
network.Host = strings.ToLower(network.Host)
|
||||
|
||||
if _, ok := h.state.getIRC(data.Host); !ok {
|
||||
log.Println(h.addr, "[IRC] Add server", data.Host)
|
||||
if _, ok := h.state.network(network.Host); !ok {
|
||||
log.Println(h.addr, "[IRC] Add server", network.Host)
|
||||
|
||||
connectIRC(data.Server, h.state, addrToIPBytes(h.addr))
|
||||
connectIRC(&network, h.state, addrToIPBytes(h.addr))
|
||||
|
||||
go h.state.user.AddServer(data.Server)
|
||||
go network.Save()
|
||||
} else {
|
||||
log.Println(h.addr, "[IRC]", data.Host, "already added")
|
||||
log.Println(h.addr, "[IRC]", network.Host, "already added")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +140,7 @@ func (h *wsHandler) reconnect(b []byte) {
|
|||
var data ReconnectSettings
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok && !i.Connected() {
|
||||
if i, ok := h.state.client(data.Network); ok && !i.Connected() {
|
||||
if i.Config.TLS {
|
||||
i.Config.TLSConfig.InsecureSkipVerify = data.SkipVerify
|
||||
}
|
||||
|
@ -145,7 +152,7 @@ func (h *wsHandler) join(b []byte) {
|
|||
var data Join
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Join(data.Channels...)
|
||||
}
|
||||
}
|
||||
|
@ -154,33 +161,43 @@ func (h *wsHandler) part(b []byte) {
|
|||
var data Part
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Part(data.Channels...)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if network, ok := h.state.network(data.Network); ok {
|
||||
network.RemoveChannels(data.Channels...)
|
||||
}
|
||||
|
||||
for _, channel := range data.Channels {
|
||||
h.state.user.RemoveChannel(data.Network, channel)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *wsHandler) quit(b []byte) {
|
||||
var data Quit
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
log.Println(h.addr, "[IRC] Remove server", data.Server)
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
h.state.deleteIRC(data.Server)
|
||||
log.Println(h.addr, "[IRC] Remove server", data.Network)
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
h.state.deleteNetwork(data.Network)
|
||||
i.Quit()
|
||||
}
|
||||
|
||||
go h.state.user.RemoveServer(data.Server)
|
||||
go h.state.user.RemoveNetwork(data.Network)
|
||||
}
|
||||
|
||||
func (h *wsHandler) message(b []byte) {
|
||||
var data Message
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Privmsg(data.To, data.Content)
|
||||
|
||||
go h.state.user.LogMessage(&storage.Message{
|
||||
Server: data.Server,
|
||||
Network: data.Network,
|
||||
From: i.GetNick(),
|
||||
To: data.To,
|
||||
Content: data.Content,
|
||||
|
@ -192,7 +209,7 @@ func (h *wsHandler) nick(b []byte) {
|
|||
var data Nick
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Nick(data.New)
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +218,7 @@ func (h *wsHandler) topic(b []byte) {
|
|||
var data Topic
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Topic(data.Channel, data.Topic)
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +227,7 @@ func (h *wsHandler) invite(b []byte) {
|
|||
var data Invite
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Invite(data.User, data.Channel)
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +236,7 @@ func (h *wsHandler) kick(b []byte) {
|
|||
var data Invite
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Kick(data.Channel, data.User)
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +245,7 @@ func (h *wsHandler) whois(b []byte) {
|
|||
var data Whois
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Whois(data.User)
|
||||
}
|
||||
}
|
||||
|
@ -237,7 +254,7 @@ func (h *wsHandler) away(b []byte) {
|
|||
var data Away
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Away(data.Message)
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +263,7 @@ func (h *wsHandler) raw(b []byte) {
|
|||
var data Raw
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok {
|
||||
if i, ok := h.state.client(data.Network); ok {
|
||||
i.Write(data.Message)
|
||||
}
|
||||
}
|
||||
|
@ -256,14 +273,14 @@ func (h *wsHandler) search(b []byte) {
|
|||
var data SearchRequest
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
results, err := h.state.user.SearchMessages(data.Server, data.Channel, data.Phrase)
|
||||
results, err := h.state.user.SearchMessages(data.Network, data.Channel, data.Phrase)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
h.state.sendJSON("search", SearchResult{
|
||||
Server: data.Server,
|
||||
Network: data.Network,
|
||||
Channel: data.Channel,
|
||||
Results: results,
|
||||
})
|
||||
|
@ -287,15 +304,18 @@ func (h *wsHandler) fetchMessages(b []byte) {
|
|||
var data FetchMessages
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
h.state.sendMessages(data.Server, data.Channel, 200, data.Next)
|
||||
h.state.sendMessages(data.Network, data.Channel, 200, data.Next)
|
||||
}
|
||||
|
||||
func (h *wsHandler) setServerName(b []byte) {
|
||||
var data ServerName
|
||||
func (h *wsHandler) setNetworkName(b []byte) {
|
||||
var data NetworkName
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
if isValidServerName(data.Name) {
|
||||
h.state.user.SetServerName(data.Name, data.Server)
|
||||
if isValidNetworkName(data.Name) {
|
||||
if network, ok := h.state.network(data.Network); ok {
|
||||
network.SetName(data.Name)
|
||||
go network.Save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,7 +330,7 @@ func (h *wsHandler) channelSearch(b []byte) {
|
|||
var data ChannelSearch
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
index, needsUpdate := channelIndexes.Get(data.Server)
|
||||
index, needsUpdate := channelIndexes.Get(data.Network)
|
||||
if index != nil {
|
||||
n := 10
|
||||
if data.Start > 0 {
|
||||
|
@ -323,8 +343,8 @@ func (h *wsHandler) channelSearch(b []byte) {
|
|||
})
|
||||
}
|
||||
|
||||
if i, ok := h.state.getIRC(data.Server); ok && needsUpdate {
|
||||
h.state.Set("update_chanlist_"+data.Server, true)
|
||||
if i, ok := h.state.client(data.Network); ok && needsUpdate {
|
||||
h.state.Set("update_chanlist_"+data.Network, true)
|
||||
i.List()
|
||||
}
|
||||
}
|
||||
|
@ -333,43 +353,43 @@ func (h *wsHandler) openDM(b []byte) {
|
|||
var data Tab
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
h.state.sendLastMessages(data.Server, data.Name, 50)
|
||||
h.state.user.AddOpenDM(data.Server, data.Name)
|
||||
h.state.sendLastMessages(data.Network, data.Name, 50)
|
||||
h.state.user.AddOpenDM(data.Network, data.Name)
|
||||
}
|
||||
|
||||
func (h *wsHandler) closeDM(b []byte) {
|
||||
var data Tab
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
h.state.user.RemoveOpenDM(data.Server, data.Name)
|
||||
h.state.user.RemoveOpenDM(data.Network, data.Name)
|
||||
}
|
||||
|
||||
func (h *wsHandler) initHandlers() {
|
||||
h.handlers = map[string]func([]byte){
|
||||
"connect": h.connect,
|
||||
"reconnect": h.reconnect,
|
||||
"join": h.join,
|
||||
"part": h.part,
|
||||
"quit": h.quit,
|
||||
"message": h.message,
|
||||
"nick": h.nick,
|
||||
"topic": h.topic,
|
||||
"invite": h.invite,
|
||||
"kick": h.kick,
|
||||
"whois": h.whois,
|
||||
"away": h.away,
|
||||
"raw": h.raw,
|
||||
"search": h.search,
|
||||
"cert": h.cert,
|
||||
"fetch_messages": h.fetchMessages,
|
||||
"set_server_name": h.setServerName,
|
||||
"settings_set": h.setSettings,
|
||||
"channel_search": h.channelSearch,
|
||||
"open_dm": h.openDM,
|
||||
"close_dm": h.closeDM,
|
||||
"connect": h.connect,
|
||||
"reconnect": h.reconnect,
|
||||
"join": h.join,
|
||||
"part": h.part,
|
||||
"quit": h.quit,
|
||||
"message": h.message,
|
||||
"nick": h.nick,
|
||||
"topic": h.topic,
|
||||
"invite": h.invite,
|
||||
"kick": h.kick,
|
||||
"whois": h.whois,
|
||||
"away": h.away,
|
||||
"raw": h.raw,
|
||||
"search": h.search,
|
||||
"cert": h.cert,
|
||||
"fetch_messages": h.fetchMessages,
|
||||
"set_network_name": h.setNetworkName,
|
||||
"settings_set": h.setSettings,
|
||||
"channel_search": h.channelSearch,
|
||||
"open_dm": h.openDM,
|
||||
"close_dm": h.closeDM,
|
||||
}
|
||||
}
|
||||
|
||||
func isValidServerName(name string) bool {
|
||||
func isValidNetworkName(name string) bool {
|
||||
return strings.TrimSpace(name) != ""
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue