mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-05-29 22:59:20 +03:00
/kick and /msg
This commit is contained in:
parent
11e92b5718
commit
e626eab624
@ -142,12 +142,20 @@ func (ch *Channel) Leave(u *User) error {
|
|||||||
// Member returns a corresponding Member object to a User if the Member is
|
// Member returns a corresponding Member object to a User if the Member is
|
||||||
// present in this channel.
|
// present in this channel.
|
||||||
func (ch *Channel) Member(u *User) (*Member, bool) {
|
func (ch *Channel) Member(u *User) (*Member, bool) {
|
||||||
m, err := ch.members.Get(u.Id())
|
m, ok := ch.MemberById(u.Id())
|
||||||
if err != nil {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
// Check that it's the same user
|
// Check that it's the same user
|
||||||
if m.(*Member).User != u {
|
if m.User != u {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *Channel) MemberById(id Id) (*Member, bool) {
|
||||||
|
m, err := ch.members.Get(id)
|
||||||
|
if err != nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return m.(*Member), true
|
return m.(*Member), true
|
||||||
|
@ -86,7 +86,7 @@ func (c Commands) Help(showOp bool) string {
|
|||||||
}
|
}
|
||||||
help := "Available commands:" + Newline + NewCommandsHelp(normal).String()
|
help := "Available commands:" + Newline + NewCommandsHelp(normal).String()
|
||||||
if showOp {
|
if showOp {
|
||||||
help += Newline + "Operator commands:" + Newline + NewCommandsHelp(op).String()
|
help += Newline + "-> Operator commands:" + Newline + NewCommandsHelp(op).String()
|
||||||
}
|
}
|
||||||
return help
|
return help
|
||||||
}
|
}
|
||||||
@ -231,12 +231,12 @@ func InitCommands(c *Commands) {
|
|||||||
|
|
||||||
// TODO: Add support for fingerprint-based op'ing. This will
|
// TODO: Add support for fingerprint-based op'ing. This will
|
||||||
// probably need to live in host land.
|
// probably need to live in host land.
|
||||||
member, err := channel.members.Get(Id(args[0]))
|
member, ok := channel.MemberById(Id(args[0]))
|
||||||
if err != nil {
|
if !ok {
|
||||||
return errors.New("user not found")
|
return errors.New("user not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
member.(*Member).Op = true
|
member.Op = true
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -20,6 +20,7 @@ type User struct {
|
|||||||
joined time.Time
|
joined time.Time
|
||||||
msg chan Message
|
msg chan Message
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
replyTo *User // Set when user gets a /msg, for replying.
|
||||||
closed bool
|
closed bool
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
}
|
}
|
||||||
|
39
command.go
Normal file
39
command.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/shazow/ssh-chat/chat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitCommands adds host-specific commands to a Commands container.
|
||||||
|
func InitCommands(h *Host, c *chat.Commands) {
|
||||||
|
c.Add(chat.Command{
|
||||||
|
Op: true,
|
||||||
|
Prefix: "/msg",
|
||||||
|
PrefixHelp: "USER MESSAGE",
|
||||||
|
Help: "Send MESSAGE to USER.",
|
||||||
|
Handler: func(channel *chat.Channel, msg chat.CommandMsg) error {
|
||||||
|
if !channel.IsOp(msg.From()) {
|
||||||
|
return errors.New("must be op")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := msg.Args()
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
return errors.New("must specify user")
|
||||||
|
case 1:
|
||||||
|
return errors.New("must specify message")
|
||||||
|
}
|
||||||
|
|
||||||
|
member, ok := channel.MemberById(chat.Id(args[0]))
|
||||||
|
if !ok {
|
||||||
|
return errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
m := chat.NewPrivateMsg("hello", msg.From(), member.User)
|
||||||
|
channel.Send(m)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
72
host.go
72
host.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
@ -9,6 +10,15 @@ import (
|
|||||||
"github.com/shazow/ssh-chat/sshd"
|
"github.com/shazow/ssh-chat/sshd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetPrompt will render the terminal prompt string based on the user.
|
||||||
|
func GetPrompt(user *chat.User) string {
|
||||||
|
name := user.Name()
|
||||||
|
if user.Config.Theme != nil {
|
||||||
|
name = user.Config.Theme.ColorName(user)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[%s] ", name)
|
||||||
|
}
|
||||||
|
|
||||||
// Host is the bridge between sshd and chat modules
|
// Host is the bridge between sshd and chat modules
|
||||||
// TODO: Should be easy to add support for multiple channels, if we want.
|
// TODO: Should be easy to add support for multiple channels, if we want.
|
||||||
type Host struct {
|
type Host struct {
|
||||||
@ -35,6 +45,7 @@ func NewHost(listener *sshd.SSHListener) *Host {
|
|||||||
// Make our own commands registry instance.
|
// Make our own commands registry instance.
|
||||||
commands := chat.Commands{}
|
commands := chat.Commands{}
|
||||||
chat.InitCommands(&commands)
|
chat.InitCommands(&commands)
|
||||||
|
h.InitCommands(&commands)
|
||||||
ch.SetCommands(commands)
|
ch.SetCommands(commands)
|
||||||
|
|
||||||
go ch.Serve()
|
go ch.Serve()
|
||||||
@ -159,11 +170,58 @@ func (h *Host) AutoCompleteFunction(line string, pos int, key rune) (newLine str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPrompt will render the terminal prompt string based on the user.
|
// InitCommands adds host-specific commands to a Commands container. These will
|
||||||
func GetPrompt(user *chat.User) string {
|
// override any existing commands.
|
||||||
name := user.Name()
|
func (h *Host) InitCommands(c *chat.Commands) {
|
||||||
if user.Config.Theme != nil {
|
c.Add(chat.Command{
|
||||||
name = user.Config.Theme.ColorName(user)
|
Prefix: "/msg",
|
||||||
}
|
PrefixHelp: "USER MESSAGE",
|
||||||
return fmt.Sprintf("[%s] ", name)
|
Help: "Send MESSAGE to USER.",
|
||||||
|
Handler: func(channel *chat.Channel, msg chat.CommandMsg) error {
|
||||||
|
args := msg.Args()
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
return errors.New("must specify user")
|
||||||
|
case 1:
|
||||||
|
return errors.New("must specify message")
|
||||||
|
}
|
||||||
|
|
||||||
|
member, ok := channel.MemberById(chat.Id(args[0]))
|
||||||
|
if !ok {
|
||||||
|
return errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
m := chat.NewPrivateMsg(strings.Join(args[2:], " "), msg.From(), member.User)
|
||||||
|
channel.Send(m)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Op commands
|
||||||
|
c.Add(chat.Command{
|
||||||
|
Op: true,
|
||||||
|
Prefix: "/kick",
|
||||||
|
PrefixHelp: "USER",
|
||||||
|
Help: "Kick USER from the server.",
|
||||||
|
Handler: func(channel *chat.Channel, msg chat.CommandMsg) error {
|
||||||
|
if !channel.IsOp(msg.From()) {
|
||||||
|
return errors.New("must be op")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := msg.Args()
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.New("must specify user")
|
||||||
|
}
|
||||||
|
|
||||||
|
member, ok := channel.MemberById(chat.Id(args[0]))
|
||||||
|
if !ok {
|
||||||
|
return errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
body := fmt.Sprintf("%s was kicked by %s.", member.Name(), msg.From().Name())
|
||||||
|
channel.Send(chat.NewAnnounceMsg(body))
|
||||||
|
member.User.Close()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
package sshd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Keep track of multiple errors and coerce them into one error
|
|
||||||
type MultiError []error
|
|
||||||
|
|
||||||
func (e MultiError) Error() string {
|
|
||||||
switch len(e) {
|
|
||||||
case 0:
|
|
||||||
return ""
|
|
||||||
case 1:
|
|
||||||
return e[0].Error()
|
|
||||||
default:
|
|
||||||
errs := []string{}
|
|
||||||
for _, err := range e {
|
|
||||||
errs = append(errs, err.Error())
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%d errors: %s", strings.Join(errs, "; "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep track of multiple closers and close them all as one closer
|
|
||||||
type MultiCloser []io.Closer
|
|
||||||
|
|
||||||
func (c MultiCloser) Close() error {
|
|
||||||
errors := MultiError{}
|
|
||||||
for _, closer := range c {
|
|
||||||
err := closer.Close()
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(errors) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors
|
|
||||||
}
|
|
@ -1,8 +1,9 @@
|
|||||||
// Borrowed from go.crypto circa 2011
|
|
||||||
package sshd
|
package sshd
|
||||||
|
|
||||||
import "encoding/binary"
|
import "encoding/binary"
|
||||||
|
|
||||||
|
// Helpers below are borrowed from go.crypto circa 2011:
|
||||||
|
|
||||||
// parsePtyRequest parses the payload of the pty-req message and extracts the
|
// parsePtyRequest parses the payload of the pty-req message and extracts the
|
||||||
// dimensions of the terminal. See RFC 4254, section 6.2.
|
// dimensions of the terminal. See RFC 4254, section 6.2.
|
||||||
func parsePtyRequest(s []byte) (width, height int, ok bool) {
|
func parsePtyRequest(s []byte) (width, height int, ok bool) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user